@fulgurjs/federation 0.6.2 → 0.7.1

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/vue.js ADDED
@@ -0,0 +1,760 @@
1
+ import "./chunk-6EGDSX3S.js";
2
+
3
+ // src/vue.ts
4
+ import { defineAsyncComponent, defineComponent, h } from "vue";
5
+
6
+ // src/semver.ts
7
+ var VERSION_RE = /^v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-((?:[0-9A-Za-z-]+)(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z.-]+)?$/;
8
+ function parseVersion(input) {
9
+ const m = VERSION_RE.exec(String(input).trim());
10
+ if (!m) return null;
11
+ return {
12
+ major: Number(m[1]),
13
+ minor: Number(m[2] ?? 0),
14
+ patch: Number(m[3] ?? 0),
15
+ pre: m[4] ? m[4].split(".") : []
16
+ };
17
+ }
18
+ function preCompare(a, b) {
19
+ if (a.length === 0 && b.length === 0) return 0;
20
+ if (a.length === 0) return 1;
21
+ if (b.length === 0) return -1;
22
+ const len = Math.max(a.length, b.length);
23
+ for (let i = 0; i < len; i++) {
24
+ const x = a[i];
25
+ const y = b[i];
26
+ if (x === void 0) return -1;
27
+ if (y === void 0) return 1;
28
+ const xn = /^\d+$/.test(x);
29
+ const yn = /^\d+$/.test(y);
30
+ if (xn && yn) {
31
+ const d = Number(x) - Number(y);
32
+ if (d !== 0) return d;
33
+ } else if (xn !== yn) {
34
+ return xn ? -1 : 1;
35
+ } else if (x !== y) {
36
+ return x < y ? -1 : 1;
37
+ }
38
+ }
39
+ return 0;
40
+ }
41
+ function compareVersions(a, b) {
42
+ const va = parseVersion(a);
43
+ const vb = parseVersion(b);
44
+ if (!va || !vb) return a < b ? -1 : a > b ? 1 : 0;
45
+ if (va.major !== vb.major) return va.major - vb.major;
46
+ if (va.minor !== vb.minor) return va.minor - vb.minor;
47
+ if (va.patch !== vb.patch) return va.patch - vb.patch;
48
+ return preCompare(va.pre, vb.pre);
49
+ }
50
+ var ANY_RANGE = /^[a-z]+[+:-]/i;
51
+ function satisfies(version, range) {
52
+ if (range === false || range == null || range === "") return true;
53
+ const rangeStr = String(range).trim();
54
+ if (!rangeStr || rangeStr === "*" || rangeStr === "latest" || ANY_RANGE.test(rangeStr)) return true;
55
+ return rangeStr.split("||").some((part) => satisfiesSet(version, part.trim()));
56
+ }
57
+ function satisfiesSet(version, set) {
58
+ if (!set || set === "*") return !parseVersion(version)?.pre.length;
59
+ const hyphen = set.split(/\s+-\s+/);
60
+ if (hyphen.length === 2) {
61
+ const lo = hyphen[0].trim();
62
+ const hi = hyphen[1].trim();
63
+ if (!satisfies(version, `>=${lo}`)) return false;
64
+ if (!satisfies(version, `<=${hi}`)) return false;
65
+ return true;
66
+ }
67
+ const v = parseVersion(version);
68
+ if (!v) return false;
69
+ const comps = parseComparators(set);
70
+ if (comps === null) return true;
71
+ for (const c of comps) {
72
+ if (!testComparator(v, version, c)) return false;
73
+ }
74
+ if (v.pre.length > 0 && !comps.some((c) => c.ver.pre.length > 0)) {
75
+ const tupleMatch = comps.some(
76
+ (c) => c.ver.major === v.major && c.ver.minor === v.minor && c.ver.patch === v.patch
77
+ );
78
+ if (!tupleMatch) return false;
79
+ }
80
+ return true;
81
+ }
82
+ function parseComparators(set) {
83
+ const comps = [];
84
+ const tokens = set.trim().split(/\s+/).filter(Boolean);
85
+ if (tokens.length === 0) return null;
86
+ for (const token of tokens) {
87
+ const m = /^(>=|<=|>|<|=|\^|~)?(.*)$/.exec(token);
88
+ if (!m) return null;
89
+ const op = m[1];
90
+ const raw = m[2];
91
+ const c = buildComparator(op, raw);
92
+ if (c === null) return null;
93
+ comps.push(...c);
94
+ }
95
+ return comps;
96
+ }
97
+ function buildComparator(op, raw) {
98
+ const parsed = parseLoose(raw);
99
+ if (!parsed) return null;
100
+ const { ver, partial } = parsed;
101
+ switch (op) {
102
+ case "^": {
103
+ const upper = caretUpper(ver);
104
+ if (!upper) return null;
105
+ return [{ op: ">=", ver }, { op: "<", ver: upper }];
106
+ }
107
+ case "~": {
108
+ const upper = partial >= 2 ? { major: ver.major, minor: ver.minor + 1, patch: 0, pre: [] } : { major: ver.major + 1, minor: 0, patch: 0, pre: [] };
109
+ return [{ op: ">=", ver }, { op: "<", ver: upper }];
110
+ }
111
+ case ">=":
112
+ return [{ op: ">=", ver }];
113
+ case ">":
114
+ if (partial === 1) return [{ op: ">=", ver: { major: ver.major + 1, minor: 0, patch: 0, pre: [] } }];
115
+ if (partial === 2)
116
+ return [{ op: ">=", ver: { major: ver.major, minor: ver.minor + 1, patch: 0, pre: [] } }];
117
+ return [{ op: ">", ver }];
118
+ case "<":
119
+ if (partial === 1) return [{ op: "<", ver: { major: ver.major, minor: 0, patch: 0, pre: [] } }];
120
+ if (partial === 2)
121
+ return [{ op: "<", ver: { major: ver.major, minor: ver.minor, patch: 0, pre: [] } }];
122
+ return [{ op: "<", ver }];
123
+ case "<=":
124
+ return [{ op: "<=", ver }];
125
+ case "=":
126
+ case void 0:
127
+ if (partial === 1)
128
+ return [
129
+ { op: ">=", ver },
130
+ { op: "<", ver: { major: ver.major + 1, minor: 0, patch: 0, pre: [] } }
131
+ ];
132
+ if (partial === 2)
133
+ return [
134
+ { op: ">=", ver },
135
+ { op: "<", ver: { major: ver.major, minor: ver.minor + 1, patch: 0, pre: [] } }
136
+ ];
137
+ return [{ op: "=", ver }];
138
+ default:
139
+ return null;
140
+ }
141
+ }
142
+ function caretUpper(ver) {
143
+ if (ver.major > 0) return { major: ver.major + 1, minor: 0, patch: 0, pre: [] };
144
+ if (ver.minor > 0) return { major: 0, minor: ver.minor + 1, patch: 0, pre: [] };
145
+ return { major: 0, minor: 0, patch: ver.patch + 1, pre: [] };
146
+ }
147
+ function parseLoose(raw) {
148
+ const cleaned = raw.replace(/^v/, "").trim();
149
+ if (!cleaned || cleaned === "*" || cleaned === "x" || cleaned === "X") {
150
+ return { ver: { major: 0, minor: 0, patch: 0, pre: [] }, partial: 0 };
151
+ }
152
+ const parts = cleaned.split(".");
153
+ const nums = [];
154
+ let partial = 0;
155
+ for (const p of parts.slice(0, 3)) {
156
+ if (/^\d+$/.test(p)) {
157
+ nums.push(Number(p));
158
+ partial++;
159
+ } else if (p === "x" || p === "X" || p === "*") {
160
+ break;
161
+ } else {
162
+ break;
163
+ }
164
+ }
165
+ const rest = cleaned.split("-");
166
+ const pre = rest.length > 1 ? rest.slice(1).join("-").split("+")[0].split(".") : [];
167
+ if (partial === 0 && pre.length === 0) return { ver: { major: 0, minor: 0, patch: 0, pre: [] }, partial: 0 };
168
+ return {
169
+ ver: { major: nums[0] ?? 0, minor: nums[1] ?? 0, patch: nums[2] ?? 0, pre },
170
+ partial
171
+ };
172
+ }
173
+ function cmpToBool(cmp, op) {
174
+ switch (op) {
175
+ case ">=":
176
+ return cmp >= 0;
177
+ case ">":
178
+ return cmp > 0;
179
+ case "<":
180
+ return cmp < 0;
181
+ case "<=":
182
+ return cmp <= 0;
183
+ case "=":
184
+ return cmp === 0;
185
+ }
186
+ }
187
+ function testComparator(v, versionStr, c) {
188
+ const full = `${v.major}.${v.minor}.${v.patch}` + (v.pre.length ? `-${v.pre.join(".")}` : "");
189
+ if (c.upper) {
190
+ const cmpLo = compareVersions(full, `${c.ver.major}.${c.ver.minor}.${c.ver.patch}` + (c.ver.pre.length ? `-${c.ver.pre.join(".")}` : ""));
191
+ if (!(cmpLo >= 0)) return false;
192
+ const cmpHi = compareVersions(full, `${c.upper.major}.${c.upper.minor}.${c.upper.patch}`);
193
+ if (!(cmpHi < 0)) return false;
194
+ return true;
195
+ }
196
+ const cmp = compareVersions(full, `${c.ver.major}.${c.ver.minor}.${c.ver.patch}` + (c.ver.pre.length ? `-${c.ver.pre.join(".")}` : ""));
197
+ const baseOk = cmpToBool(cmp, c.op);
198
+ if (c.ver.pre.length > 0 && v.pre.length === 0 && (c.op === ">" || c.op === "<")) {
199
+ return false;
200
+ }
201
+ return baseOk;
202
+ }
203
+
204
+ // src/runtime/errors.ts
205
+ var FulgurjsError = class extends Error {
206
+ code;
207
+ details;
208
+ constructor(code, message, details) {
209
+ super(`[fulgurjs:${code}] ${message}`);
210
+ this.name = "FulgurjsError";
211
+ this.code = code;
212
+ this.details = details;
213
+ }
214
+ };
215
+ var ErrorCodes = {
216
+ /** 远程容器加载失败(网络/超时/重试耗尽/熔断) */
217
+ REMOTE_LOAD_FAILED: "MFU-001",
218
+ /** remoteEntry 自报名与 remotes 配置的名称不匹配 */
219
+ REMOTE_NAME_MISMATCH: "MFU-002",
220
+ /** strictVersion 开启且共享版本不满足 */
221
+ SHARE_STRICT_VERSION: "MFU-003",
222
+ /** 共享模块缺失且无本地 fallback(import: false) */
223
+ SHARE_NOT_AVAILABLE: "MFU-004",
224
+ /** 同一容器用不同 share scope 重复 init */
225
+ CONTAINER_REINIT_CONFLICT: "MFU-005",
226
+ /** 请求的模块未被该远程 exposes */
227
+ MODULE_NOT_EXPOSED: "MFU-006",
228
+ /** 预加载失败 */
229
+ PRELOAD_FAILED: "MFU-007",
230
+ /** 未知远程 */
231
+ REMOTE_UNKNOWN: "MFU-008",
232
+ /** 加载到的模块没有任何导出(常见:exposes 指向了不导出内容的文件) */
233
+ EMPTY_EXPORTS: "MFU-009",
234
+ /** singleton 共享协商版本与消费方 requiredVersion 不一致(使用作用域版本,仅告警) */
235
+ SINGLETON_SKEW: "MFU-010"
236
+ };
237
+
238
+ // src/runtime/index.ts
239
+ var DEFAULT_TIMEOUT = 15e3;
240
+ var DEFAULT_RETRIES = 2;
241
+ var BREAKER_THRESHOLD = 5;
242
+ var BREAKER_RESET_MS = 3e4;
243
+ function createRuntime() {
244
+ const shareScopeMap2 = {};
245
+ const remotes = /* @__PURE__ */ new Map();
246
+ const plugins = [];
247
+ const loadedModules = /* @__PURE__ */ new Map();
248
+ const containerInitScopes = /* @__PURE__ */ new WeakMap();
249
+ let fulgurjsAppConfig = {};
250
+ function provideFulgurjsAppConfig2(config) {
251
+ fulgurjsAppConfig = { ...fulgurjsAppConfig, ...config };
252
+ if (typeof globalThis !== "undefined") {
253
+ ;
254
+ globalThis.__FULGURJS_APP_CONFIG__ = fulgurjsAppConfig;
255
+ }
256
+ }
257
+ function getFulgurjsAppConfig2() {
258
+ return fulgurjsAppConfig;
259
+ }
260
+ const hooks = {};
261
+ const applyPlugins = () => {
262
+ for (const p of plugins) {
263
+ try {
264
+ p.init?.(hooks);
265
+ } catch (err) {
266
+ console.warn("[fulgurjs] runtimePlugin init failed:", p.name, err);
267
+ }
268
+ }
269
+ };
270
+ const getScope = (name) => shareScopeMap2[name] ??= {};
271
+ function emitError(info) {
272
+ try {
273
+ hooks.onRemoteError?.(info);
274
+ } catch {
275
+ }
276
+ if (typeof window !== "undefined") {
277
+ window.dispatchEvent(new CustomEvent("fulgurjs:error", { detail: info }));
278
+ }
279
+ }
280
+ function initSharing2(scopeName = "default") {
281
+ getScope(scopeName);
282
+ return shareScopeMap2;
283
+ }
284
+ function registerShare2(scopeName, name, version, get, opts = {}) {
285
+ const scope = getScope(scopeName || "default");
286
+ const byName = scope[name] ??= {};
287
+ if (byName[version]) {
288
+ return;
289
+ }
290
+ byName[version] = { version, get, from: opts.from ?? "unknown", eager: !!opts.eager, loaded: !!opts.loaded };
291
+ }
292
+ function registerRemotes2(list) {
293
+ for (const r of list) {
294
+ const prev = remotes.get(r.name);
295
+ remotes.set(r.name, {
296
+ timeout: DEFAULT_TIMEOUT,
297
+ retries: DEFAULT_RETRIES,
298
+ ...prev,
299
+ ...r,
300
+ state: prev?.state ?? "idle",
301
+ breakerState: prev?.breakerState ?? { fails: 0, openUntil: 0 },
302
+ debug: prev?.debug ?? { entry: r.entry, status: "idle" }
303
+ });
304
+ }
305
+ }
306
+ function registerRemote2(remote) {
307
+ registerRemotes2([remote]);
308
+ }
309
+ function registerPlugins2(list) {
310
+ plugins.push(...list);
311
+ applyPlugins();
312
+ }
313
+ async function loadShare2(name, opts = {}) {
314
+ const scopeName = opts.shareScope || "default";
315
+ const shareKey = opts.shareKey || name;
316
+ const scope = getScope(scopeName);
317
+ const byName = scope[shareKey] || {};
318
+ const versions = Object.keys(byName);
319
+ const req = opts.requiredVersion;
320
+ const satisfying = versions.filter(
321
+ (v) => req === void 0 || req === false || satisfies(v, req)
322
+ );
323
+ let pick;
324
+ if (opts.singleton && versions.length > 0) {
325
+ const loadedVersions = versions.filter((v) => byName[v].loaded);
326
+ pick = [...loadedVersions.length ? loadedVersions : versions].sort(compareVersions).pop();
327
+ if (versions.length > 1 || !satisfying.includes(pick)) {
328
+ const entry0 = byName[pick];
329
+ const msg = `singleton skew "${shareKey}": req "${req ?? "any"}" use ${pick} from ${entry0.from}`;
330
+ if (opts.strictVersion) {
331
+ const err = new FulgurjsError(ErrorCodes.SHARE_STRICT_VERSION, msg, {
332
+ shareKey,
333
+ requiredVersion: req
334
+ });
335
+ emitError({ remote: entry0.from, error: err });
336
+ throw err;
337
+ }
338
+ console.warn(`[fulgurjs:MFU-010] ${msg}`);
339
+ }
340
+ } else {
341
+ pick = [...satisfying].sort(compareVersions).pop();
342
+ if (!pick) {
343
+ if (opts.strictVersion) {
344
+ const err2 = new FulgurjsError(
345
+ ErrorCodes.SHARE_STRICT_VERSION,
346
+ `no satisfying version for "${shareKey}" (required "${req}") in share scope "${scopeName}"`
347
+ );
348
+ emitError({ remote: shareKey, error: err2 });
349
+ throw err2;
350
+ }
351
+ if (opts.fallback) return opts.fallback();
352
+ const err = new FulgurjsError(
353
+ ErrorCodes.SHARE_NOT_AVAILABLE,
354
+ `shared "${shareKey}" (${req ?? "any"}) unavailable in scope "${scopeName}" (no local fallback)`,
355
+ { shareKey, requiredVersion: req }
356
+ );
357
+ emitError({ remote: shareKey, error: err });
358
+ throw err;
359
+ }
360
+ }
361
+ let entry = byName[pick];
362
+ if (hooks.resolveShare) {
363
+ const picked = await hooks.resolveShare({
364
+ shareKey,
365
+ shareScope: scopeName,
366
+ requiredVersion: req,
367
+ picked: entry,
368
+ available: Object.values(byName)
369
+ });
370
+ if (picked) entry = picked;
371
+ }
372
+ entry.loaded = true;
373
+ return entry.get();
374
+ }
375
+ function withTimeout(p, ms, label) {
376
+ return new Promise((resolve, reject) => {
377
+ const t = setTimeout(() => reject(new Error(`timeout after ${ms}ms: ${label}`)), ms);
378
+ p.then(
379
+ (v) => {
380
+ clearTimeout(t);
381
+ resolve(v);
382
+ },
383
+ (e) => {
384
+ clearTimeout(t);
385
+ reject(e);
386
+ }
387
+ );
388
+ });
389
+ }
390
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
391
+ function validateContainerInterface(container, remote) {
392
+ if (!container || typeof container.get !== "function") {
393
+ throw new Error(
394
+ `remoteEntry of "${remote.name}" does not export a container interface (get/init)`
395
+ );
396
+ }
397
+ if (container.name && remote.name && container.name !== remote.name) {
398
+ const msg = `remoteEntry self-reported name "${container.name}" mismatches configured name "${remote.name}"`;
399
+ if (remote.promise) {
400
+ console.warn(`[fulgurjs] ${msg} (promise-based remote, continuing)`);
401
+ } else {
402
+ throw Object.assign(new Error(msg), { code: ErrorCodes.REMOTE_NAME_MISMATCH });
403
+ }
404
+ }
405
+ }
406
+ async function importEntry(url, remote) {
407
+ return withTimeout(
408
+ import(
409
+ /* @vite-ignore */
410
+ url
411
+ ),
412
+ remote.timeout ?? DEFAULT_TIMEOUT,
413
+ `load remoteEntry ${url}`
414
+ );
415
+ }
416
+ async function acquireContainer(remote, overrides) {
417
+ const scopeKey = remote.shareScope || "default";
418
+ if (remote.container) {
419
+ const prevScope = containerInitScopes.get(remote.container);
420
+ if (prevScope && prevScope !== scopeKey) {
421
+ throw new FulgurjsError(
422
+ ErrorCodes.CONTAINER_REINIT_CONFLICT,
423
+ `container "${remote.name}" already initialized with share scope "${prevScope}", cannot re-init with "${scopeKey}"`
424
+ );
425
+ }
426
+ return remote.container;
427
+ }
428
+ if (remote.containerPromise) return remote.containerPromise;
429
+ const b = remote.breakerState;
430
+ if (b.openUntil > Date.now()) {
431
+ const err = new FulgurjsError(
432
+ ErrorCodes.REMOTE_LOAD_FAILED,
433
+ `circuit breaker open for remote "${remote.name}" until ${new Date(b.openUntil).toISOString()}`,
434
+ { remote: remote.name }
435
+ );
436
+ emitError({ remote: remote.name, error: err });
437
+ throw err;
438
+ }
439
+ remote.state = "loading";
440
+ remote.debug.status = "loading";
441
+ const containerPromise = (async () => {
442
+ const loaders = [];
443
+ if (remote.promise) {
444
+ loaders.push(async () => {
445
+ const resolved = await remote.promise();
446
+ const container2 = typeof resolved === "string" ? await importEntry(resolved, remote) : resolved;
447
+ validateContainerInterface(container2, remote);
448
+ return container2;
449
+ });
450
+ } else {
451
+ for (const url of [remote.entry, ...remote.fallback ?? []]) {
452
+ loaders.push(async () => {
453
+ const container2 = await importEntry(url, remote);
454
+ validateContainerInterface(container2, remote);
455
+ return container2;
456
+ });
457
+ }
458
+ }
459
+ let container;
460
+ let lastErr;
461
+ let succeeded = false;
462
+ const maxRetries = overrides?.retries ?? remote.retries ?? DEFAULT_RETRIES;
463
+ for (const loader of loaders) {
464
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
465
+ const start = performance.now();
466
+ try {
467
+ container = await loader();
468
+ remote.lastLoadMs = Math.round(performance.now() - start);
469
+ remote.debug.lastLoadMs = remote.lastLoadMs;
470
+ succeeded = true;
471
+ break;
472
+ } catch (err) {
473
+ lastErr = err;
474
+ if (err?.code) throw err;
475
+ if (attempt < maxRetries) {
476
+ await sleep(200 * 2 ** attempt);
477
+ }
478
+ }
479
+ }
480
+ if (succeeded) break;
481
+ }
482
+ if (!succeeded || !container) {
483
+ const hints = [];
484
+ if (/^https?:\/\//.test(remote.entry) && typeof location !== "undefined") {
485
+ hints.push(`1. is the remote dev server running? try opening ${remote.entry} in the browser \u2014 it must return JS, not HTML/an error`);
486
+ hints.push(`2. is the URL correct in federation({ remotes })? dev and prod entries can differ`);
487
+ hints.push(`3. CORS: the remote dev server must allow cross-origin requests (fulgurjs-federation enables server.cors automatically; a custom server config may have disabled it)`);
488
+ } else {
489
+ hints.push(`1. is the remote deployed and reachable? try opening ${remote.entry} in the browser \u2014 it must return JS`);
490
+ hints.push(`2. NGINX/CDN routing: the entry path must serve the remoteEntry JS file (check try_files / fallback rules)`);
491
+ }
492
+ throw new FulgurjsError(
493
+ ErrorCodes.REMOTE_LOAD_FAILED,
494
+ `failed to load remote "${remote.name}" from ${remote.entry}: ${String(lastErr?.message ?? lastErr)}
495
+ ` + hints.join("\n"),
496
+ { remote: remote.name }
497
+ );
498
+ }
499
+ validateContainerInterface(container, remote);
500
+ const prevScope = containerInitScopes.get(container);
501
+ if (prevScope && prevScope !== scopeKey) {
502
+ throw new FulgurjsError(
503
+ ErrorCodes.CONTAINER_REINIT_CONFLICT,
504
+ `container "${remote.name}" already initialized with share scope "${prevScope}", cannot re-init with "${scopeKey}"`
505
+ );
506
+ }
507
+ if (typeof container.init === "function") {
508
+ await container.init(shareScopeMap2);
509
+ }
510
+ containerInitScopes.set(container, scopeKey);
511
+ remote.container = container;
512
+ remote.state = "loaded";
513
+ remote.debug.status = "loaded";
514
+ b.fails = 0;
515
+ return container;
516
+ })();
517
+ remote.containerPromise = containerPromise;
518
+ try {
519
+ return await containerPromise;
520
+ } catch (err) {
521
+ remote.containerPromise = void 0;
522
+ remote.state = "failed";
523
+ remote.debug.status = "failed";
524
+ remote.debug.error = String(err?.message ?? err);
525
+ b.fails += 1;
526
+ if (b.fails >= BREAKER_THRESHOLD) {
527
+ b.openUntil = Date.now() + BREAKER_RESET_MS;
528
+ b.fails = 0;
529
+ }
530
+ if (err instanceof FulgurjsError) {
531
+ emitError({ remote: remote.name, error: err });
532
+ throw err;
533
+ }
534
+ if (err?.code) {
535
+ emitError({ remote: remote.name, error: err });
536
+ throw err;
537
+ }
538
+ const wrapped = new FulgurjsError(ErrorCodes.REMOTE_LOAD_FAILED, String(err?.message ?? err), {
539
+ remote: remote.name
540
+ });
541
+ emitError({ remote: remote.name, error: wrapped });
542
+ throw wrapped;
543
+ }
544
+ }
545
+ function parseSpec(spec) {
546
+ const idx = spec.indexOf("/");
547
+ if (idx === -1) return { remote: spec, module: "" };
548
+ return { remote: spec.slice(0, idx), module: normalizeModuleName(spec.slice(idx + 1)) };
549
+ }
550
+ function normalizeModuleName(m) {
551
+ if (!m) return "";
552
+ return m.startsWith(".") ? m : `./${m}`;
553
+ }
554
+ async function loadRemote2(spec, opts) {
555
+ const { remote: name, module } = parseSpec(spec);
556
+ hooks.beforeLoadRemote?.({ remote: name, module });
557
+ const remote = remotes.get(name);
558
+ if (!remote) {
559
+ throw new FulgurjsError(
560
+ ErrorCodes.REMOTE_UNKNOWN,
561
+ `unknown remote "${name}". Register it via federation({ remotes }) or registerRemote().`,
562
+ { remote: name }
563
+ );
564
+ }
565
+ let container;
566
+ try {
567
+ container = await acquireContainer(remote, { retries: opts?.retries });
568
+ } catch (err) {
569
+ if (opts?.fallbackModule) {
570
+ console.error(`[fulgurjs] loadRemote("${spec}") failed; returning fallbackModule (\u663E\u5F0F\u964D\u7EA7\uFF0C\u9519\u8BEF\u5DF2\u900F\u51FA)`, err);
571
+ emitError({ remote: name, error: err });
572
+ return await opts.fallbackModule();
573
+ }
574
+ throw err;
575
+ }
576
+ if (!module) return container;
577
+ const cacheKey = `${name}@${remote.shareScope || "default"}#${module}`;
578
+ if (!loadedModules.has(cacheKey)) {
579
+ loadedModules.set(
580
+ cacheKey,
581
+ container.get(module).catch((err) => {
582
+ loadedModules.delete(cacheKey);
583
+ if (err instanceof FulgurjsError || err?.code) throw err;
584
+ throw new FulgurjsError(
585
+ ErrorCodes.REMOTE_LOAD_FAILED,
586
+ `failed to load module "${module}" from remote "${name}": ${String(err?.message ?? err)}`,
587
+ { remote: name, module }
588
+ );
589
+ })
590
+ );
591
+ }
592
+ let ns;
593
+ try {
594
+ ns = await loadedModules.get(cacheKey);
595
+ } catch (err) {
596
+ if (opts?.fallbackModule) {
597
+ console.error(`[fulgurjs] loadRemote("${spec}") failed; returning fallbackModule (\u663E\u5F0F\u964D\u7EA7\uFF0C\u9519\u8BEF\u5DF2\u900F\u51FA)`, err);
598
+ emitError({ remote: name, error: err });
599
+ return await opts.fallbackModule();
600
+ }
601
+ throw err;
602
+ }
603
+ hooks.afterLoadRemote?.({ remote: name, module, module_ns: ns });
604
+ if (ns && typeof ns === "object" && Object.keys(ns).length === 0) {
605
+ const err = new FulgurjsError(
606
+ ErrorCodes.EMPTY_EXPORTS,
607
+ `no exports: ${module} @ ${name}`,
608
+ { remote: name, module }
609
+ );
610
+ emitError({ remote: name, error: err });
611
+ console.error(`[fulgurjs:MFU-009] ${err.message}`);
612
+ }
613
+ return ns;
614
+ }
615
+ function getContainer2(name) {
616
+ const remote = remotes.get(name);
617
+ if (!remote) {
618
+ throw new FulgurjsError(ErrorCodes.REMOTE_UNKNOWN, `unknown remote "${name}"`, { remote: name });
619
+ }
620
+ return acquireContainer(remote);
621
+ }
622
+ async function preloadRemote2(spec, opts = {}) {
623
+ const { remote: name } = parseSpec(spec);
624
+ const remote = remotes.get(name);
625
+ if (!remote) {
626
+ throw new FulgurjsError(ErrorCodes.REMOTE_UNKNOWN, `unknown remote "${name}"`, { remote: name });
627
+ }
628
+ const mode = opts.mode ?? "preload";
629
+ const inject = (href, as) => {
630
+ if (typeof document === "undefined") return;
631
+ if (document.querySelector(`link[href="${href}"]`)) return;
632
+ const link = document.createElement("link");
633
+ link.rel = as === "style" ? "stylesheet" : "modulepreload";
634
+ link.href = href;
635
+ if (mode === "prefetch") {
636
+ ;
637
+ link.fetchPriority = "low";
638
+ }
639
+ document.head.appendChild(link);
640
+ };
641
+ try {
642
+ if (remote.manifestUrl) {
643
+ const res = await fetch(remote.manifestUrl, { cache: "no-cache" });
644
+ if (res.ok) {
645
+ const manifest = await res.json();
646
+ inject(remote.entry, "modulepreload");
647
+ const exposes = manifest.exposes ?? {};
648
+ for (const key of Object.keys(exposes)) {
649
+ const item = exposes[key];
650
+ if (item?.file) inject(new URL(item.file, remote.entry).href, "modulepreload");
651
+ for (const css of item?.css ?? []) inject(new URL(css, remote.entry).href, "style");
652
+ }
653
+ return;
654
+ }
655
+ }
656
+ inject(remote.entry, "modulepreload");
657
+ } catch (err) {
658
+ emitError({
659
+ remote: name,
660
+ error: new FulgurjsError(ErrorCodes.PRELOAD_FAILED, String(err?.message ?? err), {
661
+ remote: name
662
+ })
663
+ });
664
+ }
665
+ }
666
+ const attachDebug = () => {
667
+ if (typeof window === "undefined") return;
668
+ window.__FULGURJS_SCOPE__ = shareScopeMap2;
669
+ window.__FULGURJS_INFO__ = {
670
+ get remotes() {
671
+ const out = {};
672
+ for (const [k, v] of remotes) {
673
+ out[k] = { ...v.debug, entry: v.entry };
674
+ }
675
+ return out;
676
+ },
677
+ errors: []
678
+ };
679
+ };
680
+ attachDebug();
681
+ return {
682
+ shareScopeMap: shareScopeMap2,
683
+ initSharing: initSharing2,
684
+ registerShare: registerShare2,
685
+ registerRemotes: registerRemotes2,
686
+ registerRemote: registerRemote2,
687
+ registerPlugins: registerPlugins2,
688
+ loadShare: loadShare2,
689
+ loadRemote: loadRemote2,
690
+ getContainer: getContainer2,
691
+ preloadRemote: preloadRemote2,
692
+ parseSpec,
693
+ provideFulgurjsAppConfig: provideFulgurjsAppConfig2,
694
+ getFulgurjsAppConfig: getFulgurjsAppConfig2
695
+ };
696
+ }
697
+ var g = globalThis;
698
+ var runtime = g.__FULGURJS_RUNTIME__ ?? createRuntime();
699
+ g.__FULGURJS_RUNTIME__ = runtime;
700
+ try {
701
+ Object.freeze(runtime);
702
+ } catch {
703
+ }
704
+ var initSharing = runtime.initSharing;
705
+ var registerShare = runtime.registerShare;
706
+ var registerRemotes = runtime.registerRemotes;
707
+ var registerRemote = runtime.registerRemote;
708
+ var registerPlugins = runtime.registerPlugins;
709
+ var loadShare = runtime.loadShare;
710
+ var loadRemote = runtime.loadRemote;
711
+ var getContainer = runtime.getContainer;
712
+ var preloadRemote = runtime.preloadRemote;
713
+ var shareScopeMap = runtime.shareScopeMap;
714
+ var provideFulgurjsAppConfig = runtime.provideFulgurjsAppConfig;
715
+ var getFulgurjsAppConfig = runtime.getFulgurjsAppConfig;
716
+
717
+ // src/vue.ts
718
+ var ERROR_STYLE = {
719
+ padding: "16px",
720
+ border: "1px solid #fde2e2",
721
+ borderRadius: "4px",
722
+ background: "#fef0f0",
723
+ color: "#c45656",
724
+ fontSize: "13px",
725
+ lineHeight: "1.6"
726
+ };
727
+ var RemoteErrorPlaceholder = defineComponent({
728
+ name: "FulgurjsRemoteError",
729
+ props: {
730
+ error: { type: Object, default: void 0 }
731
+ },
732
+ setup(props) {
733
+ return () => {
734
+ const err = props.error;
735
+ const code = err?.code ?? "UNKNOWN";
736
+ const message = err?.message ?? String(props.error ?? "unknown error");
737
+ return h("div", { style: ERROR_STYLE }, [
738
+ h("p", { style: "margin:0 0 4px;font-weight:600" }, `\u8FDC\u7A0B\u7EC4\u4EF6\u52A0\u8F7D\u5931\u8D25\uFF08\u9519\u8BEF\u7801 ${code}\uFF09`),
739
+ h("p", { style: "margin:0 0 8px;word-break:break-all" }, message),
740
+ h(
741
+ "p",
742
+ { style: "margin:0" },
743
+ "\u4FEE\u6CD5\uFF1A\u2460 \u6838\u5BF9 spec \u7684\u300C\u8FDC\u7A0B\u540D/expose \u540D\u300D\u4E0E\u8FDC\u7A0B\u5E94\u7528 exposes \u662F\u5426\u4E00\u81F4\uFF08MFU-006/008\uFF09\uFF1B\u2461 \u6838\u5BF9 remotes \u5730\u5740\u7AEF\u53E3\u4E0E\u8FDC\u7A0B\u670D\u52A1\u53EF\u8FBE\u6027\u3001remoteEntry \u662F\u5426\u53EF\u8BBF\u95EE\uFF08MFU-001\uFF09\uFF1B\u2462 \u67E5\u770B window \u7684 fulgurjs:error \u4E8B\u4EF6\u4E0E console \u540C\u6E90\u9519\u8BEF\u5B9A\u4F4D\u6839\u56E0\u3002"
744
+ )
745
+ ]);
746
+ };
747
+ }
748
+ });
749
+ function remoteComponent(spec, opts = {}) {
750
+ return defineAsyncComponent({
751
+ loader: () => loadRemote(spec, { retries: opts.retries }).then((m) => m.default ?? m),
752
+ loadingComponent: opts.loadingComponent,
753
+ errorComponent: opts.errorComponent ?? RemoteErrorPlaceholder,
754
+ delay: opts.delay,
755
+ timeout: opts.timeout
756
+ });
757
+ }
758
+ export {
759
+ remoteComponent
760
+ };