remote-reload-utils 0.0.12 → 0.0.14

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/dist/main.cjs CHANGED
@@ -1,5 +1,16 @@
1
- "use strict";
2
- var __webpack_require__ = {};
1
+ var __webpack_modules__ = {
2
+ "./src/styles/index.css": function() {}
3
+ };
4
+ var __webpack_module_cache__ = {};
5
+ function __webpack_require__(moduleId) {
6
+ var cachedModule = __webpack_module_cache__[moduleId];
7
+ if (void 0 !== cachedModule) return cachedModule.exports;
8
+ var module = __webpack_module_cache__[moduleId] = {
9
+ exports: {}
10
+ };
11
+ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
12
+ return module.exports;
13
+ }
3
14
  (()=>{
4
15
  __webpack_require__.n = (module)=>{
5
16
  var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
@@ -31,1036 +42,1037 @@ var __webpack_require__ = {};
31
42
  };
32
43
  })();
33
44
  var __webpack_exports__ = {};
34
- __webpack_require__.r(__webpack_exports__);
35
- __webpack_require__.d(__webpack_exports__, {
36
- compareVersions: ()=>compareVersions,
37
- fetchAvailableVersions: ()=>fetchAvailableVersions,
38
- sortVersions: ()=>sortVersions,
39
- useRemoteModuleHook: ()=>useRemoteModuleHook,
40
- registerRemoteInstance: ()=>registerRemoteInstance,
41
- fallbackPlugin: ()=>fallbackPlugin,
42
- createEventBus: ()=>createEventBus,
43
- withRemote: ()=>withRemote,
44
- getPreloadStatus: ()=>getPreloadStatus,
45
- parseVersion: ()=>parseVersion,
46
- resolveFinalVersion: ()=>resolveFinalVersion,
47
- buildFinalUrls: ()=>buildFinalUrls,
48
- fetchLatestVersion: ()=>fetchLatestVersion,
49
- ErrorBoundary: ()=>ErrorBoundary,
50
- getFinalSharedConfig: ()=>getFinalSharedConfig,
51
- isRemoteLoaded: ()=>isRemoteLoaded,
52
- tryLoadRemote: ()=>tryLoadRemote,
53
- getVersionCache: ()=>getVersionCache,
54
- eventBus: ()=>eventBus,
55
- RemoteModuleRenderer: ()=>RemoteModuleRenderer,
56
- clearPreloadCache: ()=>clearPreloadCache,
57
- loadRemoteMultiVersion: ()=>loadRemoteMultiVersion,
58
- getRemoteHealthReport: ()=>getRemoteHealthReport,
59
- checkModuleLoadable: ()=>checkModuleLoadable,
60
- getStableVersions: ()=>getStableVersions,
61
- extractMajorVersion: ()=>extractMajorVersion,
62
- buildCdnUrls: ()=>buildCdnUrls,
63
- loadReactVersion: ()=>loadReactVersion,
64
- preloadRemoteList: ()=>preloadRemoteList,
65
- SuspenseRemoteLoader: ()=>SuspenseRemoteLoader,
66
- registerLoadedModule: ()=>registerLoadedModule,
67
- getCompatibleReactVersions: ()=>getCompatibleReactVersions,
68
- SuspenseRemote: ()=>SuspenseRemote,
69
- unloadRemote: ()=>unloadRemote,
70
- useRemoteModule: ()=>useRemoteModule,
71
- RemoteModuleProvider: ()=>RemoteModuleProvider,
72
- cancelPreload: ()=>cancelPreload,
73
- findCompatibleVersion: ()=>findCompatibleVersion,
74
- getLoadedRemotes: ()=>getLoadedRemotes,
75
- getLatestVersion: ()=>getLatestVersion,
76
- preloadRemote: ()=>preloadRemote,
77
- satisfiesVersion: ()=>satisfiesVersion,
78
- formatHealthStatus: ()=>formatHealthStatus,
79
- unloadAll: ()=>unloadAll,
80
- setVersionCache: ()=>setVersionCache,
81
- lazyRemote: ()=>lazyRemote,
82
- checkRemoteHealth: ()=>checkRemoteHealth,
83
- isPrerelease: ()=>isPrerelease,
84
- checkVersionCompatibility: ()=>checkVersionCompatibility
85
- });
86
- const runtime_namespaceObject = require("@module-federation/enhanced/runtime");
87
- async function loadReactVersion(version) {
88
- const runtime = await (0, runtime_namespaceObject.createInstance)({
89
- name: `react_${version}_runtime`,
90
- remotes: [
91
- {
92
- name: `react@${version}`,
93
- entry: `https://cdn.jsdelivr.net/npm/react@${version}/umd/react.production.min.js`,
94
- type: 'var',
95
- entryGlobalName: 'React'
96
- },
97
- {
98
- name: `react-dom@${version}`,
99
- entry: `https://cdn.jsdelivr.net/npm/react-dom@${version}/umd/react-dom.production.min.js`,
100
- type: 'var',
101
- entryGlobalName: 'ReactDOM'
102
- }
103
- ]
45
+ (()=>{
46
+ "use strict";
47
+ __webpack_require__.r(__webpack_exports__);
48
+ __webpack_require__.d(__webpack_exports__, {
49
+ compareVersions: ()=>compareVersions,
50
+ fetchAvailableVersions: ()=>fetchAvailableVersions,
51
+ sortVersions: ()=>sortVersions,
52
+ useRemoteModuleHook: ()=>useRemoteModuleHook,
53
+ registerRemoteInstance: ()=>registerRemoteInstance,
54
+ fallbackPlugin: ()=>fallbackPlugin,
55
+ createEventBus: ()=>createEventBus,
56
+ withRemote: ()=>withRemote,
57
+ getPreloadStatus: ()=>getPreloadStatus,
58
+ parseVersion: ()=>parseVersion,
59
+ resolveFinalVersion: ()=>resolveFinalVersion,
60
+ buildFinalUrls: ()=>buildFinalUrls,
61
+ fetchLatestVersion: ()=>fetchLatestVersion,
62
+ ErrorBoundary: ()=>ErrorBoundary,
63
+ getFinalSharedConfig: ()=>getFinalSharedConfig,
64
+ isRemoteLoaded: ()=>isRemoteLoaded,
65
+ tryLoadRemote: ()=>tryLoadRemote,
66
+ getVersionCache: ()=>getVersionCache,
67
+ eventBus: ()=>eventBus,
68
+ RemoteModuleRenderer: ()=>RemoteModuleRenderer,
69
+ clearPreloadCache: ()=>clearPreloadCache,
70
+ loadRemoteMultiVersion: ()=>loadRemoteMultiVersion,
71
+ getRemoteHealthReport: ()=>getRemoteHealthReport,
72
+ checkModuleLoadable: ()=>checkModuleLoadable,
73
+ getStableVersions: ()=>getStableVersions,
74
+ extractMajorVersion: ()=>extractMajorVersion,
75
+ buildCdnUrls: ()=>buildCdnUrls,
76
+ loadReactVersion: ()=>loadReactVersion,
77
+ preloadRemoteList: ()=>preloadRemoteList,
78
+ SuspenseRemoteLoader: ()=>SuspenseRemoteLoader,
79
+ registerLoadedModule: ()=>registerLoadedModule,
80
+ getCompatibleReactVersions: ()=>getCompatibleReactVersions,
81
+ SuspenseRemote: ()=>SuspenseRemote,
82
+ unloadRemote: ()=>unloadRemote,
83
+ useRemoteModule: ()=>useRemoteModule,
84
+ RemoteModuleProvider: ()=>RemoteModuleProvider,
85
+ cancelPreload: ()=>cancelPreload,
86
+ findCompatibleVersion: ()=>findCompatibleVersion,
87
+ getLoadedRemotes: ()=>getLoadedRemotes,
88
+ getLatestVersion: ()=>getLatestVersion,
89
+ preloadRemote: ()=>preloadRemote,
90
+ satisfiesVersion: ()=>satisfiesVersion,
91
+ formatHealthStatus: ()=>formatHealthStatus,
92
+ unloadAll: ()=>unloadAll,
93
+ setVersionCache: ()=>setVersionCache,
94
+ lazyRemote: ()=>lazyRemote,
95
+ checkRemoteHealth: ()=>checkRemoteHealth,
96
+ isPrerelease: ()=>isPrerelease,
97
+ checkVersionCompatibility: ()=>checkVersionCompatibility
104
98
  });
105
- const React = await runtime.loadRemote(`react@${version}`);
106
- const ReactDOM = await runtime.loadRemote(`react-dom@${version}`);
107
- return {
108
- React,
109
- ReactDOM
110
- };
111
- }
112
- function parseVersion(version) {
113
- const cleaned = version.replace(/^v/i, '');
114
- const parts = cleaned.split(/[-+]/);
115
- const [major, minor, patch] = parts[0].split('.').map(Number);
116
- return {
117
- major: isNaN(major) ? 0 : major,
118
- minor: isNaN(minor) ? 0 : minor,
119
- patch: isNaN(patch) ? 0 : patch,
120
- prerelease: parts[1],
121
- build: parts[2],
122
- raw: cleaned
123
- };
124
- }
125
- function compareVersions(v1, v2) {
126
- const p1 = parseVersion(v1);
127
- const p2 = parseVersion(v2);
128
- if (p1.major !== p2.major) return p1.major - p2.major;
129
- if (p1.minor !== p2.minor) return p1.minor - p2.minor;
130
- if (p1.patch !== p2.patch) return p1.patch - p2.patch;
131
- if (p1.prerelease && !p2.prerelease) return -1;
132
- if (!p1.prerelease && p2.prerelease) return 1;
133
- if (p1.prerelease && p2.prerelease) return p1.prerelease.localeCompare(p2.prerelease);
134
- return 0;
135
- }
136
- function satisfiesVersion(current, required) {
137
- const opMatch = required.match(/^(>=|<=|>|<|=|~|\^)?/);
138
- const operator = opMatch?.[1] || '=';
139
- const version = required.replace(/^(>=|<=|>|<|=|~|\^)/, '');
140
- const cmp = compareVersions(current, version);
141
- switch(operator){
142
- case '>':
143
- return cmp > 0;
144
- case '>=':
145
- return cmp >= 0;
146
- case '<':
147
- return cmp < 0;
148
- case '<=':
149
- return cmp <= 0;
150
- case '=':
151
- case '':
152
- return 0 === cmp;
153
- case '^':
154
- return parseVersion(current).major === parseVersion(version).major && (parseVersion(current).major > 0 || parseVersion(current).minor === parseVersion(version).minor);
155
- case '~':
156
- return parseVersion(current).major === parseVersion(version).major && parseVersion(current).minor === parseVersion(version).minor;
157
- default:
158
- return 0 === cmp;
159
- }
160
- }
161
- function checkVersionCompatibility(currentVersion, requiredVersion, packageName) {
162
- const isCompatible = satisfiesVersion(currentVersion, requiredVersion);
163
- const current = parseVersion(currentVersion);
164
- const required = parseVersion(requiredVersion);
165
- let message;
166
- let suggestion;
167
- let severity;
168
- if (isCompatible) {
169
- message = `${packageName}@${currentVersion} 满足要求 ${requiredVersion}`;
170
- severity = 'info';
171
- } else {
172
- const cmp = compareVersions(currentVersion, requiredVersion);
173
- if (cmp < 0) {
174
- message = `${packageName}@${currentVersion} 版本过低,需要 ${requiredVersion}`;
175
- severity = 'error';
176
- current.patch;
177
- current.major, current.minor;
178
- suggestion = `建议升级到 ${current.major}.${current.minor}.x 或更高版本`;
179
- } else {
180
- message = `${packageName}@${currentVersion} 版本过高,需要 ${requiredVersion}`;
181
- severity = 'warning';
182
- suggestion = `建议降级到 ${required.major}.${required.minor}.x 或匹配主版本的兼容版本`;
183
- }
99
+ __webpack_require__("./src/styles/index.css");
100
+ const runtime_namespaceObject = require("@module-federation/enhanced/runtime");
101
+ async function loadReactVersion(version) {
102
+ const runtime = await (0, runtime_namespaceObject.createInstance)({
103
+ name: `react_${version}_runtime`,
104
+ remotes: [
105
+ {
106
+ name: `react@${version}`,
107
+ entry: `https://cdn.jsdelivr.net/npm/react@${version}/umd/react.production.min.js`,
108
+ type: 'var',
109
+ entryGlobalName: 'React'
110
+ },
111
+ {
112
+ name: `react-dom@${version}`,
113
+ entry: `https://cdn.jsdelivr.net/npm/react-dom@${version}/umd/react-dom.production.min.js`,
114
+ type: 'var',
115
+ entryGlobalName: 'ReactDOM'
116
+ }
117
+ ]
118
+ });
119
+ const React = await runtime.loadRemote(`react@${version}`);
120
+ const ReactDOM = await runtime.loadRemote(`react-dom@${version}`);
121
+ return {
122
+ React,
123
+ ReactDOM
124
+ };
184
125
  }
185
- return {
186
- compatible: isCompatible,
187
- currentVersion,
188
- requiredVersion,
189
- suggestion,
190
- severity,
191
- message
192
- };
193
- }
194
- function findCompatibleVersion(availableVersions, range) {
195
- let candidates = availableVersions;
196
- if (range.exact) candidates = candidates.filter((v)=>v === range.exact);
197
- else {
198
- if (range.min) candidates = candidates.filter((v)=>compareVersions(v, range.min) >= 0);
199
- if (range.max) candidates = candidates.filter((v)=>compareVersions(v, range.max) <= 0);
126
+ function parseVersion(version) {
127
+ const cleaned = version.replace(/^v/i, '');
128
+ const parts = cleaned.split(/[-+]/);
129
+ const [major, minor, patch] = parts[0].split('.').map(Number);
130
+ return {
131
+ major: isNaN(major) ? 0 : major,
132
+ minor: isNaN(minor) ? 0 : minor,
133
+ patch: isNaN(patch) ? 0 : patch,
134
+ prerelease: parts[1],
135
+ build: parts[2],
136
+ raw: cleaned
137
+ };
200
138
  }
201
- if (0 === candidates.length) return null;
202
- return candidates.sort((a, b)=>compareVersions(b, a))[0];
203
- }
204
- function getCompatibleReactVersions(hostVersion) {
205
- const host = parseVersion(hostVersion);
206
- const versions = [];
207
- for(let i = host.major; i >= 15; i--)for(let j = 0; j <= 5; j++){
208
- const version = `${i}.${j}.0`;
209
- if (checkVersionCompatibility(version, `^${host.major}.0.0`, 'react').compatible) versions.push(version);
139
+ function compareVersions(v1, v2) {
140
+ const p1 = parseVersion(v1);
141
+ const p2 = parseVersion(v2);
142
+ if (p1.major !== p2.major) return p1.major - p2.major;
143
+ if (p1.minor !== p2.minor) return p1.minor - p2.minor;
144
+ if (p1.patch !== p2.patch) return p1.patch - p2.patch;
145
+ if (p1.prerelease && !p2.prerelease) return -1;
146
+ if (!p1.prerelease && p2.prerelease) return 1;
147
+ if (p1.prerelease && p2.prerelease) return p1.prerelease.localeCompare(p2.prerelease);
148
+ return 0;
210
149
  }
211
- const uniqueVersions = [];
212
- const seen = new Set();
213
- for (const v of versions)if (!seen.has(v)) {
214
- seen.add(v);
215
- uniqueVersions.push(v);
150
+ function satisfiesVersion(current, required) {
151
+ const opMatch = required.match(/^(>=|<=|>|<|=|~|\^)?/);
152
+ const operator = opMatch?.[1] || '=';
153
+ const version = required.replace(/^(>=|<=|>|<|=|~|\^)/, '');
154
+ const cmp = compareVersions(current, version);
155
+ switch(operator){
156
+ case '>':
157
+ return cmp > 0;
158
+ case '>=':
159
+ return cmp >= 0;
160
+ case '<':
161
+ return cmp < 0;
162
+ case '<=':
163
+ return cmp <= 0;
164
+ case '=':
165
+ case '':
166
+ return 0 === cmp;
167
+ case '^':
168
+ return parseVersion(current).major === parseVersion(version).major && (parseVersion(current).major > 0 || parseVersion(current).minor === parseVersion(version).minor);
169
+ case '~':
170
+ return parseVersion(current).major === parseVersion(version).major && parseVersion(current).minor === parseVersion(version).minor;
171
+ default:
172
+ return 0 === cmp;
173
+ }
216
174
  }
217
- return uniqueVersions;
218
- }
219
- async function fetchAvailableVersions(pkg) {
220
- try {
221
- const res = await fetch(`https://registry.npmjs.org/${pkg}`);
222
- if (!res.ok) return [];
223
- const data = await res.json();
224
- return Object.keys(data.versions || {});
225
- } catch {
226
- return [];
175
+ function checkVersionCompatibility(currentVersion, requiredVersion, packageName) {
176
+ const isCompatible = satisfiesVersion(currentVersion, requiredVersion);
177
+ const current = parseVersion(currentVersion);
178
+ const required = parseVersion(requiredVersion);
179
+ let message;
180
+ let suggestion;
181
+ let severity;
182
+ if (isCompatible) {
183
+ message = `${packageName}@${currentVersion} 满足要求 ${requiredVersion}`;
184
+ severity = 'info';
185
+ } else {
186
+ const cmp = compareVersions(currentVersion, requiredVersion);
187
+ if (cmp < 0) {
188
+ message = `${packageName}@${currentVersion} 版本过低,需要 ${requiredVersion}`;
189
+ severity = 'error';
190
+ current.patch;
191
+ current.major, current.minor;
192
+ suggestion = `建议升级到 ${current.major}.${current.minor}.x 或更高版本`;
193
+ } else {
194
+ message = `${packageName}@${currentVersion} 版本过高,需要 ${requiredVersion}`;
195
+ severity = 'warning';
196
+ suggestion = `建议降级到 ${required.major}.${required.minor}.x 或匹配主版本的兼容版本`;
197
+ }
198
+ }
199
+ return {
200
+ compatible: isCompatible,
201
+ currentVersion,
202
+ requiredVersion,
203
+ suggestion,
204
+ severity,
205
+ message
206
+ };
227
207
  }
228
- }
229
- function sortVersions(versions, order = 'desc') {
230
- return [
231
- ...versions
232
- ].sort((a, b)=>{
233
- const cmp = compareVersions(a, b);
234
- return 'desc' === order ? -cmp : cmp;
235
- });
236
- }
237
- function getLatestVersion(versions) {
238
- if (0 === versions.length) return null;
239
- return sortVersions(versions, 'desc')[0];
240
- }
241
- function getStableVersions(versions) {
242
- return versions.filter((v)=>!v.includes('alpha') && !v.includes('beta') && !v.includes('rc'));
243
- }
244
- function extractMajorVersion(version) {
245
- return parseVersion(version).major;
246
- }
247
- function isPrerelease(version) {
248
- const v = parseVersion(version);
249
- return !!v.prerelease;
250
- }
251
- const fallbackPlugin = ()=>({
252
- name: 'fallback-plugin',
253
- errorLoadRemote (args) {
254
- const fallback = 'fallback';
255
- console.log(args, 'args');
256
- return fallback;
208
+ function findCompatibleVersion(availableVersions, range) {
209
+ let candidates = availableVersions;
210
+ if (range.exact) candidates = candidates.filter((v)=>v === range.exact);
211
+ else {
212
+ if (range.min) candidates = candidates.filter((v)=>compareVersions(v, range.min) >= 0);
213
+ if (range.max) candidates = candidates.filter((v)=>compareVersions(v, range.max) <= 0);
257
214
  }
258
- });
259
- const DEFAULT_CDN_TEMPLATES = [
260
- 'https://cdn.jsdelivr.net/npm/{pkg}@{version}/dist/remoteEntry.js',
261
- 'https://unpkg.com/{pkg}@{version}/dist/remoteEntry.js'
262
- ];
263
- const DEFAULT_SHARED_CONFIG = {
264
- react: {
265
- shareConfig: {
266
- singleton: true,
267
- eager: true,
268
- requiredVersion: false
215
+ if (0 === candidates.length) return null;
216
+ return candidates.sort((a, b)=>compareVersions(b, a))[0];
217
+ }
218
+ function getCompatibleReactVersions(hostVersion) {
219
+ const host = parseVersion(hostVersion);
220
+ const versions = [];
221
+ for(let i = host.major; i >= 15; i--)for(let j = 0; j <= 5; j++){
222
+ const version = `${i}.${j}.0`;
223
+ if (checkVersionCompatibility(version, `^${host.major}.0.0`, 'react').compatible) versions.push(version);
269
224
  }
270
- },
271
- 'react-dom': {
272
- shareConfig: {
273
- singleton: true,
274
- eager: true,
275
- requiredVersion: false
225
+ const uniqueVersions = [];
226
+ const seen = new Set();
227
+ for (const v of versions)if (!seen.has(v)) {
228
+ seen.add(v);
229
+ uniqueVersions.push(v);
276
230
  }
231
+ return uniqueVersions;
277
232
  }
278
- };
279
- async function fetchLatestVersion(pkg) {
280
- const res = await fetch(`https://registry.npmjs.org/${pkg}`);
281
- if (!res.ok) throw new Error(`[MF] 无法获取 ${pkg} 的版本信息,状态码:${res.status}`);
282
- const data = await res.json();
283
- const latest = data['dist-tags']?.latest;
284
- if (!latest) throw new Error(`[MF] 无法从 NPM 获取 ${pkg} 的 latest tag`);
285
- return latest;
286
- }
287
- function getVersionCache() {
288
- try {
289
- const cacheStr = localStorage.getItem('mf-multi-version');
290
- return cacheStr ? JSON.parse(cacheStr) : {};
291
- } catch (e) {
292
- console.error('[MF Cache] 读取缓存失败:', e);
293
- return {};
294
- }
295
- }
296
- function setVersionCache(pkg, version) {
297
- try {
298
- const cache = getVersionCache();
299
- cache[pkg] = cache[pkg] || {};
300
- cache[pkg][version] = {
301
- timestamp: Date.now()
302
- };
303
- localStorage.setItem('mf-multi-version', JSON.stringify(cache));
304
- } catch (e) {
305
- console.error('[MF Cache] 写入缓存失败:', e);
233
+ async function fetchAvailableVersions(pkg) {
234
+ try {
235
+ const res = await fetch(`https://registry.npmjs.org/${pkg}`);
236
+ if (!res.ok) return [];
237
+ const data = await res.json();
238
+ return Object.keys(data.versions || {});
239
+ } catch {
240
+ return [];
241
+ }
306
242
  }
307
- }
308
- function buildCdnUrls(pkg, version) {
309
- return DEFAULT_CDN_TEMPLATES.map((template)=>template.replace('{pkg}', pkg).replace('{version}', version));
310
- }
311
- async function tryLoadRemote(scopeName, url, retries, delay, sharedConfig, plugins) {
312
- let lastError;
313
- for(let i = 0; i < retries; i++)try {
314
- const mf = (0, runtime_namespaceObject.createInstance)({
315
- name: 'host',
316
- remotes: [
317
- {
318
- name: scopeName,
319
- entry: url
320
- }
321
- ],
322
- shared: sharedConfig,
323
- plugins: [
324
- ...plugins,
325
- fallbackPlugin()
326
- ]
243
+ function sortVersions(versions, order = 'desc') {
244
+ return [
245
+ ...versions
246
+ ].sort((a, b)=>{
247
+ const cmp = compareVersions(a, b);
248
+ return 'desc' === order ? -cmp : cmp;
327
249
  });
328
- return {
329
- scopeName,
330
- mf
331
- };
332
- } catch (e) {
333
- lastError = e;
334
- console.warn(`[MF] URL ${url} 加载失败,第 ${i + 1} 次重试...`);
335
- if (i < retries - 1) await new Promise((res)=>setTimeout(res, delay));
336
250
  }
337
- throw new Error(`[MF] URL ${url} 经过 ${retries} 次重试仍加载失败。`, {
338
- cause: lastError
339
- });
340
- }
341
- function getFinalSharedConfig(customShared) {
342
- return {
343
- ...DEFAULT_SHARED_CONFIG,
344
- ...customShared || {}
345
- };
346
- }
347
- async function resolveFinalVersion(pkg, version, cacheTTL, revalidate) {
348
- let finalVersion = version;
349
- if ('latest' === version) {
350
- const cache = getVersionCache();
351
- const versions = cache[pkg] || {};
352
- const latestCached = Object.keys(versions).sort((a, b)=>versions[b].timestamp - versions[a].timestamp)[0];
353
- if (latestCached && Date.now() - versions[latestCached].timestamp < cacheTTL) {
354
- finalVersion = latestCached;
355
- if (revalidate) fetchLatestVersion(pkg).then((latest)=>{
356
- if (latest !== latestCached) {
357
- console.log(`[MF] 发现 ${pkg} 新版本 ${latest},已更新缓存。`);
358
- setVersionCache(pkg, latest);
359
- }
360
- }).catch((e)=>console.error(`[MF] 异步检查最新版本失败:`, e));
361
- } else {
362
- finalVersion = await fetchLatestVersion(pkg);
363
- setVersionCache(pkg, finalVersion);
364
- }
251
+ function getLatestVersion(versions) {
252
+ if (0 === versions.length) return null;
253
+ return sortVersions(versions, 'desc')[0];
365
254
  }
366
- return finalVersion;
367
- }
368
- function buildFinalUrls(pkg, version, localFallback) {
369
- const urls = buildCdnUrls(pkg, version);
370
- if (localFallback) urls.push(localFallback);
371
- return urls;
372
- }
373
- async function loadRemoteMultiVersion(options, plugins) {
374
- const { name, pkg, version = 'latest', retries = 3, delay = 1000, localFallback, cacheTTL = 86400000, revalidate = true, shared: customShared } = options;
375
- const finalVersion = await resolveFinalVersion(pkg, version, cacheTTL, revalidate);
376
- const scopeName = `${name}`;
377
- const urls = buildFinalUrls(pkg, finalVersion, localFallback);
378
- const finalSharedConfig = getFinalSharedConfig(customShared);
379
- for (const url of urls)try {
380
- return await tryLoadRemote(scopeName, url, retries, delay, finalSharedConfig, plugins);
381
- } catch (e) {
382
- console.warn(`[MF] 切换 CDN 路径:${url} 失败,尝试下一个...`, e);
255
+ function getStableVersions(versions) {
256
+ return versions.filter((v)=>!v.includes('alpha') && !v.includes('beta') && !v.includes('rc'));
383
257
  }
384
- throw new Error(`[MF] 所有加载源 (${urls.length} 个) 均加载失败。`);
385
- }
386
- const preloadCache = {};
387
- const PRELOAD_CACHE_TTL = 300000;
388
- function getCachedPreload(pkg, version) {
389
- const cached = preloadCache[pkg];
390
- if (!cached) return null;
391
- if (cached.version !== version) return null;
392
- if (Date.now() - cached.timestamp > PRELOAD_CACHE_TTL) {
393
- delete preloadCache[pkg];
394
- return null;
258
+ function extractMajorVersion(version) {
259
+ return parseVersion(version).major;
395
260
  }
396
- return {
397
- scopeName: cached.scopeName,
398
- mf: cached.mf
399
- };
400
- }
401
- function setCachedPreload(pkg, version, scopeName, mf) {
402
- preloadCache[pkg] = {
403
- version,
404
- scopeName,
405
- mf,
406
- timestamp: Date.now()
261
+ function isPrerelease(version) {
262
+ const v = parseVersion(version);
263
+ return !!v.prerelease;
264
+ }
265
+ const fallbackPlugin = ()=>({
266
+ name: 'fallback-plugin',
267
+ errorLoadRemote (args) {
268
+ const fallback = 'fallback';
269
+ console.log(args, 'args');
270
+ return fallback;
271
+ }
272
+ });
273
+ const DEFAULT_CDN_TEMPLATES = [
274
+ 'https://cdn.jsdelivr.net/npm/{pkg}@{version}/dist/remoteEntry.js',
275
+ 'https://unpkg.com/{pkg}@{version}/dist/remoteEntry.js'
276
+ ];
277
+ const DEFAULT_SHARED_CONFIG = {
278
+ react: {
279
+ shareConfig: {
280
+ singleton: true,
281
+ eager: true,
282
+ requiredVersion: false
283
+ }
284
+ },
285
+ 'react-dom': {
286
+ shareConfig: {
287
+ singleton: true,
288
+ eager: true,
289
+ requiredVersion: false
290
+ }
291
+ }
407
292
  };
408
- }
409
- function executeWhenIdle(callback) {
410
- if ('undefined' != typeof requestIdleCallback) requestIdleCallback(()=>callback());
411
- else setTimeout(callback, 1);
412
- }
413
- async function preloadRemote(options) {
414
- const { pkg, version = 'latest', priority = 'idle', force = false } = options;
415
- if (!force) {
416
- const cached = getCachedPreload(pkg, version);
417
- if (cached) return cached;
293
+ async function fetchLatestVersion(pkg) {
294
+ const res = await fetch(`https://registry.npmjs.org/${pkg}`);
295
+ if (!res.ok) throw new Error(`[MF] 无法获取 ${pkg} 的版本信息,状态码:${res.status}`);
296
+ const data = await res.json();
297
+ const latest = data['dist-tags']?.latest;
298
+ if (!latest) throw new Error(`[MF] 无法从 NPM 获取 ${pkg} 的 latest tag`);
299
+ return latest;
418
300
  }
419
- const preloadTask = async ()=>{
301
+ function getVersionCache() {
420
302
  try {
421
- const { scopeName, mf } = await loadRemoteMultiVersion(options, []);
422
- setCachedPreload(pkg, version, scopeName, mf);
303
+ const cacheStr = localStorage.getItem('mf-multi-version');
304
+ return cacheStr ? JSON.parse(cacheStr) : {};
305
+ } catch (e) {
306
+ console.error('[MF Cache] 读取缓存失败:', e);
307
+ return {};
308
+ }
309
+ }
310
+ function setVersionCache(pkg, version) {
311
+ try {
312
+ const cache = getVersionCache();
313
+ cache[pkg] = cache[pkg] || {};
314
+ cache[pkg][version] = {
315
+ timestamp: Date.now()
316
+ };
317
+ localStorage.setItem('mf-multi-version', JSON.stringify(cache));
318
+ } catch (e) {
319
+ console.error('[MF Cache] 写入缓存失败:', e);
320
+ }
321
+ }
322
+ function buildCdnUrls(pkg, version) {
323
+ return DEFAULT_CDN_TEMPLATES.map((template)=>template.replace('{pkg}', pkg).replace('{version}', version));
324
+ }
325
+ async function tryLoadRemote(scopeName, url, retries, delay, sharedConfig, plugins) {
326
+ let lastError;
327
+ for(let i = 0; i < retries; i++)try {
328
+ const mf = (0, runtime_namespaceObject.createInstance)({
329
+ name: 'host',
330
+ remotes: [
331
+ {
332
+ name: scopeName,
333
+ entry: url
334
+ }
335
+ ],
336
+ shared: sharedConfig,
337
+ plugins: [
338
+ ...plugins,
339
+ fallbackPlugin()
340
+ ]
341
+ });
423
342
  return {
424
343
  scopeName,
425
344
  mf
426
345
  };
427
346
  } catch (e) {
428
- console.warn(`[MF Preload] 预加载失败 ${pkg}@${version}:`, e);
429
- return null;
347
+ lastError = e;
348
+ console.warn(`[MF] URL ${url} 加载失败,第 ${i + 1} 次重试...`);
349
+ if (i < retries - 1) await new Promise((res)=>setTimeout(res, delay));
430
350
  }
431
- };
432
- if ('high' === priority) return preloadTask();
433
- return new Promise((resolve)=>{
434
- executeWhenIdle(async ()=>{
435
- const result = await preloadTask();
436
- resolve(result);
351
+ throw new Error(`[MF] URL ${url} 经过 ${retries} 次重试仍加载失败。`, {
352
+ cause: lastError
437
353
  });
438
- });
439
- }
440
- function preloadRemoteList(optionsList, onProgress) {
441
- let loaded = 0;
442
- const total = optionsList.length;
443
- const promises = optionsList.map((options)=>preloadRemote(options).then((result)=>{
444
- loaded++;
445
- onProgress?.(loaded, total);
446
- return result;
447
- }));
448
- return Promise.all(promises);
449
- }
450
- function cancelPreload(pkg) {
451
- const cached = preloadCache[pkg];
452
- if (cached) delete preloadCache[pkg];
453
- }
454
- function clearPreloadCache() {
455
- Object.keys(preloadCache).forEach((pkg)=>delete preloadCache[pkg]);
456
- }
457
- function getPreloadStatus(pkg) {
458
- const cached = preloadCache[pkg];
459
- if (!cached) return null;
460
- return {
461
- loaded: true,
462
- timestamp: cached.timestamp
463
- };
464
- }
465
- const remoteInstances = new Map();
466
- function generateInstanceKey(name, pkg, version) {
467
- return `${name}::${pkg}@${version}`;
468
- }
469
- async function unloadRemote(options) {
470
- const { name, pkg, version = '*', clearCache = false } = options;
471
- const keysToDelete = [];
472
- remoteInstances.forEach((instance, key)=>{
473
- const versionMatch = '*' === version || instance.version === version;
474
- const pkgMatch = instance.pkg === pkg;
475
- const nameMatch = instance.name === name;
476
- if (nameMatch && pkgMatch && versionMatch) keysToDelete.push(key);
477
- });
478
- for (const key of keysToDelete){
479
- const instance = remoteInstances.get(key);
480
- if (instance) {
481
- await cleanupInstance(instance);
482
- remoteInstances.delete(key);
354
+ }
355
+ function getFinalSharedConfig(customShared) {
356
+ return {
357
+ ...DEFAULT_SHARED_CONFIG,
358
+ ...customShared || {}
359
+ };
360
+ }
361
+ async function resolveFinalVersion(pkg, version, cacheTTL, revalidate) {
362
+ let finalVersion = version;
363
+ if ('latest' === version) {
364
+ const cache = getVersionCache();
365
+ const versions = cache[pkg] || {};
366
+ const latestCached = Object.keys(versions).sort((a, b)=>versions[b].timestamp - versions[a].timestamp)[0];
367
+ if (latestCached && Date.now() - versions[latestCached].timestamp < cacheTTL) {
368
+ finalVersion = latestCached;
369
+ if (revalidate) fetchLatestVersion(pkg).then((latest)=>{
370
+ if (latest !== latestCached) {
371
+ console.log(`[MF] 发现 ${pkg} 新版本 ${latest},已更新缓存。`);
372
+ setVersionCache(pkg, latest);
373
+ }
374
+ }).catch((e)=>console.error(`[MF] 异步检查最新版本失败:`, e));
375
+ } else {
376
+ finalVersion = await fetchLatestVersion(pkg);
377
+ setVersionCache(pkg, finalVersion);
378
+ }
483
379
  }
380
+ return finalVersion;
484
381
  }
485
- if (clearCache) clearVersionCache(pkg, version);
486
- return keysToDelete.length > 0;
487
- }
488
- async function cleanupInstance(instance) {
489
- try {
490
- instance.loadedModules.clear();
491
- if (instance.mf && 'function' == typeof instance.mf.cleanup) await instance.mf.cleanup();
492
- console.log(`[MF Unload] 已卸载 ${instance.pkg}@${instance.version}`);
493
- } catch (e) {
494
- console.warn(`[MF Unload] 卸载时出错 ${instance.pkg}:`, e);
382
+ function buildFinalUrls(pkg, version, localFallback) {
383
+ const urls = buildCdnUrls(pkg, version);
384
+ if (localFallback) urls.push(localFallback);
385
+ return urls;
495
386
  }
496
- }
497
- function clearVersionCache(pkg, version) {
498
- try {
499
- const cacheKey = 'mf-multi-version';
500
- const cacheStr = localStorage.getItem(cacheKey);
501
- if (!cacheStr) return;
502
- const cache = JSON.parse(cacheStr);
503
- if (!cache[pkg]) return;
504
- if ('*' === version) delete cache[pkg];
505
- else delete cache[pkg][version];
506
- localStorage.setItem(cacheKey, JSON.stringify(cache));
507
- console.log(`[MF Unload] 已清除版本缓存 ${pkg}@${version}`);
508
- } catch (e) {
509
- console.warn('[MF Unload] 清除缓存失败:', e);
387
+ async function loadRemoteMultiVersion(options, plugins) {
388
+ const { name, pkg, version = 'latest', retries = 3, delay = 1000, localFallback, cacheTTL = 86400000, revalidate = true, shared: customShared } = options;
389
+ const finalVersion = await resolveFinalVersion(pkg, version, cacheTTL, revalidate);
390
+ const scopeName = `${name}`;
391
+ const urls = buildFinalUrls(pkg, finalVersion, localFallback);
392
+ const finalSharedConfig = getFinalSharedConfig(customShared);
393
+ for (const url of urls)try {
394
+ return await tryLoadRemote(scopeName, url, retries, delay, finalSharedConfig, plugins);
395
+ } catch (e) {
396
+ console.warn(`[MF] 切换 CDN 路径:${url} 失败,尝试下一个...`, e);
397
+ }
398
+ throw new Error(`[MF] 所有加载源 (${urls.length}) 均加载失败。`);
510
399
  }
511
- }
512
- function registerRemoteInstance(name, scopeName, pkg, version, mf) {
513
- const key = generateInstanceKey(name, pkg, version);
514
- const instance = {
515
- name,
516
- scopeName,
517
- pkg,
518
- version,
519
- mf,
520
- loadedModules: new Set(),
521
- timestamp: Date.now()
522
- };
523
- remoteInstances.set(key, instance);
524
- return key;
525
- }
526
- function registerLoadedModule(key, moduleId) {
527
- const instance = remoteInstances.get(key);
528
- if (instance) instance.loadedModules.add(moduleId);
529
- }
530
- function unloadAll(clearAllCache = false) {
531
- return new Promise((resolve)=>{
532
- const keys = Array.from(remoteInstances.keys());
533
- if (0 === keys.length) return void resolve();
534
- let completed = 0;
535
- keys.forEach(async (key)=>{
536
- const instance = remoteInstances.get(key);
537
- if (instance) {
538
- await cleanupInstance(instance);
539
- remoteInstances.delete(key);
540
- }
541
- completed++;
542
- if (completed >= keys.length) {
543
- if (clearAllCache) try {
544
- localStorage.removeItem('mf-multi-version');
545
- } catch (e) {
546
- console.warn('[MF Unload] 清除所有缓存失败:', e);
547
- }
548
- resolve();
549
- }
550
- });
551
- });
552
- }
553
- function getLoadedRemotes() {
554
- const result = [];
555
- remoteInstances.forEach((instance)=>{
556
- result.push({
557
- name: instance.name,
558
- pkg: instance.pkg,
559
- version: instance.version,
560
- loadedModules: instance.loadedModules.size,
561
- timestamp: instance.timestamp
562
- });
563
- });
564
- return result;
565
- }
566
- function isRemoteLoaded(name, pkg, version) {
567
- const key = generateInstanceKey(name, pkg, version || '*');
568
- return remoteInstances.has(key);
569
- }
570
- const CDN_URLS = [
571
- 'https://cdn.jsdelivr.net/npm/{pkg}@{version}/dist/remoteEntry.js',
572
- 'https://unpkg.com/{pkg}@{version}/dist/remoteEntry.js'
573
- ];
574
- async function checkCdnAccess(cdnUrl) {
575
- const start = performance.now();
576
- try {
577
- const controller = new AbortController();
578
- const timeout = setTimeout(()=>controller.abort(), 5000);
579
- const res = await fetch(cdnUrl, {
580
- method: 'HEAD',
581
- signal: controller.signal
582
- });
583
- clearTimeout(timeout);
584
- const latency = Math.round(performance.now() - start);
400
+ const preloadCache = {};
401
+ const PRELOAD_CACHE_TTL = 300000;
402
+ function getCachedPreload(pkg, version) {
403
+ const cached = preloadCache[pkg];
404
+ if (!cached) return null;
405
+ if (cached.version !== version) return null;
406
+ if (Date.now() - cached.timestamp > PRELOAD_CACHE_TTL) {
407
+ delete preloadCache[pkg];
408
+ return null;
409
+ }
585
410
  return {
586
- reachable: res.ok,
587
- latency
411
+ scopeName: cached.scopeName,
412
+ mf: cached.mf
588
413
  };
589
- } catch (e) {
590
- const latency = Math.round(performance.now() - start);
591
- return {
592
- reachable: false,
593
- latency
414
+ }
415
+ function setCachedPreload(pkg, version, scopeName, mf) {
416
+ preloadCache[pkg] = {
417
+ version,
418
+ scopeName,
419
+ mf,
420
+ timestamp: Date.now()
594
421
  };
595
422
  }
596
- }
597
- async function health_fetchLatestVersion(pkg) {
598
- try {
599
- const res = await fetch(`https://registry.npmjs.org/${pkg}`);
600
- if (!res.ok) return null;
601
- const data = await res.json();
602
- return data['dist-tags']?.latest || null;
603
- } catch {
604
- return null;
423
+ function executeWhenIdle(callback) {
424
+ if ('undefined' != typeof requestIdleCallback) requestIdleCallback(()=>callback());
425
+ else setTimeout(callback, 1);
605
426
  }
606
- }
607
- async function checkRemoteHealth(options) {
608
- const { pkg, version = 'latest' } = options;
609
- const actualVersion = 'latest' === version ? await health_fetchLatestVersion(pkg) || version : version;
610
- const results = [];
611
- for (const template of CDN_URLS){
612
- const url = template.replace('{pkg}', pkg).replace('{version}', actualVersion);
613
- const result = await checkCdnAccess(url);
614
- results.push({
615
- cdn: url,
616
- ...result
427
+ async function preloadRemote(options) {
428
+ const { pkg, version = 'latest', priority = 'idle', force = false } = options;
429
+ if (!force) {
430
+ const cached = getCachedPreload(pkg, version);
431
+ if (cached) return cached;
432
+ }
433
+ const preloadTask = async ()=>{
434
+ try {
435
+ const { scopeName, mf } = await loadRemoteMultiVersion(options, []);
436
+ setCachedPreload(pkg, version, scopeName, mf);
437
+ return {
438
+ scopeName,
439
+ mf
440
+ };
441
+ } catch (e) {
442
+ console.warn(`[MF Preload] 预加载失败 ${pkg}@${version}:`, e);
443
+ return null;
444
+ }
445
+ };
446
+ if ('high' === priority) return preloadTask();
447
+ return new Promise((resolve)=>{
448
+ executeWhenIdle(async ()=>{
449
+ const result = await preloadTask();
450
+ resolve(result);
451
+ });
617
452
  });
618
453
  }
619
- const workingCdn = results.find((r)=>r.reachable);
620
- const bestCdn = results.sort((a, b)=>a.latency - b.latency)[0];
621
- let status = 'unhealthy';
622
- if (workingCdn) status = bestCdn.latency < 1000 ? 'healthy' : 'degraded';
623
- return {
624
- pkg,
625
- version: actualVersion,
626
- status,
627
- latency: bestCdn?.latency || 0,
628
- cdn: bestCdn?.cdn || '',
629
- details: {
630
- cdnReachable: !!workingCdn,
631
- remoteEntryValid: false,
632
- modulesLoadable: false
633
- }
634
- };
635
- }
636
- async function checkModuleLoadable(scopeName, modulePath, mf) {
637
- try {
638
- if (!mf || 'function' != typeof mf.loadRemote) return false;
639
- const mod = await mf.loadRemote(`${scopeName}/${modulePath}`);
640
- return null != mod;
641
- } catch {
642
- return false;
454
+ function preloadRemoteList(optionsList, onProgress) {
455
+ let loaded = 0;
456
+ const total = optionsList.length;
457
+ const promises = optionsList.map((options)=>preloadRemote(options).then((result)=>{
458
+ loaded++;
459
+ onProgress?.(loaded, total);
460
+ return result;
461
+ }));
462
+ return Promise.all(promises);
643
463
  }
644
- }
645
- async function getRemoteHealthReport(remotes) {
646
- const results = await Promise.all(remotes.map((r)=>checkRemoteHealth(r)));
647
- let overall = 'healthy';
648
- if (results.some((r)=>'unhealthy' === r.status)) overall = 'unhealthy';
649
- else if (results.some((r)=>'degraded' === r.status)) overall = 'degraded';
650
- return {
651
- timestamp: Date.now(),
652
- overall,
653
- remotes: results
654
- };
655
- }
656
- function formatHealthStatus(status) {
657
- const icons = {
658
- healthy: '🟢',
659
- degraded: '🟡',
660
- unhealthy: '🔴'
661
- };
662
- return `${icons[status]} ${status}`;
663
- }
664
- class EventBusClass {
665
- listeners = new Map();
666
- eventHistory = new Map();
667
- maxHistorySize = 100;
668
- generateId() {
669
- return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
464
+ function cancelPreload(pkg) {
465
+ const cached = preloadCache[pkg];
466
+ if (cached) delete preloadCache[pkg];
670
467
  }
671
- on(event, callback, options) {
672
- const onceValue = options?.once ?? false;
673
- if (!this.listeners.has(event)) this.listeners.set(event, new Set());
674
- const subscription = {
675
- callback,
676
- once: onceValue,
677
- filter: options?.filter
678
- };
679
- this.listeners.get(event).add(subscription);
680
- return ()=>{
681
- this.off(event, callback);
468
+ function clearPreloadCache() {
469
+ Object.keys(preloadCache).forEach((pkg)=>delete preloadCache[pkg]);
470
+ }
471
+ function getPreloadStatus(pkg) {
472
+ const cached = preloadCache[pkg];
473
+ if (!cached) return null;
474
+ return {
475
+ loaded: true,
476
+ timestamp: cached.timestamp
682
477
  };
683
478
  }
684
- once(event, callback) {
685
- return this.on(event, callback, {
686
- once: true
479
+ const remoteInstances = new Map();
480
+ function generateInstanceKey(name, pkg, version) {
481
+ return `${name}::${pkg}@${version}`;
482
+ }
483
+ async function unloadRemote(options) {
484
+ const { name, pkg, version = '*', clearCache = false } = options;
485
+ const keysToDelete = [];
486
+ remoteInstances.forEach((instance, key)=>{
487
+ const versionMatch = '*' === version || instance.version === version;
488
+ const pkgMatch = instance.pkg === pkg;
489
+ const nameMatch = instance.name === name;
490
+ if (nameMatch && pkgMatch && versionMatch) keysToDelete.push(key);
687
491
  });
492
+ for (const key of keysToDelete){
493
+ const instance = remoteInstances.get(key);
494
+ if (instance) {
495
+ await cleanupInstance(instance);
496
+ remoteInstances.delete(key);
497
+ }
498
+ }
499
+ if (clearCache) clearVersionCache(pkg, version);
500
+ return keysToDelete.length > 0;
688
501
  }
689
- off(event, callback) {
690
- if (!this.listeners.has(event)) return;
691
- if (callback) {
692
- const subscriptions = this.listeners.get(event);
693
- subscriptions.forEach((sub)=>{
694
- if (sub.callback === callback) subscriptions.delete(sub);
695
- });
696
- } else this.listeners.delete(event);
502
+ async function cleanupInstance(instance) {
503
+ try {
504
+ instance.loadedModules.clear();
505
+ if (instance.mf && 'function' == typeof instance.mf.cleanup) await instance.mf.cleanup();
506
+ console.log(`[MF Unload] 已卸载 ${instance.pkg}@${instance.version}`);
507
+ } catch (e) {
508
+ console.warn(`[MF Unload] 卸载时出错 ${instance.pkg}:`, e);
509
+ }
697
510
  }
698
- emit(event, data, meta) {
699
- const eventMeta = {
700
- timestamp: Date.now(),
701
- source: meta?.source,
702
- id: meta?.id || this.generateId()
511
+ function clearVersionCache(pkg, version) {
512
+ try {
513
+ const cacheKey = 'mf-multi-version';
514
+ const cacheStr = localStorage.getItem(cacheKey);
515
+ if (!cacheStr) return;
516
+ const cache = JSON.parse(cacheStr);
517
+ if (!cache[pkg]) return;
518
+ if ('*' === version) delete cache[pkg];
519
+ else delete cache[pkg][version];
520
+ localStorage.setItem(cacheKey, JSON.stringify(cache));
521
+ console.log(`[MF Unload] 已清除版本缓存 ${pkg}@${version}`);
522
+ } catch (e) {
523
+ console.warn('[MF Unload] 清除缓存失败:', e);
524
+ }
525
+ }
526
+ function registerRemoteInstance(name, scopeName, pkg, version, mf) {
527
+ const key = generateInstanceKey(name, pkg, version);
528
+ const instance = {
529
+ name,
530
+ scopeName,
531
+ pkg,
532
+ version,
533
+ mf,
534
+ loadedModules: new Set(),
535
+ timestamp: Date.now()
703
536
  };
704
- this.addToHistory(event, data, eventMeta);
705
- const subscriptions = this.listeners.get(event);
706
- if (!subscriptions) return;
707
- const toRemove = [];
708
- subscriptions.forEach((sub)=>{
709
- if (sub.filter && !sub.filter(data, eventMeta)) return;
710
- try {
711
- sub.callback(data, eventMeta);
712
- } catch (e) {
713
- console.error(`[MF EventBus] 事件 ${event} 处理出错:`, e);
714
- }
715
- if (sub.once) toRemove.push(sub);
537
+ remoteInstances.set(key, instance);
538
+ return key;
539
+ }
540
+ function registerLoadedModule(key, moduleId) {
541
+ const instance = remoteInstances.get(key);
542
+ if (instance) instance.loadedModules.add(moduleId);
543
+ }
544
+ function unloadAll(clearAllCache = false) {
545
+ return new Promise((resolve)=>{
546
+ const keys = Array.from(remoteInstances.keys());
547
+ if (0 === keys.length) return void resolve();
548
+ let completed = 0;
549
+ keys.forEach(async (key)=>{
550
+ const instance = remoteInstances.get(key);
551
+ if (instance) {
552
+ await cleanupInstance(instance);
553
+ remoteInstances.delete(key);
554
+ }
555
+ completed++;
556
+ if (completed >= keys.length) {
557
+ if (clearAllCache) try {
558
+ localStorage.removeItem('mf-multi-version');
559
+ } catch (e) {
560
+ console.warn('[MF Unload] 清除所有缓存失败:', e);
561
+ }
562
+ resolve();
563
+ }
564
+ });
716
565
  });
717
- toRemove.forEach((sub)=>subscriptions.delete(sub));
718
566
  }
719
- addToHistory(event, data, meta) {
720
- if (!this.eventHistory.has(event)) this.eventHistory.set(event, []);
721
- const history = this.eventHistory.get(event);
722
- history.push({
723
- data,
724
- meta
567
+ function getLoadedRemotes() {
568
+ const result = [];
569
+ remoteInstances.forEach((instance)=>{
570
+ result.push({
571
+ name: instance.name,
572
+ pkg: instance.pkg,
573
+ version: instance.version,
574
+ loadedModules: instance.loadedModules.size,
575
+ timestamp: instance.timestamp
576
+ });
725
577
  });
726
- if (history.length > this.maxHistorySize) history.shift();
578
+ return result;
727
579
  }
728
- getHistory(event) {
729
- return this.eventHistory.get(event) || [];
580
+ function isRemoteLoaded(name, pkg, version) {
581
+ const key = generateInstanceKey(name, pkg, version || '*');
582
+ return remoteInstances.has(key);
730
583
  }
731
- getEvents() {
732
- return Array.from(this.listeners.keys());
584
+ const CDN_URLS = [
585
+ 'https://cdn.jsdelivr.net/npm/{pkg}@{version}/dist/remoteEntry.js',
586
+ 'https://unpkg.com/{pkg}@{version}/dist/remoteEntry.js'
587
+ ];
588
+ async function checkCdnAccess(cdnUrl) {
589
+ const start = performance.now();
590
+ try {
591
+ const controller = new AbortController();
592
+ const timeout = setTimeout(()=>controller.abort(), 5000);
593
+ const res = await fetch(cdnUrl, {
594
+ method: 'HEAD',
595
+ signal: controller.signal
596
+ });
597
+ clearTimeout(timeout);
598
+ const latency = Math.round(performance.now() - start);
599
+ return {
600
+ reachable: res.ok,
601
+ latency
602
+ };
603
+ } catch (e) {
604
+ const latency = Math.round(performance.now() - start);
605
+ return {
606
+ reachable: false,
607
+ latency
608
+ };
609
+ }
733
610
  }
734
- getListenerCount(event) {
735
- return this.listeners.get(event)?.size || 0;
611
+ async function health_fetchLatestVersion(pkg) {
612
+ try {
613
+ const res = await fetch(`https://registry.npmjs.org/${pkg}`);
614
+ if (!res.ok) return null;
615
+ const data = await res.json();
616
+ return data['dist-tags']?.latest || null;
617
+ } catch {
618
+ return null;
619
+ }
736
620
  }
737
- hasListeners(event) {
738
- return this.listeners.has(event) && this.listeners.get(event).size > 0;
621
+ async function checkRemoteHealth(options) {
622
+ const { pkg, version = 'latest' } = options;
623
+ const actualVersion = 'latest' === version ? await health_fetchLatestVersion(pkg) || version : version;
624
+ const results = [];
625
+ for (const template of CDN_URLS){
626
+ const url = template.replace('{pkg}', pkg).replace('{version}', actualVersion);
627
+ const result = await checkCdnAccess(url);
628
+ results.push({
629
+ cdn: url,
630
+ ...result
631
+ });
632
+ }
633
+ const workingCdn = results.find((r)=>r.reachable);
634
+ const bestCdn = results.sort((a, b)=>a.latency - b.latency)[0];
635
+ let status = 'unhealthy';
636
+ if (workingCdn) status = bestCdn.latency < 1000 ? 'healthy' : 'degraded';
637
+ return {
638
+ pkg,
639
+ version: actualVersion,
640
+ status,
641
+ latency: bestCdn?.latency || 0,
642
+ cdn: bestCdn?.cdn || '',
643
+ details: {
644
+ cdnReachable: !!workingCdn,
645
+ remoteEntryValid: false,
646
+ modulesLoadable: false
647
+ }
648
+ };
739
649
  }
740
- clear(event) {
741
- if (event) {
742
- this.listeners.delete(event);
743
- this.eventHistory.delete(event);
744
- } else {
745
- this.listeners.clear();
746
- this.eventHistory.clear();
650
+ async function checkModuleLoadable(scopeName, modulePath, mf) {
651
+ try {
652
+ if (!mf || 'function' != typeof mf.loadRemote) return false;
653
+ const mod = await mf.loadRemote(`${scopeName}/${modulePath}`);
654
+ return null != mod;
655
+ } catch {
656
+ return false;
747
657
  }
748
658
  }
749
- listenerExists(event, callback) {
750
- const subscriptions = this.listeners.get(event);
751
- if (!subscriptions) return false;
752
- let exists = false;
753
- subscriptions.forEach((sub)=>{
754
- if (sub.callback === callback) exists = true;
755
- });
756
- return exists;
659
+ async function getRemoteHealthReport(remotes) {
660
+ const results = await Promise.all(remotes.map((r)=>checkRemoteHealth(r)));
661
+ let overall = 'healthy';
662
+ if (results.some((r)=>'unhealthy' === r.status)) overall = 'unhealthy';
663
+ else if (results.some((r)=>'degraded' === r.status)) overall = 'degraded';
664
+ return {
665
+ timestamp: Date.now(),
666
+ overall,
667
+ remotes: results
668
+ };
757
669
  }
758
- emitAsync(event, data, meta) {
759
- return new Promise((resolve)=>{
760
- this.emit(event, data, meta);
761
- resolve();
762
- });
670
+ function formatHealthStatus(status) {
671
+ const icons = {
672
+ healthy: '🟢',
673
+ degraded: '🟡',
674
+ unhealthy: '🔴'
675
+ };
676
+ return `${icons[status]} ${status}`;
763
677
  }
764
- static create() {
765
- return new EventBusClass();
678
+ class EventBusClass {
679
+ listeners = new Map();
680
+ eventHistory = new Map();
681
+ maxHistorySize = 100;
682
+ generateId() {
683
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
684
+ }
685
+ on(event, callback, options) {
686
+ const onceValue = options?.once ?? false;
687
+ if (!this.listeners.has(event)) this.listeners.set(event, new Set());
688
+ const subscription = {
689
+ callback,
690
+ once: onceValue,
691
+ filter: options?.filter
692
+ };
693
+ this.listeners.get(event).add(subscription);
694
+ return ()=>{
695
+ this.off(event, callback);
696
+ };
697
+ }
698
+ once(event, callback) {
699
+ return this.on(event, callback, {
700
+ once: true
701
+ });
702
+ }
703
+ off(event, callback) {
704
+ if (!this.listeners.has(event)) return;
705
+ if (callback) {
706
+ const subscriptions = this.listeners.get(event);
707
+ subscriptions.forEach((sub)=>{
708
+ if (sub.callback === callback) subscriptions.delete(sub);
709
+ });
710
+ } else this.listeners.delete(event);
711
+ }
712
+ emit(event, data, meta) {
713
+ const eventMeta = {
714
+ timestamp: Date.now(),
715
+ source: meta?.source,
716
+ id: meta?.id || this.generateId()
717
+ };
718
+ this.addToHistory(event, data, eventMeta);
719
+ const subscriptions = this.listeners.get(event);
720
+ if (!subscriptions) return;
721
+ const toRemove = [];
722
+ subscriptions.forEach((sub)=>{
723
+ if (sub.filter && !sub.filter(data, eventMeta)) return;
724
+ try {
725
+ sub.callback(data, eventMeta);
726
+ } catch (e) {
727
+ console.error(`[MF EventBus] 事件 ${event} 处理出错:`, e);
728
+ }
729
+ if (sub.once) toRemove.push(sub);
730
+ });
731
+ toRemove.forEach((sub)=>subscriptions.delete(sub));
732
+ }
733
+ addToHistory(event, data, meta) {
734
+ if (!this.eventHistory.has(event)) this.eventHistory.set(event, []);
735
+ const history = this.eventHistory.get(event);
736
+ history.push({
737
+ data,
738
+ meta
739
+ });
740
+ if (history.length > this.maxHistorySize) history.shift();
741
+ }
742
+ getHistory(event) {
743
+ return this.eventHistory.get(event) || [];
744
+ }
745
+ getEvents() {
746
+ return Array.from(this.listeners.keys());
747
+ }
748
+ getListenerCount(event) {
749
+ return this.listeners.get(event)?.size || 0;
750
+ }
751
+ hasListeners(event) {
752
+ return this.listeners.has(event) && this.listeners.get(event).size > 0;
753
+ }
754
+ clear(event) {
755
+ if (event) {
756
+ this.listeners.delete(event);
757
+ this.eventHistory.delete(event);
758
+ } else {
759
+ this.listeners.clear();
760
+ this.eventHistory.clear();
761
+ }
762
+ }
763
+ listenerExists(event, callback) {
764
+ const subscriptions = this.listeners.get(event);
765
+ if (!subscriptions) return false;
766
+ let exists = false;
767
+ subscriptions.forEach((sub)=>{
768
+ if (sub.callback === callback) exists = true;
769
+ });
770
+ return exists;
771
+ }
772
+ emitAsync(event, data, meta) {
773
+ return new Promise((resolve)=>{
774
+ this.emit(event, data, meta);
775
+ resolve();
776
+ });
777
+ }
778
+ static create() {
779
+ return new EventBusClass();
780
+ }
766
781
  }
767
- }
768
- const eventBus = EventBusClass.create();
769
- function createEventBus() {
770
- return EventBusClass.create();
771
- }
772
- const external_react_namespaceObject = require("react");
773
- var external_react_default = /*#__PURE__*/ __webpack_require__.n(external_react_namespaceObject);
774
- class ErrorBoundary extends external_react_default().Component {
775
- constructor(props){
776
- super(props);
777
- this.state = {
778
- hasError: false,
779
- error: null,
780
- errorInfo: null
781
- };
782
+ const eventBus = EventBusClass.create();
783
+ function createEventBus() {
784
+ return EventBusClass.create();
782
785
  }
783
- static getDerivedStateFromError(error) {
784
- return {
785
- hasError: true,
786
- error,
787
- errorInfo: null
786
+ const external_react_namespaceObject = require("react");
787
+ var external_react_default = /*#__PURE__*/ __webpack_require__.n(external_react_namespaceObject);
788
+ class ErrorBoundary extends external_react_default().Component {
789
+ constructor(props){
790
+ super(props);
791
+ this.state = {
792
+ hasError: false,
793
+ error: null,
794
+ errorInfo: null
795
+ };
796
+ }
797
+ static getDerivedStateFromError(error) {
798
+ return {
799
+ hasError: true,
800
+ error,
801
+ errorInfo: null
802
+ };
803
+ }
804
+ componentDidCatch(error, errorInfo) {
805
+ this.setState({
806
+ errorInfo
807
+ });
808
+ this.props.onError?.(error, errorInfo);
809
+ console.error('[RemoteReloadUtils] ErrorBoundary caught error:', error, errorInfo);
810
+ }
811
+ handleReset = ()=>{
812
+ this.setState({
813
+ hasError: false,
814
+ error: null,
815
+ errorInfo: null
816
+ });
817
+ this.props.onReset?.();
788
818
  };
819
+ render() {
820
+ if (this.state.hasError && this.state.error) {
821
+ if ('function' == typeof this.props.fallback) return this.props.fallback(this.state.error, this.handleReset);
822
+ if (void 0 !== this.props.fallback) return this.props.fallback;
823
+ return /*#__PURE__*/ external_react_default().createElement("div", {
824
+ role: "alert",
825
+ className: "p-4 border border-red-200 bg-red-50 rounded-lg"
826
+ }, /*#__PURE__*/ external_react_default().createElement("h3", {
827
+ className: "text-lg font-semibold mb-2"
828
+ }, "Something went wrong"), /*#__PURE__*/ external_react_default().createElement("p", {
829
+ className: "text-red-700"
830
+ }, this.state.error.message), /*#__PURE__*/ external_react_default().createElement("button", {
831
+ onClick: this.handleReset,
832
+ className: "mt-3 px-4 py-2 text-sm font-medium text-white bg-red-600 rounded hover:bg-red-700 cursor-pointer transition-colors"
833
+ }, "Try again"));
834
+ }
835
+ return this.props.children;
836
+ }
789
837
  }
790
- componentDidCatch(error, errorInfo) {
791
- this.setState({
792
- errorInfo
838
+ function useRemoteModule({ pkg, version, moduleName, scopeName, onError, onLoad, retryKey = 0 }) {
839
+ const [moduleState, setModuleState] = (0, external_react_namespaceObject.useState)({
840
+ loading: true,
841
+ error: null,
842
+ component: null
793
843
  });
794
- this.props.onError?.(error, errorInfo);
795
- console.error('[RemoteReloadUtils] ErrorBoundary caught error:', error, errorInfo);
844
+ (0, external_react_namespaceObject.useEffect)(()=>{
845
+ let mounted = true;
846
+ async function loadModule() {
847
+ try {
848
+ setModuleState((prev)=>({
849
+ ...prev,
850
+ loading: true,
851
+ error: null
852
+ }));
853
+ const { mf } = await loadRemoteMultiVersion({
854
+ name: scopeName,
855
+ pkg,
856
+ version
857
+ }, []);
858
+ if (!mf || !mounted) return;
859
+ const mod = await mf.loadRemote(`${scopeName}/${moduleName}`);
860
+ if (!mounted) return;
861
+ if (mod && 'object' == typeof mod && 'default' in mod) {
862
+ const Component = mod.default;
863
+ setModuleState({
864
+ loading: false,
865
+ error: null,
866
+ component: Component
867
+ });
868
+ onLoad?.(Component);
869
+ } else throw new Error(`Module "${scopeName}/${moduleName}" does not export a default component`);
870
+ } catch (err) {
871
+ if (mounted) {
872
+ const error = err instanceof Error ? err : new Error(String(err));
873
+ setModuleState({
874
+ loading: false,
875
+ error,
876
+ component: null
877
+ });
878
+ onError?.(error);
879
+ }
880
+ }
881
+ }
882
+ loadModule();
883
+ return ()=>{
884
+ mounted = false;
885
+ };
886
+ }, [
887
+ pkg,
888
+ version,
889
+ moduleName,
890
+ scopeName,
891
+ onError,
892
+ onLoad,
893
+ retryKey
894
+ ]);
895
+ return moduleState;
796
896
  }
797
- handleReset = ()=>{
798
- this.setState({
799
- hasError: false,
800
- error: null,
801
- errorInfo: null
897
+ function RemoteModuleRenderer({ pkg, version, moduleName, scopeName, loadingFallback, errorFallback, componentProps, className, style, onError, onLoad }) {
898
+ const moduleState = useRemoteModule({
899
+ pkg,
900
+ version,
901
+ moduleName,
902
+ scopeName,
903
+ onError,
904
+ onLoad
802
905
  });
803
- this.props.onReset?.();
804
- };
805
- render() {
806
- if (this.state.hasError && this.state.error) {
807
- if ('function' == typeof this.props.fallback) return this.props.fallback(this.state.error, this.handleReset);
808
- if (void 0 !== this.props.fallback) return this.props.fallback;
906
+ const [retryKey, setRetryKey] = (0, external_react_namespaceObject.useState)(0);
907
+ const handleRetry = (0, external_react_namespaceObject.useCallback)(()=>{
908
+ setRetryKey((prev)=>prev + 1);
909
+ }, []);
910
+ (0, external_react_namespaceObject.useEffect)(()=>{}, [
911
+ retryKey
912
+ ]);
913
+ if (moduleState.loading) return /*#__PURE__*/ external_react_default().createElement("div", {
914
+ className: className,
915
+ style: style,
916
+ role: "status",
917
+ "aria-live": "polite"
918
+ }, loadingFallback || /*#__PURE__*/ external_react_default().createElement("div", {
919
+ className: "module-card module-card--loading"
920
+ }, /*#__PURE__*/ external_react_default().createElement("div", {
921
+ className: "loading-spinner",
922
+ "aria-hidden": "true"
923
+ }), /*#__PURE__*/ external_react_default().createElement("span", {
924
+ className: "text-gray-600"
925
+ }, "Loading ", moduleName, "...")));
926
+ if (moduleState.error) {
927
+ if ('function' == typeof errorFallback) return /*#__PURE__*/ external_react_default().createElement(external_react_default().Fragment, null, errorFallback(moduleState.error, handleRetry));
928
+ if (void 0 !== errorFallback) return /*#__PURE__*/ external_react_default().createElement(external_react_default().Fragment, null, errorFallback);
809
929
  return /*#__PURE__*/ external_react_default().createElement("div", {
810
- role: "alert",
811
- style: {
812
- padding: '16px',
813
- border: '1px solid #ffcccc',
814
- backgroundColor: '#fff5f5',
815
- borderRadius: '4px'
816
- }
817
- }, /*#__PURE__*/ external_react_default().createElement("h3", null, "Something went wrong"), /*#__PURE__*/ external_react_default().createElement("p", null, this.state.error.message), /*#__PURE__*/ external_react_default().createElement("button", {
818
- onClick: this.handleReset,
819
- style: {
820
- marginTop: '8px',
821
- padding: '8px 16px',
822
- cursor: 'pointer'
823
- }
824
- }, "Try again"));
930
+ className: className,
931
+ style: style,
932
+ role: "alert"
933
+ }, /*#__PURE__*/ external_react_default().createElement("div", {
934
+ className: "module-card module-card--error"
935
+ }, /*#__PURE__*/ external_react_default().createElement("span", {
936
+ className: "error-icon",
937
+ "aria-hidden": "true"
938
+ }, "!"), /*#__PURE__*/ external_react_default().createElement("span", null, "Failed to load ", moduleName), /*#__PURE__*/ external_react_default().createElement("p", {
939
+ className: "error-message"
940
+ }, moduleState.error.message), /*#__PURE__*/ external_react_default().createElement("button", {
941
+ onClick: handleRetry,
942
+ className: "retry-button",
943
+ type: "button"
944
+ }, "Retry")));
825
945
  }
826
- return this.props.children;
946
+ if (!moduleState.component) return null;
947
+ const Component = moduleState.component;
948
+ return /*#__PURE__*/ external_react_default().createElement("div", {
949
+ className: className,
950
+ style: style
951
+ }, /*#__PURE__*/ external_react_default().createElement(Component, componentProps));
827
952
  }
828
- }
829
- function useRemoteModule({ pkg, version, moduleName, scopeName, onError, onLoad, retryKey = 0 }) {
830
- const [moduleState, setModuleState] = (0, external_react_namespaceObject.useState)({
831
- loading: true,
832
- error: null,
833
- component: null
834
- });
835
- (0, external_react_namespaceObject.useEffect)(()=>{
836
- let mounted = true;
837
- async function loadModule() {
953
+ function RemoteModuleProvider(props) {
954
+ const { disableErrorBoundary, errorFallback, loadingFallback, errorBoundaryOptions } = props;
955
+ if (disableErrorBoundary) return /*#__PURE__*/ external_react_default().createElement(external_react_namespaceObject.Suspense, {
956
+ fallback: loadingFallback || /*#__PURE__*/ external_react_default().createElement("div", null, "Loading...")
957
+ }, /*#__PURE__*/ external_react_default().createElement(RemoteModuleRenderer, props));
958
+ return /*#__PURE__*/ external_react_default().createElement(ErrorBoundary, {
959
+ fallback: errorFallback,
960
+ onError: errorBoundaryOptions?.onError,
961
+ onReset: errorBoundaryOptions?.onReset
962
+ }, /*#__PURE__*/ external_react_default().createElement(external_react_namespaceObject.Suspense, {
963
+ fallback: loadingFallback || /*#__PURE__*/ external_react_default().createElement("div", null, "Loading...")
964
+ }, /*#__PURE__*/ external_react_default().createElement(RemoteModuleRenderer, props)));
965
+ }
966
+ function lazyRemote(options) {
967
+ const { pkg, version = 'latest', moduleName, scopeName, maxRetries = 3, retryDelay = 1000 } = options;
968
+ let retryCount = 0;
969
+ const loadComponent = async ()=>{
838
970
  try {
839
- setModuleState((prev)=>({
840
- ...prev,
841
- loading: true,
842
- error: null
843
- }));
844
971
  const { mf } = await loadRemoteMultiVersion({
845
972
  name: scopeName,
846
973
  pkg,
847
974
  version
848
975
  }, []);
849
- if (!mf || !mounted) return;
976
+ if (!mf) throw new Error(`[RemoteReloadUtils] Failed to get Module Federation instance for ${scopeName}`);
850
977
  const mod = await mf.loadRemote(`${scopeName}/${moduleName}`);
851
- if (!mounted) return;
852
- if (mod && 'object' == typeof mod && 'default' in mod) {
853
- const Component = mod.default;
854
- setModuleState({
855
- loading: false,
856
- error: null,
857
- component: Component
858
- });
859
- onLoad?.(Component);
860
- } else throw new Error(`Module "${scopeName}/${moduleName}" does not export a default component`);
861
- } catch (err) {
862
- if (mounted) {
863
- const error = err instanceof Error ? err : new Error(String(err));
864
- setModuleState({
865
- loading: false,
866
- error,
867
- component: null
868
- });
869
- onError?.(error);
978
+ if (!mod || 'object' != typeof mod || !('default' in mod)) throw new Error(`[RemoteReloadUtils] Module "${scopeName}/${moduleName}" does not export a default component`);
979
+ return {
980
+ default: mod.default
981
+ };
982
+ } catch (error) {
983
+ if (retryCount < maxRetries) {
984
+ retryCount++;
985
+ await new Promise((resolve)=>setTimeout(resolve, retryDelay * retryCount));
986
+ return loadComponent();
870
987
  }
988
+ throw error;
871
989
  }
872
- }
873
- loadModule();
874
- return ()=>{
875
- mounted = false;
876
990
  };
877
- }, [
878
- pkg,
879
- version,
880
- moduleName,
881
- scopeName,
882
- onError,
883
- onLoad,
884
- retryKey
885
- ]);
886
- return moduleState;
887
- }
888
- function RemoteModuleRenderer({ pkg, version, moduleName, scopeName, loadingFallback, errorFallback, componentProps, className, style, onError, onLoad }) {
889
- const moduleState = useRemoteModule({
890
- pkg,
891
- version,
892
- moduleName,
893
- scopeName,
894
- onError,
895
- onLoad
896
- });
897
- const [retryKey, setRetryKey] = (0, external_react_namespaceObject.useState)(0);
898
- const handleRetry = (0, external_react_namespaceObject.useCallback)(()=>{
899
- setRetryKey((prev)=>prev + 1);
900
- }, []);
901
- (0, external_react_namespaceObject.useEffect)(()=>{}, [
902
- retryKey
903
- ]);
904
- if (moduleState.loading) return /*#__PURE__*/ external_react_default().createElement("div", {
905
- className: className,
906
- style: style,
907
- role: "status",
908
- "aria-live": "polite"
909
- }, loadingFallback || /*#__PURE__*/ external_react_default().createElement("div", {
910
- className: "module-card module-card--loading"
911
- }, /*#__PURE__*/ external_react_default().createElement("div", {
912
- className: "loading-spinner",
913
- "aria-hidden": "true"
914
- }), /*#__PURE__*/ external_react_default().createElement("span", null, "Loading ", moduleName, "...")));
915
- if (moduleState.error) {
916
- if ('function' == typeof errorFallback) return /*#__PURE__*/ external_react_default().createElement(external_react_default().Fragment, null, errorFallback(moduleState.error, handleRetry));
917
- if (void 0 !== errorFallback) return /*#__PURE__*/ external_react_default().createElement(external_react_default().Fragment, null, errorFallback);
918
- return /*#__PURE__*/ external_react_default().createElement("div", {
919
- className: className,
920
- style: style,
921
- role: "alert"
922
- }, /*#__PURE__*/ external_react_default().createElement("div", {
923
- className: "module-card module-card--error"
924
- }, /*#__PURE__*/ external_react_default().createElement("span", {
925
- className: "error-icon",
926
- "aria-hidden": "true"
927
- }, "!"), /*#__PURE__*/ external_react_default().createElement("span", null, "Failed to load ", moduleName), /*#__PURE__*/ external_react_default().createElement("p", {
928
- className: "error-message"
929
- }, moduleState.error.message), /*#__PURE__*/ external_react_default().createElement("button", {
930
- onClick: handleRetry,
931
- className: "retry-button",
932
- type: "button"
933
- }, "Retry")));
991
+ return /*#__PURE__*/ (0, external_react_namespaceObject.lazy)(loadComponent);
934
992
  }
935
- if (!moduleState.component) return null;
936
- const Component = moduleState.component;
937
- return /*#__PURE__*/ external_react_default().createElement("div", {
938
- className: className,
939
- style: style
940
- }, /*#__PURE__*/ external_react_default().createElement(Component, componentProps));
941
- }
942
- function RemoteModuleProvider(props) {
943
- const { disableErrorBoundary, errorFallback, loadingFallback, errorBoundaryOptions } = props;
944
- if (disableErrorBoundary) return /*#__PURE__*/ external_react_default().createElement(external_react_namespaceObject.Suspense, {
945
- fallback: loadingFallback || /*#__PURE__*/ external_react_default().createElement("div", null, "Loading...")
946
- }, /*#__PURE__*/ external_react_default().createElement(RemoteModuleRenderer, props));
947
- return /*#__PURE__*/ external_react_default().createElement(ErrorBoundary, {
948
- fallback: errorFallback,
949
- onError: errorBoundaryOptions?.onError,
950
- onReset: errorBoundaryOptions?.onReset
951
- }, /*#__PURE__*/ external_react_default().createElement(external_react_namespaceObject.Suspense, {
952
- fallback: loadingFallback || /*#__PURE__*/ external_react_default().createElement("div", null, "Loading...")
953
- }, /*#__PURE__*/ external_react_default().createElement(RemoteModuleRenderer, props)));
954
- }
955
- function lazyRemote(options) {
956
- const { pkg, version = 'latest', moduleName, scopeName, maxRetries = 3, retryDelay = 1000 } = options;
957
- let retryCount = 0;
958
- const loadComponent = async ()=>{
959
- try {
960
- const { mf } = await loadRemoteMultiVersion({
961
- name: scopeName,
962
- pkg,
963
- version
964
- }, []);
965
- if (!mf) throw new Error(`[RemoteReloadUtils] Failed to get Module Federation instance for ${scopeName}`);
966
- const mod = await mf.loadRemote(`${scopeName}/${moduleName}`);
967
- if (!mod || 'object' != typeof mod || !('default' in mod)) throw new Error(`[RemoteReloadUtils] Module "${scopeName}/${moduleName}" does not export a default component`);
968
- return {
969
- default: mod.default
970
- };
971
- } catch (error) {
972
- if (retryCount < maxRetries) {
973
- retryCount++;
974
- await new Promise((resolve)=>setTimeout(resolve, retryDelay * retryCount));
975
- return loadComponent();
976
- }
977
- throw error;
978
- }
979
- };
980
- return /*#__PURE__*/ (0, external_react_namespaceObject.lazy)(loadComponent);
981
- }
982
- function SuspenseRemote({ fallback, children }) {
983
- return /*#__PURE__*/ external_react_default().createElement(external_react_namespaceObject.Suspense, {
984
- fallback: fallback || /*#__PURE__*/ external_react_default().createElement("div", null, "Loading...")
985
- }, children);
986
- }
987
- function SuspenseRemoteLoader({ pkg, version = 'latest', moduleName, scopeName, fallback, errorFallback, componentProps }) {
988
- const RemoteComponent = lazyRemote({
989
- pkg,
990
- version,
991
- moduleName,
992
- scopeName
993
- });
994
- if (errorFallback) return /*#__PURE__*/ external_react_default().createElement(ErrorBoundary, {
995
- fallback: errorFallback
996
- }, /*#__PURE__*/ external_react_default().createElement(external_react_namespaceObject.Suspense, {
997
- fallback: fallback || /*#__PURE__*/ external_react_default().createElement("div", null, "Loading...")
998
- }, /*#__PURE__*/ external_react_default().createElement(RemoteComponent, componentProps)));
999
- return /*#__PURE__*/ external_react_default().createElement(external_react_namespaceObject.Suspense, {
1000
- fallback: fallback || /*#__PURE__*/ external_react_default().createElement("div", null, "Loading...")
1001
- }, /*#__PURE__*/ external_react_default().createElement(RemoteComponent, componentProps));
1002
- }
1003
- function withRemote(WrappedComponent, options) {
1004
- const RemoteComponent = lazyRemote(options);
1005
- return function(props) {
993
+ function SuspenseRemote({ fallback, children }) {
1006
994
  return /*#__PURE__*/ external_react_default().createElement(external_react_namespaceObject.Suspense, {
1007
- fallback: /*#__PURE__*/ external_react_default().createElement("div", null, "Loading...")
1008
- }, /*#__PURE__*/ external_react_default().createElement(RemoteComponent, props));
1009
- };
1010
- }
1011
- function useRemoteModuleHook(options) {
1012
- const { pkg, version = 'latest', moduleName, scopeName } = options;
1013
- const [state, setState] = (0, external_react_namespaceObject.useState)({
1014
- component: null,
1015
- loading: true,
1016
- error: null
1017
- });
1018
- (0, external_react_namespaceObject.useEffect)(()=>{
1019
- let mounted = true;
1020
- async function load() {
1021
- try {
1022
- setState((prev)=>({
1023
- ...prev,
1024
- loading: true,
995
+ fallback: fallback || /*#__PURE__*/ external_react_default().createElement("div", null, "Loading...")
996
+ }, children);
997
+ }
998
+ function SuspenseRemoteLoader({ pkg, version = 'latest', moduleName, scopeName, fallback, errorFallback, componentProps }) {
999
+ const RemoteComponent = lazyRemote({
1000
+ pkg,
1001
+ version,
1002
+ moduleName,
1003
+ scopeName
1004
+ });
1005
+ if (errorFallback) return /*#__PURE__*/ external_react_default().createElement(ErrorBoundary, {
1006
+ fallback: errorFallback
1007
+ }, /*#__PURE__*/ external_react_default().createElement(external_react_namespaceObject.Suspense, {
1008
+ fallback: fallback || /*#__PURE__*/ external_react_default().createElement("div", null, "Loading...")
1009
+ }, /*#__PURE__*/ external_react_default().createElement(RemoteComponent, componentProps)));
1010
+ return /*#__PURE__*/ external_react_default().createElement(external_react_namespaceObject.Suspense, {
1011
+ fallback: fallback || /*#__PURE__*/ external_react_default().createElement("div", null, "Loading...")
1012
+ }, /*#__PURE__*/ external_react_default().createElement(RemoteComponent, componentProps));
1013
+ }
1014
+ function withRemote(WrappedComponent, options) {
1015
+ const RemoteComponent = lazyRemote(options);
1016
+ return function(props) {
1017
+ return /*#__PURE__*/ external_react_default().createElement(external_react_namespaceObject.Suspense, {
1018
+ fallback: /*#__PURE__*/ external_react_default().createElement("div", null, "Loading...")
1019
+ }, /*#__PURE__*/ external_react_default().createElement(RemoteComponent, props));
1020
+ };
1021
+ }
1022
+ function useRemoteModuleHook(options) {
1023
+ const { pkg, version = 'latest', moduleName, scopeName } = options;
1024
+ const [state, setState] = (0, external_react_namespaceObject.useState)({
1025
+ component: null,
1026
+ loading: true,
1027
+ error: null
1028
+ });
1029
+ (0, external_react_namespaceObject.useEffect)(()=>{
1030
+ let mounted = true;
1031
+ async function load() {
1032
+ try {
1033
+ setState((prev)=>({
1034
+ ...prev,
1035
+ loading: true,
1036
+ error: null
1037
+ }));
1038
+ const { mf } = await loadRemoteMultiVersion({
1039
+ name: scopeName,
1040
+ pkg,
1041
+ version
1042
+ }, []);
1043
+ if (!mf) throw new Error(`[RemoteReloadUtils] Failed to get Module Federation instance for ${scopeName}`);
1044
+ const mod = await mf.loadRemote(`${scopeName}/${moduleName}`);
1045
+ if (mounted) if (mod && 'object' == typeof mod && 'default' in mod) setState({
1046
+ component: mod.default,
1047
+ loading: false,
1025
1048
  error: null
1026
- }));
1027
- const { mf } = await loadRemoteMultiVersion({
1028
- name: scopeName,
1029
- pkg,
1030
- version
1031
- }, []);
1032
- if (!mf) throw new Error(`[RemoteReloadUtils] Failed to get Module Federation instance for ${scopeName}`);
1033
- const mod = await mf.loadRemote(`${scopeName}/${moduleName}`);
1034
- if (mounted) if (mod && 'object' == typeof mod && 'default' in mod) setState({
1035
- component: mod.default,
1036
- loading: false,
1037
- error: null
1038
- });
1039
- else throw new Error(`[RemoteReloadUtils] Module "${scopeName}/${moduleName}" does not export a default component`);
1040
- } catch (err) {
1041
- if (mounted) setState({
1042
- component: null,
1043
- loading: false,
1044
- error: err instanceof Error ? err : new Error(String(err))
1045
- });
1049
+ });
1050
+ else throw new Error(`[RemoteReloadUtils] Module "${scopeName}/${moduleName}" does not export a default component`);
1051
+ } catch (err) {
1052
+ if (mounted) setState({
1053
+ component: null,
1054
+ loading: false,
1055
+ error: err instanceof Error ? err : new Error(String(err))
1056
+ });
1057
+ }
1046
1058
  }
1047
- }
1048
- load();
1049
- return ()=>{
1050
- mounted = false;
1059
+ load();
1060
+ return ()=>{
1061
+ mounted = false;
1062
+ };
1063
+ }, [
1064
+ pkg,
1065
+ version,
1066
+ moduleName,
1067
+ scopeName
1068
+ ]);
1069
+ return {
1070
+ component: state.component,
1071
+ loading: state.loading,
1072
+ error: state.error
1051
1073
  };
1052
- }, [
1053
- pkg,
1054
- version,
1055
- moduleName,
1056
- scopeName
1057
- ]);
1058
- return {
1059
- component: state.component,
1060
- loading: state.loading,
1061
- error: state.error
1062
- };
1063
- }
1074
+ }
1075
+ })();
1064
1076
  exports.ErrorBoundary = __webpack_exports__.ErrorBoundary;
1065
1077
  exports.RemoteModuleProvider = __webpack_exports__.RemoteModuleProvider;
1066
1078
  exports.RemoteModuleRenderer = __webpack_exports__.RemoteModuleRenderer;