mfe-runtime-z 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Delpi.Kye
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,332 @@
1
+ <div>
2
+ <h1>mfe-runtime-z</h1>
3
+ <p>Framework-agnostic micro-frontend runtime</p>
4
+ <a href="https://codesandbox.io/p/sandbox/c57cwd" target="_blank">LIVE EXAMPLE</a>
5
+ </div>
6
+
7
+ ---
8
+
9
+
10
+ [![NPM](https://img.shields.io/npm/v/mfe-runtime-z.svg)](https://www.npmjs.com/package/mfe-runtime-z)
11
+ [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
12
+ ![Downloads](https://img.shields.io/npm/dt/mfe-runtime-z.svg)
13
+
14
+ ---
15
+
16
+ ### Description
17
+
18
+ **mfe-runtime-z** is a lightweight, framework-agnostic **micro-frontend runtime**.
19
+
20
+ It provides the minimal building blocks needed to run multiple independent frontend applications (micro-frontends) together **at runtime**, without coupling them through framework internals or build-time federation.
21
+
22
+ This library focuses on **application-level integration**, not component sharing.
23
+
24
+ #### What it solves
25
+
26
+ - Load and mount remote applications dynamically
27
+ - Isolate application lifecycles (mount / unmount / reload)
28
+ - Share state safely between apps
29
+ - Synchronize routing without sharing router instances
30
+ - Communicate via a central event bus
31
+ - Work with **any framework** (React, Vue, Svelte, Vanilla JS)
32
+
33
+ ---
34
+
35
+ ### Installation
36
+
37
+ ```bash
38
+ npm install mfe-runtime-z
39
+ # or
40
+ yarn add mfe-runtime-z
41
+ ```
42
+
43
+ ---
44
+
45
+ ### Core Concepts
46
+
47
+ ##### Host-driven architecture
48
+
49
+ - Each micro-frontend is an **independent app**
50
+ - The host coordinates loading, routing, and shared services
51
+ - No direct imports between micro-frontends
52
+
53
+ ##### Runtime contract
54
+
55
+ Micro-frontends expose a simple runtime contract:
56
+
57
+ ```ts
58
+ window.myRemoteApp = {
59
+ mount(el, ctx),
60
+ unmount(el)
61
+ }
62
+ ```
63
+
64
+ No framework or bundler assumptions.
65
+
66
+ ---
67
+
68
+ ### Usage
69
+
70
+ ##### Host application
71
+
72
+ ```ts
73
+ import { MFEHost, createSharedStore } from "mfe-runtime-z"
74
+
75
+ const authStore = createSharedStore({
76
+ user: null
77
+ })
78
+
79
+ const host = new MFEHost({
80
+ stores: { auth: authStore },
81
+ navigate: (path) => history.pushState({}, "", path)
82
+ })
83
+
84
+ const remote = await host.load(
85
+ "http://localhost:3001/remote.js",
86
+ "productApp"
87
+ )
88
+
89
+ host.mount(remote, document.getElementById("app"), "productApp")
90
+
91
+ // host.unmount(remote, document.getElementById("app"), "productApp")
92
+ ```
93
+
94
+ ---
95
+
96
+ ##### Remote application (framework-agnostic)
97
+
98
+ ```ts
99
+ export function mount(el, ctx) {
100
+ const authStore = ctx.stores.auth
101
+
102
+ authStore.subscribe((state) => {
103
+ el.innerHTML = state.user
104
+ ? `Hello ${state.user.name}`
105
+ : `<button id="login">Login</button>`
106
+
107
+ el.querySelector("#login")?.addEventListener("click", () => {
108
+ authStore.setState({
109
+ user: { id: "1", name: "Alice" }
110
+ })
111
+ })
112
+ })
113
+ }
114
+
115
+ export function unmount(el) {
116
+ el.innerHTML = ""
117
+ }
118
+
119
+ window.productApp = { mount, unmount }
120
+ ```
121
+
122
+ ##### Remote app (framework-react-library)
123
+
124
+ ```ts
125
+ // src/app.tsx
126
+ import React from "react"
127
+ import ReactDOM from "react-dom/client"
128
+
129
+ type AuthStore = {
130
+ user: { id: string; name: string } | null
131
+ subscribe: (callback: (state: any) => void) => void
132
+ setState: (newState: any) => void
133
+ }
134
+
135
+ // React component
136
+ function App({ store }: { store: AuthStore }) {
137
+ const [user, setUser] = React.useState(store.user)
138
+
139
+ React.useEffect(() => {
140
+ const unsubscribe = store.subscribe((state: any) => {
141
+ setUser(state.user)
142
+ })
143
+ return () => unsubscribe?.()
144
+ }, [store])
145
+
146
+ const handleLogin = () => {
147
+ store.setState({ user: { id: "1", name: "Alice" } })
148
+ }
149
+
150
+ return (
151
+ <div>
152
+ {user ? (
153
+ <span>Hello {user.name}</span>
154
+ ) : (
155
+ <button id="login" onClick={handleLogin}>
156
+ Login
157
+ </button>
158
+ )}
159
+ </div>
160
+ )
161
+ }
162
+
163
+ // index.js
164
+ // mount/unmount functions
165
+ export function mount(el: HTMLElement, ctx: { stores: { auth: AuthStore } }) {
166
+ const root = ReactDOM.createRoot(el)
167
+ ;(el as any)._reactRoot = root
168
+ root.render(<App store={ctx.stores.auth} />)
169
+ }
170
+
171
+ export function unmount(el: HTMLElement) {
172
+ const root = (el as any)._reactRoot
173
+ if (root) {
174
+ root.unmount()
175
+ }
176
+ el.innerHTML = ""
177
+ }
178
+
179
+
180
+ // expose mount/unmount to window
181
+ ;(window as any).productApp = { mount, unmount }
182
+ ```
183
+
184
+ ---
185
+
186
+ ### Shared State
187
+
188
+ ```ts
189
+ const store = createSharedStore({ count: 0 })
190
+
191
+ store.subscribe((state) => {
192
+ console.log(state.count)
193
+ })
194
+
195
+ store.setState({ count: 1 })
196
+ ```
197
+
198
+ - Push-based
199
+ - No Redux
200
+ - No shared framework state
201
+
202
+ ---
203
+
204
+ ### Router Synchronization
205
+
206
+ ```ts
207
+ import { createSharedRouter } from "mfe-runtime-z"
208
+
209
+ const router = createSharedRouter(ctx)
210
+
211
+ router.go("/cart")
212
+
213
+ router.onChange((path) => {
214
+ console.log("navigated to", path)
215
+ })
216
+ ```
217
+
218
+ - Intent-based navigation
219
+ - Host owns the URL
220
+ - No shared router instances
221
+
222
+ ---
223
+
224
+ ### How remote applications are built
225
+
226
+ Remote applications only need to build a single JavaScript file. No Module Federation required.
227
+ - What a remote must provide
228
+ - Build a browser-loadable file (e.g. remote.js)
229
+ - Expose itself on window
230
+ - Implement mount / unmount
231
+ - Minimal remote build (Vite)
232
+
233
+
234
+ ```ts
235
+ // vite.config.ts
236
+ export default {
237
+ build: {
238
+ lib: {
239
+ entry: "src/index.ts",
240
+ name: "productApp",
241
+ formats: ["umd"],
242
+ fileName: () => "remote.js",
243
+ },
244
+ },
245
+ }
246
+
247
+ // src/index.ts
248
+ import { mount, unmount } from "./app"
249
+
250
+ // see above
251
+ ;(window as any).productApp = { mount, unmount }
252
+ ```
253
+
254
+ 👉 That’s it. `This file can be served from any CDN or server and loaded at runtime.`
255
+
256
+ ---
257
+
258
+ #### Dev HMR section
259
+
260
+ ###### Host dev reload
261
+ ```js
262
+ import { MFEHost } from "mfe-runtime-z"
263
+ import { createMFEReloadServer } from "mfe-runtime-z/dev"
264
+
265
+ // Initialize the MFE host runtime
266
+ const host = new MFEHost()
267
+
268
+ // Create a dev reload server to automatically reload remote apps on changes
269
+ const startDevReload = createMFEReloadServer(host, {
270
+ productApp: {
271
+ url: "http://localhost:3001/remote.js", // URL of the remote JS file
272
+ global: "productApp", // Name of the global mount/unmount object exposed by remote
273
+ el: () => document.getElementById("product-root")!, // DOM container to mount the remote
274
+ },
275
+ })
276
+
277
+ // Only start the dev reload server in development mode
278
+ if (process.env.NODE_ENV === "development") {
279
+ startDevReload()
280
+ }
281
+
282
+ ```
283
+
284
+ ###### Remote dev notify.
285
+ ```ts
286
+ // Only run in development
287
+ if (import.meta.env.DEV) {
288
+ // Connect to the host's WebSocket dev reload server
289
+ const ws = new WebSocket("ws://localhost:3000/__mfe_reload")
290
+
291
+ // Accept HMR updates from Vite
292
+ import.meta.hot?.accept(() => {
293
+ // Notify the host that this remote app has changed and should be reloaded
294
+ ws.send(JSON.stringify({ type: "RELOAD", name: "productApp" }))
295
+ })
296
+ }
297
+ ```
298
+
299
+ ✅ Summary of Comments
300
+
301
+ - Host side: sets up a WebSocket-based dev reload server that listens for changes from remotes and automatically reloads them without refreshing the host page.
302
+
303
+ - Remote side: connects to host WebSocket and sends a message when HMR triggers (code changed), so the host can remount the updated remote.
304
+
305
+ - Purpose: smooth dev experience for micro-frontends, framework-agnostic, avoids full page reload.
306
+
307
+ ---
308
+
309
+ ### Features
310
+
311
+ - Framework-agnostic micro-frontend runtime
312
+ - Dynamic remote loading
313
+ - Safe lifecycle management
314
+ - Central event bus
315
+ - Shared state with subscriptions
316
+ - Router synchronization
317
+ - Hot reload support for remote apps
318
+ - No build-time federation required
319
+
320
+ ---
321
+
322
+ ### What this library is NOT
323
+
324
+ - ❌ Not a UI component library
325
+ - ❌ Not a replacement for React/Vue routers
326
+ - ❌ Not a build-time module federation tool
327
+
328
+ ---
329
+
330
+ ### License
331
+
332
+ MIT
package/build/dev.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from "./micro-fe/dev";
@@ -0,0 +1,8 @@
1
+ export { MFEHost } from "./micro-fe/host/MFEHost";
2
+ export { enableRouterSync } from "./micro-fe/host/routerSync";
3
+ export { loadScript } from "./micro-fe/loader/loadScript";
4
+ export { EventBus } from "./micro-fe/event-bus/EventBus";
5
+ export { createSharedStore } from "./micro-fe/store/createSharedStore";
6
+ export { createSharedRouter } from "./micro-fe/router/createSharedRouter";
7
+ export type { RemoteApp } from "./micro-fe/types/RemoteApp";
8
+ export type { MFEContext } from "./micro-fe/types/MFEContext";
@@ -0,0 +1,305 @@
1
+ /******************************************************************************
2
+ Copyright (c) Microsoft Corporation.
3
+
4
+ Permission to use, copy, modify, and/or distribute this software for any
5
+ purpose with or without fee is hereby granted.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
8
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
9
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
10
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
11
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
12
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
13
+ PERFORMANCE OF THIS SOFTWARE.
14
+ ***************************************************************************** */
15
+
16
+ var __assign = function() {
17
+ __assign = Object.assign || function __assign(t) {
18
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
19
+ s = arguments[i];
20
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
21
+ }
22
+ return t;
23
+ };
24
+ return __assign.apply(this, arguments);
25
+ };
26
+
27
+ function __awaiter(thisArg, _arguments, P, generator) {
28
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
29
+ return new (P || (P = Promise))(function (resolve, reject) {
30
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
31
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
32
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
33
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
34
+ });
35
+ }
36
+
37
+ function __generator(thisArg, body) {
38
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
39
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
40
+ function verb(n) { return function (v) { return step([n, v]); }; }
41
+ function step(op) {
42
+ if (f) throw new TypeError("Generator is already executing.");
43
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
44
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
45
+ if (y = 0, t) op = [op[0] & 2, t.value];
46
+ switch (op[0]) {
47
+ case 0: case 1: t = op; break;
48
+ case 4: _.label++; return { value: op[1], done: false };
49
+ case 5: _.label++; y = op[1]; op = [0]; continue;
50
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
51
+ default:
52
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
53
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
54
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
55
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
56
+ if (t[2]) _.ops.pop();
57
+ _.trys.pop(); continue;
58
+ }
59
+ op = body.call(thisArg, _);
60
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
61
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
62
+ }
63
+ }
64
+
65
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
66
+ var e = new Error(message);
67
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
68
+ };
69
+
70
+ // loader/loadScript.ts
71
+ var loaded = new Set();
72
+ function loadScript(url, options) {
73
+ if (options === void 0) { options = {}; }
74
+ return __awaiter(this, void 0, void 0, function () {
75
+ var _a, timeout, _b, retries, attempt, err_1;
76
+ return __generator(this, function (_c) {
77
+ switch (_c.label) {
78
+ case 0:
79
+ _a = options.timeout, timeout = _a === void 0 ? 15000 : _a, _b = options.retries, retries = _b === void 0 ? 1 : _b;
80
+ if (loaded.has(url))
81
+ return [2 /*return*/];
82
+ attempt = 0;
83
+ _c.label = 1;
84
+ case 1:
85
+ if (!(attempt <= retries)) return [3 /*break*/, 6];
86
+ _c.label = 2;
87
+ case 2:
88
+ _c.trys.push([2, 4, , 5]);
89
+ return [4 /*yield*/, loadOnce(url, timeout)];
90
+ case 3:
91
+ _c.sent();
92
+ loaded.add(url);
93
+ return [2 /*return*/];
94
+ case 4:
95
+ err_1 = _c.sent();
96
+ if (attempt === retries) {
97
+ throw err_1;
98
+ }
99
+ return [3 /*break*/, 5];
100
+ case 5:
101
+ attempt++;
102
+ return [3 /*break*/, 1];
103
+ case 6: return [2 /*return*/];
104
+ }
105
+ });
106
+ });
107
+ }
108
+ function loadOnce(url, timeout) {
109
+ return new Promise(function (resolve, reject) {
110
+ var script = document.createElement("script");
111
+ script.src = url;
112
+ script.async = true;
113
+ var timer = setTimeout(function () {
114
+ cleanup();
115
+ reject(new Error("Timeout loading ".concat(url)));
116
+ }, timeout);
117
+ function cleanup() {
118
+ clearTimeout(timer);
119
+ script.remove();
120
+ }
121
+ script.onload = function () {
122
+ cleanup();
123
+ resolve();
124
+ };
125
+ script.onerror = function () {
126
+ cleanup();
127
+ reject(new Error("Failed to load ".concat(url)));
128
+ };
129
+ document.body.appendChild(script);
130
+ });
131
+ }
132
+
133
+ var EventBus = /** @class */ (function () {
134
+ function EventBus() {
135
+ this.map = new Map();
136
+ }
137
+ EventBus.prototype.on = function (event, handler) {
138
+ if (!this.map.has(event)) {
139
+ this.map.set(event, new Set());
140
+ }
141
+ this.map.get(event).add(handler);
142
+ };
143
+ EventBus.prototype.off = function (event, handler) {
144
+ var _a;
145
+ (_a = this.map.get(event)) === null || _a === void 0 ? void 0 : _a.delete(handler);
146
+ };
147
+ EventBus.prototype.emit = function (event, payload) {
148
+ var _a;
149
+ (_a = this.map.get(event)) === null || _a === void 0 ? void 0 : _a.forEach(function (fn) { return fn(payload); });
150
+ };
151
+ return EventBus;
152
+ }());
153
+
154
+ var ROUTER_EVENTS = {
155
+ REQUEST_NAVIGATE: "ROUTER:REQUEST_NAVIGATE",
156
+ NAVIGATE: "ROUTER:NAVIGATE",
157
+ };
158
+
159
+ function enableRouterSync(eventBus, navigate) {
160
+ eventBus.on(ROUTER_EVENTS.REQUEST_NAVIGATE, function (_a) {
161
+ var path = _a.path;
162
+ navigate(path);
163
+ eventBus.emit(ROUTER_EVENTS.NAVIGATE, { path: path });
164
+ });
165
+ }
166
+
167
+ var MFEHost = /** @class */ (function () {
168
+ function MFEHost(options) {
169
+ if (options === void 0) { options = {}; }
170
+ var _a;
171
+ this.eventBus = new EventBus();
172
+ this.stores = (_a = options.stores) !== null && _a !== void 0 ? _a : {};
173
+ this.navigate = options.navigate;
174
+ this.onRemoteError = options.onRemoteError;
175
+ if (this.navigate) {
176
+ enableRouterSync(this.eventBus, this.navigate);
177
+ }
178
+ }
179
+ /** load script only */
180
+ MFEHost.prototype.load = function (url, global) {
181
+ var _a, _b;
182
+ return __awaiter(this, void 0, void 0, function () {
183
+ var err_1, remote, err;
184
+ return __generator(this, function (_c) {
185
+ switch (_c.label) {
186
+ case 0:
187
+ _c.trys.push([0, 2, , 3]);
188
+ return [4 /*yield*/, loadScript(url, { retries: 1 })];
189
+ case 1:
190
+ _c.sent();
191
+ return [3 /*break*/, 3];
192
+ case 2:
193
+ err_1 = _c.sent();
194
+ (_a = this.onRemoteError) === null || _a === void 0 ? void 0 : _a.call(this, err_1);
195
+ throw err_1;
196
+ case 3:
197
+ remote = window[global];
198
+ if (!(remote === null || remote === void 0 ? void 0 : remote.mount)) {
199
+ err = new Error("Remote \"".concat(global, "\" not found"));
200
+ (_b = this.onRemoteError) === null || _b === void 0 ? void 0 : _b.call(this, err);
201
+ throw err;
202
+ }
203
+ return [2 /*return*/, remote];
204
+ }
205
+ });
206
+ });
207
+ };
208
+ /** mount with identity */
209
+ MFEHost.prototype.mount = function (remote, el, name) {
210
+ var _a;
211
+ var ctx = {
212
+ name: name,
213
+ eventBus: this.eventBus,
214
+ stores: this.stores,
215
+ host: {
216
+ navigate: this.navigate,
217
+ },
218
+ };
219
+ try {
220
+ remote.mount(el, ctx);
221
+ console.log("[MFE] mounted ".concat(name));
222
+ }
223
+ catch (err) {
224
+ (_a = this.onRemoteError) === null || _a === void 0 ? void 0 : _a.call(this, err);
225
+ throw err;
226
+ }
227
+ };
228
+ MFEHost.prototype.unmount = function (remote, el, name) {
229
+ var _a, _b;
230
+ try {
231
+ (_a = remote.unmount) === null || _a === void 0 ? void 0 : _a.call(remote, el);
232
+ name && console.log("[MFE] unmounted ".concat(name));
233
+ }
234
+ catch (err) {
235
+ (_b = this.onRemoteError) === null || _b === void 0 ? void 0 : _b.call(this, err);
236
+ }
237
+ };
238
+ MFEHost.prototype.getEventBus = function () {
239
+ return this.eventBus;
240
+ };
241
+ /** 🔥 reload = unmount + reload script + mount (KEEP name) */
242
+ MFEHost.prototype.reloadRemote = function (options) {
243
+ var _a;
244
+ return __awaiter(this, void 0, void 0, function () {
245
+ var name, url, global, el, oldRemote, newRemote;
246
+ return __generator(this, function (_b) {
247
+ switch (_b.label) {
248
+ case 0:
249
+ name = options.name, url = options.url, global = options.global, el = options.el;
250
+ oldRemote = window[global];
251
+ try {
252
+ (_a = oldRemote === null || oldRemote === void 0 ? void 0 : oldRemote.unmount) === null || _a === void 0 ? void 0 : _a.call(oldRemote, el);
253
+ }
254
+ catch (_c) { }
255
+ delete window[global];
256
+ return [4 /*yield*/, this.load("".concat(url, "?t=").concat(Date.now()), global)];
257
+ case 1:
258
+ newRemote = _b.sent();
259
+ this.mount(newRemote, el, name);
260
+ return [2 /*return*/];
261
+ }
262
+ });
263
+ });
264
+ };
265
+ return MFEHost;
266
+ }());
267
+
268
+ function createSharedStore(initial) {
269
+ var state = initial;
270
+ var listeners = new Set();
271
+ return {
272
+ getState: function () {
273
+ return state;
274
+ },
275
+ setState: function (partial) {
276
+ state = __assign(__assign({}, state), partial);
277
+ listeners.forEach(function (l) { return l(state); });
278
+ },
279
+ subscribe: function (fn) {
280
+ listeners.add(fn);
281
+ return function () { return listeners.delete(fn); };
282
+ },
283
+ };
284
+ }
285
+
286
+ function createSharedRouter(ctx) {
287
+ var eventBus = ctx.eventBus, name = ctx.name;
288
+ return {
289
+ /** Remote → Host */
290
+ go: function (path) {
291
+ eventBus.emit(ROUTER_EVENTS.REQUEST_NAVIGATE, {
292
+ from: name,
293
+ path: path,
294
+ });
295
+ },
296
+ /** Host → Remote */
297
+ onChange: function (cb) {
298
+ return eventBus.on(ROUTER_EVENTS.NAVIGATE, function (payload) {
299
+ cb(payload.path);
300
+ });
301
+ },
302
+ };
303
+ }
304
+
305
+ export { EventBus, MFEHost, createSharedRouter, createSharedStore, enableRouterSync, loadScript };
package/build/index.js ADDED
@@ -0,0 +1,320 @@
1
+ (function (global, factory) {
2
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
4
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.MFERuntimeZ = {}));
5
+ })(this, (function (exports) { 'use strict';
6
+
7
+ /******************************************************************************
8
+ Copyright (c) Microsoft Corporation.
9
+
10
+ Permission to use, copy, modify, and/or distribute this software for any
11
+ purpose with or without fee is hereby granted.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
14
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
15
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
16
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
17
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
18
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
19
+ PERFORMANCE OF THIS SOFTWARE.
20
+ ***************************************************************************** */
21
+
22
+ var __assign = function() {
23
+ __assign = Object.assign || function __assign(t) {
24
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
25
+ s = arguments[i];
26
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
27
+ }
28
+ return t;
29
+ };
30
+ return __assign.apply(this, arguments);
31
+ };
32
+
33
+ function __awaiter(thisArg, _arguments, P, generator) {
34
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
35
+ return new (P || (P = Promise))(function (resolve, reject) {
36
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
37
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
38
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
39
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
40
+ });
41
+ }
42
+
43
+ function __generator(thisArg, body) {
44
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
45
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
46
+ function verb(n) { return function (v) { return step([n, v]); }; }
47
+ function step(op) {
48
+ if (f) throw new TypeError("Generator is already executing.");
49
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
50
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
51
+ if (y = 0, t) op = [op[0] & 2, t.value];
52
+ switch (op[0]) {
53
+ case 0: case 1: t = op; break;
54
+ case 4: _.label++; return { value: op[1], done: false };
55
+ case 5: _.label++; y = op[1]; op = [0]; continue;
56
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
57
+ default:
58
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
59
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
60
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
61
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
62
+ if (t[2]) _.ops.pop();
63
+ _.trys.pop(); continue;
64
+ }
65
+ op = body.call(thisArg, _);
66
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
67
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
68
+ }
69
+ }
70
+
71
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
72
+ var e = new Error(message);
73
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
74
+ };
75
+
76
+ // loader/loadScript.ts
77
+ var loaded = new Set();
78
+ function loadScript(url, options) {
79
+ if (options === void 0) { options = {}; }
80
+ return __awaiter(this, void 0, void 0, function () {
81
+ var _a, timeout, _b, retries, attempt, err_1;
82
+ return __generator(this, function (_c) {
83
+ switch (_c.label) {
84
+ case 0:
85
+ _a = options.timeout, timeout = _a === void 0 ? 15000 : _a, _b = options.retries, retries = _b === void 0 ? 1 : _b;
86
+ if (loaded.has(url))
87
+ return [2 /*return*/];
88
+ attempt = 0;
89
+ _c.label = 1;
90
+ case 1:
91
+ if (!(attempt <= retries)) return [3 /*break*/, 6];
92
+ _c.label = 2;
93
+ case 2:
94
+ _c.trys.push([2, 4, , 5]);
95
+ return [4 /*yield*/, loadOnce(url, timeout)];
96
+ case 3:
97
+ _c.sent();
98
+ loaded.add(url);
99
+ return [2 /*return*/];
100
+ case 4:
101
+ err_1 = _c.sent();
102
+ if (attempt === retries) {
103
+ throw err_1;
104
+ }
105
+ return [3 /*break*/, 5];
106
+ case 5:
107
+ attempt++;
108
+ return [3 /*break*/, 1];
109
+ case 6: return [2 /*return*/];
110
+ }
111
+ });
112
+ });
113
+ }
114
+ function loadOnce(url, timeout) {
115
+ return new Promise(function (resolve, reject) {
116
+ var script = document.createElement("script");
117
+ script.src = url;
118
+ script.async = true;
119
+ var timer = setTimeout(function () {
120
+ cleanup();
121
+ reject(new Error("Timeout loading ".concat(url)));
122
+ }, timeout);
123
+ function cleanup() {
124
+ clearTimeout(timer);
125
+ script.remove();
126
+ }
127
+ script.onload = function () {
128
+ cleanup();
129
+ resolve();
130
+ };
131
+ script.onerror = function () {
132
+ cleanup();
133
+ reject(new Error("Failed to load ".concat(url)));
134
+ };
135
+ document.body.appendChild(script);
136
+ });
137
+ }
138
+
139
+ var EventBus = /** @class */ (function () {
140
+ function EventBus() {
141
+ this.map = new Map();
142
+ }
143
+ EventBus.prototype.on = function (event, handler) {
144
+ if (!this.map.has(event)) {
145
+ this.map.set(event, new Set());
146
+ }
147
+ this.map.get(event).add(handler);
148
+ };
149
+ EventBus.prototype.off = function (event, handler) {
150
+ var _a;
151
+ (_a = this.map.get(event)) === null || _a === void 0 ? void 0 : _a.delete(handler);
152
+ };
153
+ EventBus.prototype.emit = function (event, payload) {
154
+ var _a;
155
+ (_a = this.map.get(event)) === null || _a === void 0 ? void 0 : _a.forEach(function (fn) { return fn(payload); });
156
+ };
157
+ return EventBus;
158
+ }());
159
+
160
+ var ROUTER_EVENTS = {
161
+ REQUEST_NAVIGATE: "ROUTER:REQUEST_NAVIGATE",
162
+ NAVIGATE: "ROUTER:NAVIGATE",
163
+ };
164
+
165
+ function enableRouterSync(eventBus, navigate) {
166
+ eventBus.on(ROUTER_EVENTS.REQUEST_NAVIGATE, function (_a) {
167
+ var path = _a.path;
168
+ navigate(path);
169
+ eventBus.emit(ROUTER_EVENTS.NAVIGATE, { path: path });
170
+ });
171
+ }
172
+
173
+ var MFEHost = /** @class */ (function () {
174
+ function MFEHost(options) {
175
+ if (options === void 0) { options = {}; }
176
+ var _a;
177
+ this.eventBus = new EventBus();
178
+ this.stores = (_a = options.stores) !== null && _a !== void 0 ? _a : {};
179
+ this.navigate = options.navigate;
180
+ this.onRemoteError = options.onRemoteError;
181
+ if (this.navigate) {
182
+ enableRouterSync(this.eventBus, this.navigate);
183
+ }
184
+ }
185
+ /** load script only */
186
+ MFEHost.prototype.load = function (url, global) {
187
+ var _a, _b;
188
+ return __awaiter(this, void 0, void 0, function () {
189
+ var err_1, remote, err;
190
+ return __generator(this, function (_c) {
191
+ switch (_c.label) {
192
+ case 0:
193
+ _c.trys.push([0, 2, , 3]);
194
+ return [4 /*yield*/, loadScript(url, { retries: 1 })];
195
+ case 1:
196
+ _c.sent();
197
+ return [3 /*break*/, 3];
198
+ case 2:
199
+ err_1 = _c.sent();
200
+ (_a = this.onRemoteError) === null || _a === void 0 ? void 0 : _a.call(this, err_1);
201
+ throw err_1;
202
+ case 3:
203
+ remote = window[global];
204
+ if (!(remote === null || remote === void 0 ? void 0 : remote.mount)) {
205
+ err = new Error("Remote \"".concat(global, "\" not found"));
206
+ (_b = this.onRemoteError) === null || _b === void 0 ? void 0 : _b.call(this, err);
207
+ throw err;
208
+ }
209
+ return [2 /*return*/, remote];
210
+ }
211
+ });
212
+ });
213
+ };
214
+ /** mount with identity */
215
+ MFEHost.prototype.mount = function (remote, el, name) {
216
+ var _a;
217
+ var ctx = {
218
+ name: name,
219
+ eventBus: this.eventBus,
220
+ stores: this.stores,
221
+ host: {
222
+ navigate: this.navigate,
223
+ },
224
+ };
225
+ try {
226
+ remote.mount(el, ctx);
227
+ console.log("[MFE] mounted ".concat(name));
228
+ }
229
+ catch (err) {
230
+ (_a = this.onRemoteError) === null || _a === void 0 ? void 0 : _a.call(this, err);
231
+ throw err;
232
+ }
233
+ };
234
+ MFEHost.prototype.unmount = function (remote, el, name) {
235
+ var _a, _b;
236
+ try {
237
+ (_a = remote.unmount) === null || _a === void 0 ? void 0 : _a.call(remote, el);
238
+ name && console.log("[MFE] unmounted ".concat(name));
239
+ }
240
+ catch (err) {
241
+ (_b = this.onRemoteError) === null || _b === void 0 ? void 0 : _b.call(this, err);
242
+ }
243
+ };
244
+ MFEHost.prototype.getEventBus = function () {
245
+ return this.eventBus;
246
+ };
247
+ /** 🔥 reload = unmount + reload script + mount (KEEP name) */
248
+ MFEHost.prototype.reloadRemote = function (options) {
249
+ var _a;
250
+ return __awaiter(this, void 0, void 0, function () {
251
+ var name, url, global, el, oldRemote, newRemote;
252
+ return __generator(this, function (_b) {
253
+ switch (_b.label) {
254
+ case 0:
255
+ name = options.name, url = options.url, global = options.global, el = options.el;
256
+ oldRemote = window[global];
257
+ try {
258
+ (_a = oldRemote === null || oldRemote === void 0 ? void 0 : oldRemote.unmount) === null || _a === void 0 ? void 0 : _a.call(oldRemote, el);
259
+ }
260
+ catch (_c) { }
261
+ delete window[global];
262
+ return [4 /*yield*/, this.load("".concat(url, "?t=").concat(Date.now()), global)];
263
+ case 1:
264
+ newRemote = _b.sent();
265
+ this.mount(newRemote, el, name);
266
+ return [2 /*return*/];
267
+ }
268
+ });
269
+ });
270
+ };
271
+ return MFEHost;
272
+ }());
273
+
274
+ function createSharedStore(initial) {
275
+ var state = initial;
276
+ var listeners = new Set();
277
+ return {
278
+ getState: function () {
279
+ return state;
280
+ },
281
+ setState: function (partial) {
282
+ state = __assign(__assign({}, state), partial);
283
+ listeners.forEach(function (l) { return l(state); });
284
+ },
285
+ subscribe: function (fn) {
286
+ listeners.add(fn);
287
+ return function () { return listeners.delete(fn); };
288
+ },
289
+ };
290
+ }
291
+
292
+ function createSharedRouter(ctx) {
293
+ var eventBus = ctx.eventBus, name = ctx.name;
294
+ return {
295
+ /** Remote → Host */
296
+ go: function (path) {
297
+ eventBus.emit(ROUTER_EVENTS.REQUEST_NAVIGATE, {
298
+ from: name,
299
+ path: path,
300
+ });
301
+ },
302
+ /** Host → Remote */
303
+ onChange: function (cb) {
304
+ return eventBus.on(ROUTER_EVENTS.NAVIGATE, function (payload) {
305
+ cb(payload.path);
306
+ });
307
+ },
308
+ };
309
+ }
310
+
311
+ exports.EventBus = EventBus;
312
+ exports.MFEHost = MFEHost;
313
+ exports.createSharedRouter = createSharedRouter;
314
+ exports.createSharedStore = createSharedStore;
315
+ exports.enableRouterSync = enableRouterSync;
316
+ exports.loadScript = loadScript;
317
+
318
+ Object.defineProperty(exports, '__esModule', { value: true });
319
+
320
+ }));
@@ -0,0 +1 @@
1
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).MFERuntimeZ={})}(this,function(t){"use strict";var e=function(){return e=Object.assign||function(t){for(var e,n=1,o=arguments.length;n<o;n++)for(var r in e=arguments[n])Object.prototype.hasOwnProperty.call(e,r)&&(t[r]=e[r]);return t},e.apply(this,arguments)};function n(t,e,n,o){return new(n||(n=Promise))(function(r,i){function u(t){try{c(o.next(t))}catch(t){i(t)}}function a(t){try{c(o.throw(t))}catch(t){i(t)}}function c(t){var e;t.done?r(t.value):(e=t.value,e instanceof n?e:new n(function(t){t(e)})).then(u,a)}c((o=o.apply(t,e||[])).next())})}function o(t,e){var n,o,r,i={label:0,sent:function(){if(1&r[0])throw r[1];return r[1]},trys:[],ops:[]},u=Object.create(("function"==typeof Iterator?Iterator:Object).prototype);return u.next=a(0),u.throw=a(1),u.return=a(2),"function"==typeof Symbol&&(u[Symbol.iterator]=function(){return this}),u;function a(a){return function(c){return function(a){if(n)throw new TypeError("Generator is already executing.");for(;u&&(u=0,a[0]&&(i=0)),i;)try{if(n=1,o&&(r=2&a[0]?o.return:a[0]?o.throw||((r=o.return)&&r.call(o),0):o.next)&&!(r=r.call(o,a[1])).done)return r;switch(o=0,r&&(a=[2&a[0],r.value]),a[0]){case 0:case 1:r=a;break;case 4:return i.label++,{value:a[1],done:!1};case 5:i.label++,o=a[1],a=[0];continue;case 7:a=i.ops.pop(),i.trys.pop();continue;default:if(!(r=i.trys,(r=r.length>0&&r[r.length-1])||6!==a[0]&&2!==a[0])){i=0;continue}if(3===a[0]&&(!r||a[1]>r[0]&&a[1]<r[3])){i.label=a[1];break}if(6===a[0]&&i.label<r[1]){i.label=r[1],r=a;break}if(r&&i.label<r[2]){i.label=r[2],i.ops.push(a);break}r[2]&&i.ops.pop(),i.trys.pop();continue}a=e.call(t,i)}catch(t){a=[6,t],o=0}finally{n=r=0}if(5&a[0])throw a[1];return{value:a[0]?a[1]:void 0,done:!0}}([a,c])}}}"function"==typeof SuppressedError&&SuppressedError;var r=new Set;function i(t,e){return void 0===e&&(e={}),n(this,void 0,void 0,function(){var n,i,a,c,s,l;return o(this,function(o){switch(o.label){case 0:if(n=e.timeout,i=void 0===n?15e3:n,a=e.retries,c=void 0===a?1:a,r.has(t))return[2];s=0,o.label=1;case 1:if(!(s<=c))return[3,6];o.label=2;case 2:return o.trys.push([2,4,,5]),[4,u(t,i)];case 3:return o.sent(),r.add(t),[2];case 4:if(l=o.sent(),s===c)throw l;return[3,5];case 5:return s++,[3,1];case 6:return[2]}})})}function u(t,e){return new Promise(function(n,o){var r=document.createElement("script");r.src=t,r.async=!0;var i=setTimeout(function(){u(),o(new Error("Timeout loading ".concat(t)))},e);function u(){clearTimeout(i),r.remove()}r.onload=function(){u(),n()},r.onerror=function(){u(),o(new Error("Failed to load ".concat(t)))},document.body.appendChild(r)})}var a=function(){function t(){this.map=new Map}return t.prototype.on=function(t,e){this.map.has(t)||this.map.set(t,new Set),this.map.get(t).add(e)},t.prototype.off=function(t,e){var n;null===(n=this.map.get(t))||void 0===n||n.delete(e)},t.prototype.emit=function(t,e){var n;null===(n=this.map.get(t))||void 0===n||n.forEach(function(t){return t(e)})},t}(),c="ROUTER:REQUEST_NAVIGATE",s="ROUTER:NAVIGATE";function l(t,e){t.on(c,function(n){var o=n.path;e(o),t.emit(s,{path:o})})}var f=function(){function t(t){var e;void 0===t&&(t={}),this.eventBus=new a,this.stores=null!==(e=t.stores)&&void 0!==e?e:{},this.navigate=t.navigate,this.onRemoteError=t.onRemoteError,this.navigate&&l(this.eventBus,this.navigate)}return t.prototype.load=function(t,e){var r,u;return n(this,void 0,void 0,function(){var n,a,c;return o(this,function(o){switch(o.label){case 0:return o.trys.push([0,2,,3]),[4,i(t,{retries:1})];case 1:return o.sent(),[3,3];case 2:throw n=o.sent(),null===(r=this.onRemoteError)||void 0===r||r.call(this,n),n;case 3:if(!(null==(a=window[e])?void 0:a.mount))throw c=new Error('Remote "'.concat(e,'" not found')),null===(u=this.onRemoteError)||void 0===u||u.call(this,c),c;return[2,a]}})})},t.prototype.mount=function(t,e,n){var o,r={name:n,eventBus:this.eventBus,stores:this.stores,host:{navigate:this.navigate}};try{t.mount(e,r),console.log("[MFE] mounted ".concat(n))}catch(t){throw null===(o=this.onRemoteError)||void 0===o||o.call(this,t),t}},t.prototype.unmount=function(t,e,n){var o,r;try{null===(o=t.unmount)||void 0===o||o.call(t,e),n&&console.log("[MFE] unmounted ".concat(n))}catch(t){null===(r=this.onRemoteError)||void 0===r||r.call(this,t)}},t.prototype.getEventBus=function(){return this.eventBus},t.prototype.reloadRemote=function(t){var e;return n(this,void 0,void 0,function(){var n,r,i,u,a,c;return o(this,function(o){switch(o.label){case 0:n=t.name,r=t.url,i=t.global,u=t.el,a=window[i];try{null===(e=null==a?void 0:a.unmount)||void 0===e||e.call(a,u)}catch(t){}return delete window[i],[4,this.load("".concat(r,"?t=").concat(Date.now()),i)];case 1:return c=o.sent(),this.mount(c,u,n),[2]}})})},t}();t.EventBus=a,t.MFEHost=f,t.createSharedRouter=function(t){var e=t.eventBus,n=t.name;return{go:function(t){e.emit(c,{from:n,path:t})},onChange:function(t){return e.on(s,function(e){t(e.path)})}}},t.createSharedStore=function(t){var n=t,o=new Set;return{getState:function(){return n},setState:function(t){n=e(e({},n),t),o.forEach(function(t){return t(n)})},subscribe:function(t){return o.add(t),function(){return o.delete(t)}}}},t.enableRouterSync=l,t.loadScript=i,Object.defineProperty(t,"__esModule",{value:!0})});
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ export type ReloadMessage = {
2
+ type: "RELOAD";
3
+ name: string;
4
+ };
5
+ export declare function startMFEDevServer(onReload: (name: string) => void): void;
@@ -0,0 +1,3 @@
1
+ export { startMFEDevServer } from "./devServer";
2
+ export { createMFEReloadServer } from "./reloadServer";
3
+ export type { DevRemoteConfig } from "./reloadServer";
@@ -0,0 +1,7 @@
1
+ import { MFEHost } from "../host/MFEHost";
2
+ export type DevRemoteConfig = {
3
+ url: string;
4
+ global: string;
5
+ el: () => HTMLElement;
6
+ };
7
+ export declare function createMFEReloadServer(host: MFEHost, remotes: Record<string, DevRemoteConfig>): () => void;
@@ -0,0 +1,7 @@
1
+ export type Handler<T = any> = (payload: T) => void;
2
+ export declare class EventBus<Events extends Record<string, any> = any> {
3
+ private map;
4
+ on<K extends keyof Events>(event: K, handler: Handler<Events[K]>): void;
5
+ off<K extends keyof Events>(event: K, handler: Handler<Events[K]>): void;
6
+ emit<K extends keyof Events>(event: K, payload: Events[K]): void;
7
+ }
@@ -0,0 +1,28 @@
1
+ import { EventBus } from "../event-bus/EventBus";
2
+ import type { RemoteApp } from "../types/RemoteApp";
3
+ type HostOptions = {
4
+ stores?: Record<string, any>;
5
+ navigate?: (path: string) => void;
6
+ onRemoteError?: (error: Error) => void;
7
+ };
8
+ export declare class MFEHost {
9
+ private eventBus;
10
+ private stores;
11
+ private navigate?;
12
+ private onRemoteError?;
13
+ constructor(options?: HostOptions);
14
+ /** load script only */
15
+ load(url: string, global: string): Promise<RemoteApp>;
16
+ /** mount with identity */
17
+ mount(remote: RemoteApp, el: HTMLElement, name: string): void;
18
+ unmount(remote: RemoteApp, el: HTMLElement, name?: string): void;
19
+ getEventBus(): EventBus<any>;
20
+ /** 🔥 reload = unmount + reload script + mount (KEEP name) */
21
+ reloadRemote(options: {
22
+ name: string;
23
+ url: string;
24
+ global: string;
25
+ el: HTMLElement;
26
+ }): Promise<void>;
27
+ }
28
+ export {};
@@ -0,0 +1,13 @@
1
+ type RemoteConfig = {
2
+ name: string;
3
+ url: string;
4
+ global: string;
5
+ el: HTMLElement;
6
+ };
7
+ export declare class RemoteRegistry {
8
+ private remotes;
9
+ register(config: RemoteConfig): void;
10
+ get(name: string): RemoteConfig;
11
+ getAll(): any[];
12
+ }
13
+ export {};
@@ -0,0 +1 @@
1
+ export declare function enableRouterSync(eventBus: any, navigate: (path: string) => void): void;
@@ -0,0 +1,6 @@
1
+ type LoadOptions = {
2
+ timeout?: number;
3
+ retries?: number;
4
+ };
5
+ export declare function loadScript(url: string, options?: LoadOptions): Promise<void>;
6
+ export {};
@@ -0,0 +1,9 @@
1
+ export declare function createSharedRouter(ctx: {
2
+ eventBus: any;
3
+ name: string;
4
+ }): {
5
+ /** Remote → Host */
6
+ go(path: string): void;
7
+ /** Host → Remote */
8
+ onChange(cb: (path: string) => void): any;
9
+ };
@@ -0,0 +1,4 @@
1
+ export declare const ROUTER_EVENTS: {
2
+ readonly REQUEST_NAVIGATE: "ROUTER:REQUEST_NAVIGATE";
3
+ readonly NAVIGATE: "ROUTER:NAVIGATE";
4
+ };
@@ -0,0 +1,6 @@
1
+ export type Unsubscribe = () => void;
2
+ export declare function createSharedStore<T extends object>(initial: T): {
3
+ getState(): T;
4
+ setState(partial: Partial<T>): void;
5
+ subscribe(fn: (s: T) => void): Unsubscribe;
6
+ };
@@ -0,0 +1,10 @@
1
+ import type { EventBus } from "../event-bus/EventBus";
2
+ export type MFEContext = {
3
+ /** identity của microFE hiện tại */
4
+ name: string;
5
+ eventBus: EventBus<any>;
6
+ stores: Record<string, any>;
7
+ host?: {
8
+ navigate?: (path: string) => void;
9
+ };
10
+ };
@@ -0,0 +1,6 @@
1
+ import { MFEContext } from "./MFEContext";
2
+ export type RemoteApp = {
3
+ name?: string;
4
+ mount: (el: HTMLElement, ctx: MFEContext) => void;
5
+ unmount?: (el: HTMLElement) => void;
6
+ };
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "mfe-runtime-z",
3
+ "version": "1.0.0",
4
+ "description": "Framework-agnostic micro-frontend runtime (host, event bus, shared state, router sync)",
5
+ "main": "build/index.js",
6
+ "module": "build/index.esm.js",
7
+ "browser": "build/index.js",
8
+ "unpkg": "build/index.min.js",
9
+ "types": "build/index.d.ts",
10
+ "files": [
11
+ "build"
12
+ ],
13
+ "exports": {
14
+ ".": {
15
+ "import": "./build/index.esm.js",
16
+ "require": "./build/index.js"
17
+ },
18
+ "./dev": {
19
+ "import": "./build/dev.esm.js",
20
+ "require": "./build/dev.js"
21
+ }
22
+ },
23
+ "scripts": {
24
+ "clean": "rimraf build",
25
+ "build": "rollup -c",
26
+ "cb": "npm run clean && npm run build",
27
+ "prepublishOnly": "npm run build"
28
+ },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/delpikye-v/mfe-runtime-z.git"
32
+ },
33
+ "homepage": "https://github.com/delpikye-v/mfe-runtime-z#readme",
34
+ "keywords": [
35
+ "micro-frontend",
36
+ "mfe",
37
+ "runtime",
38
+ "event-bus",
39
+ "shared-state",
40
+ "router-sync",
41
+ "framework-agnostic",
42
+ "micro-frontend-runtime",
43
+ "mfe-runtime"
44
+ ],
45
+ "author": "Delpi.Kye",
46
+ "license": "MIT",
47
+ "bugs": {
48
+ "url": "https://github.com/delpikye-v/mfe-runtime-z/issues"
49
+ },
50
+ "devDependencies": {
51
+ "@babel/core": "^7.15.0",
52
+ "@rollup/plugin-commonjs": "^17.1.0",
53
+ "@rollup/plugin-node-resolve": "^11.2.1",
54
+ "babel-loader": "^8.2.2",
55
+ "rimraf": "^3.0.2",
56
+ "rollup": "^2.56.3",
57
+ "rollup-plugin-copy": "^3.4.0",
58
+ "rollup-plugin-peer-deps-external": "^2.2.4",
59
+ "rollup-plugin-terser": "^7.0.2",
60
+ "rollup-plugin-typescript2": "^0.29.0",
61
+ "tslib": "^2.3.1",
62
+ "typescript": "^4.4.2",
63
+ "ws": "^8.18.3"
64
+ },
65
+ "optionalDependencies": {
66
+ "ws": "^8.18.3"
67
+ },
68
+ "dependencies": {
69
+ "eventbus-z": "^2.1.0"
70
+ }
71
+ }