@fulgurjs/federation 3.0.1 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/vue.js CHANGED
@@ -1,845 +1,4 @@
1
- import {
2
- ErrorCodes,
3
- FgError
4
- } from "./chunk-3VBYOVH4.js";
5
- import "./chunk-TMSMEZPM.js";
6
-
7
- // src/vue.ts
8
- import { defineAsyncComponent, defineComponent, h } from "vue";
9
-
10
- // src/semver.ts
11
- var VERSION_RE = /^v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-((?:[0-9A-Za-z-]+)(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z.-]+)?$/;
12
- function parseVersion(input) {
13
- const m = VERSION_RE.exec(String(input).trim());
14
- if (!m) return null;
15
- return {
16
- major: Number(m[1]),
17
- minor: Number(m[2] ?? 0),
18
- patch: Number(m[3] ?? 0),
19
- pre: m[4] ? m[4].split(".") : []
20
- };
21
- }
22
- function preCompare(a, b) {
23
- if (a.length === 0 && b.length === 0) return 0;
24
- if (a.length === 0) return 1;
25
- if (b.length === 0) return -1;
26
- const len = Math.max(a.length, b.length);
27
- for (let i = 0; i < len; i++) {
28
- const x = a[i];
29
- const y = b[i];
30
- if (x === void 0) return -1;
31
- if (y === void 0) return 1;
32
- const xn = /^\d+$/.test(x);
33
- const yn = /^\d+$/.test(y);
34
- if (xn && yn) {
35
- const d = Number(x) - Number(y);
36
- if (d !== 0) return d;
37
- } else if (xn !== yn) {
38
- return xn ? -1 : 1;
39
- } else if (x !== y) {
40
- return x < y ? -1 : 1;
41
- }
42
- }
43
- return 0;
44
- }
45
- function compareVersions(a, b) {
46
- const va = parseVersion(a);
47
- const vb = parseVersion(b);
48
- if (!va || !vb) return a < b ? -1 : a > b ? 1 : 0;
49
- if (va.major !== vb.major) return va.major - vb.major;
50
- if (va.minor !== vb.minor) return va.minor - vb.minor;
51
- if (va.patch !== vb.patch) return va.patch - vb.patch;
52
- return preCompare(va.pre, vb.pre);
53
- }
54
- var ANY_RANGE = /^[a-z]+[+:-]/i;
55
- function satisfies(version, range) {
56
- if (range === false || range == null || range === "") return true;
57
- const rangeStr = String(range).trim();
58
- if (!rangeStr || rangeStr === "*" || rangeStr === "latest" || ANY_RANGE.test(rangeStr)) return true;
59
- return rangeStr.split("||").some((part) => satisfiesSet(version, part.trim()));
60
- }
61
- function satisfiesSet(version, set) {
62
- if (!set || set === "*") return !parseVersion(version)?.pre.length;
63
- const hyphen = set.split(/\s+-\s+/);
64
- if (hyphen.length === 2) {
65
- const lo = hyphen[0].trim();
66
- const hi = hyphen[1].trim();
67
- if (!satisfies(version, `>=${lo}`)) return false;
68
- if (!satisfies(version, `<=${hi}`)) return false;
69
- return true;
70
- }
71
- const v = parseVersion(version);
72
- if (!v) return false;
73
- const comps = parseComparators(set);
74
- if (comps === null) return true;
75
- for (const c of comps) {
76
- if (!testComparator(v, version, c)) return false;
77
- }
78
- if (v.pre.length > 0 && !comps.some((c) => c.ver.pre.length > 0)) {
79
- const tupleMatch = comps.some(
80
- (c) => c.ver.major === v.major && c.ver.minor === v.minor && c.ver.patch === v.patch
81
- );
82
- if (!tupleMatch) return false;
83
- }
84
- return true;
85
- }
86
- function parseComparators(set) {
87
- const comps = [];
88
- const tokens = set.trim().split(/\s+/).filter(Boolean);
89
- if (tokens.length === 0) return null;
90
- for (const token of tokens) {
91
- const m = /^(>=|<=|>|<|=|\^|~)?(.*)$/.exec(token);
92
- if (!m) return null;
93
- const op = m[1];
94
- const raw = m[2];
95
- const c = buildComparator(op, raw);
96
- if (c === null) return null;
97
- comps.push(...c);
98
- }
99
- return comps;
100
- }
101
- function buildComparator(op, raw) {
102
- const parsed = parseLoose(raw);
103
- if (!parsed) return null;
104
- const { ver, partial } = parsed;
105
- switch (op) {
106
- case "^": {
107
- const upper = caretUpper(ver);
108
- if (!upper) return null;
109
- return [{ op: ">=", ver }, { op: "<", ver: upper }];
110
- }
111
- case "~": {
112
- const upper = partial >= 2 ? { major: ver.major, minor: ver.minor + 1, patch: 0, pre: [] } : { major: ver.major + 1, minor: 0, patch: 0, pre: [] };
113
- return [{ op: ">=", ver }, { op: "<", ver: upper }];
114
- }
115
- case ">=":
116
- return [{ op: ">=", ver }];
117
- case ">":
118
- if (partial === 1) return [{ op: ">=", ver: { major: ver.major + 1, minor: 0, patch: 0, pre: [] } }];
119
- if (partial === 2)
120
- return [{ op: ">=", ver: { major: ver.major, minor: ver.minor + 1, patch: 0, pre: [] } }];
121
- return [{ op: ">", ver }];
122
- case "<":
123
- if (partial === 1) return [{ op: "<", ver: { major: ver.major, minor: 0, patch: 0, pre: [] } }];
124
- if (partial === 2)
125
- return [{ op: "<", ver: { major: ver.major, minor: ver.minor, patch: 0, pre: [] } }];
126
- return [{ op: "<", ver }];
127
- case "<=":
128
- return [{ op: "<=", ver }];
129
- case "=":
130
- case void 0:
131
- if (partial === 1)
132
- return [
133
- { op: ">=", ver },
134
- { op: "<", ver: { major: ver.major + 1, minor: 0, patch: 0, pre: [] } }
135
- ];
136
- if (partial === 2)
137
- return [
138
- { op: ">=", ver },
139
- { op: "<", ver: { major: ver.major, minor: ver.minor + 1, patch: 0, pre: [] } }
140
- ];
141
- return [{ op: "=", ver }];
142
- default:
143
- return null;
144
- }
145
- }
146
- function caretUpper(ver) {
147
- if (ver.major > 0) return { major: ver.major + 1, minor: 0, patch: 0, pre: [] };
148
- if (ver.minor > 0) return { major: 0, minor: ver.minor + 1, patch: 0, pre: [] };
149
- return { major: 0, minor: 0, patch: ver.patch + 1, pre: [] };
150
- }
151
- function parseLoose(raw) {
152
- const cleaned = raw.replace(/^v/, "").trim();
153
- if (!cleaned || cleaned === "*" || cleaned === "x" || cleaned === "X") {
154
- return { ver: { major: 0, minor: 0, patch: 0, pre: [] }, partial: 0 };
155
- }
156
- const parts = cleaned.split(".");
157
- const nums = [];
158
- let partial = 0;
159
- for (const p of parts.slice(0, 3)) {
160
- if (/^\d+$/.test(p)) {
161
- nums.push(Number(p));
162
- partial++;
163
- } else if (p === "x" || p === "X" || p === "*") {
164
- break;
165
- } else {
166
- break;
167
- }
168
- }
169
- const rest = cleaned.split("-");
170
- const pre = rest.length > 1 ? rest.slice(1).join("-").split("+")[0].split(".") : [];
171
- if (partial === 0 && pre.length === 0) return { ver: { major: 0, minor: 0, patch: 0, pre: [] }, partial: 0 };
172
- return {
173
- ver: { major: nums[0] ?? 0, minor: nums[1] ?? 0, patch: nums[2] ?? 0, pre },
174
- partial
175
- };
176
- }
177
- function cmpToBool(cmp, op) {
178
- switch (op) {
179
- case ">=":
180
- return cmp >= 0;
181
- case ">":
182
- return cmp > 0;
183
- case "<":
184
- return cmp < 0;
185
- case "<=":
186
- return cmp <= 0;
187
- case "=":
188
- return cmp === 0;
189
- }
190
- }
191
- function testComparator(v, versionStr, c) {
192
- const full = `${v.major}.${v.minor}.${v.patch}` + (v.pre.length ? `-${v.pre.join(".")}` : "");
193
- if (c.upper) {
194
- const cmpLo = compareVersions(full, `${c.ver.major}.${c.ver.minor}.${c.ver.patch}` + (c.ver.pre.length ? `-${c.ver.pre.join(".")}` : ""));
195
- if (!(cmpLo >= 0)) return false;
196
- const cmpHi = compareVersions(full, `${c.upper.major}.${c.upper.minor}.${c.upper.patch}`);
197
- if (!(cmpHi < 0)) return false;
198
- return true;
199
- }
200
- const cmp = compareVersions(full, `${c.ver.major}.${c.ver.minor}.${c.ver.patch}` + (c.ver.pre.length ? `-${c.ver.pre.join(".")}` : ""));
201
- const baseOk = cmpToBool(cmp, c.op);
202
- if (c.ver.pre.length > 0 && v.pre.length === 0 && (c.op === ">" || c.op === "<")) {
203
- return false;
204
- }
205
- return baseOk;
206
- }
207
-
208
- // src/runtime/index.ts
209
- var DEFAULT_TIMEOUT = 15e3;
210
- var DEFAULT_RETRIES = 2;
211
- var BREAKER_THRESHOLD = 5;
212
- var BREAKER_RESET_MS = 3e4;
213
- var BACKOFF_MAX_MS = 4e3;
214
- var BACKOFF_JITTER = 0.2;
215
- function createRuntime() {
216
- const shareScopeMap2 = /* @__PURE__ */ Object.create(null);
217
- const remotes = /* @__PURE__ */ new Map();
218
- const plugins = [];
219
- const loadedModules = /* @__PURE__ */ new Map();
220
- const remoteManifests = /* @__PURE__ */ new Map();
221
- const stylesheetLoads = /* @__PURE__ */ new WeakMap();
222
- const containerInitScopes = /* @__PURE__ */ new WeakMap();
223
- const hooks = {};
224
- const applyPlugins = () => {
225
- for (const p of plugins) {
226
- try {
227
- p.init?.(hooks);
228
- } catch (err) {
229
- console.warn("[fulgurjs] runtimePlugin init failed:", p.name, err);
230
- }
231
- }
232
- };
233
- const getScope = (name) => shareScopeMap2[name] ??= /* @__PURE__ */ Object.create(null);
234
- function emitError(info) {
235
- try {
236
- hooks.onRemoteError?.(info);
237
- } catch {
238
- }
239
- if (typeof window !== "undefined") {
240
- window.dispatchEvent(new CustomEvent("fulgurjs:error", { detail: info }));
241
- }
242
- }
243
- function initSharing2(scopeName = "default") {
244
- getScope(scopeName);
245
- return shareScopeMap2;
246
- }
247
- function registerShare2(scopeName, name, version, get, opts = {}) {
248
- const scope = getScope(scopeName || "default");
249
- const own = Object.hasOwn(scope, name) ? scope[name] : void 0;
250
- let byName = own && typeof own === "object" ? own : void 0;
251
- if (!byName) {
252
- byName = /* @__PURE__ */ Object.create(null);
253
- scope[name] = byName;
254
- }
255
- if (Object.hasOwn(byName, version) && byName[version]) {
256
- return;
257
- }
258
- byName[version] = { version, get, from: opts.from ?? "unknown", eager: !!opts.eager, loaded: !!opts.loaded };
259
- }
260
- function assertRemoteParams(r) {
261
- const check = (v, name, int10) => {
262
- if (v === void 0) return;
263
- const ok = typeof v === "number" && (int10 ? Number.isInteger(v) && v >= 0 && v <= 10 : Number.isFinite(v) && v > 0);
264
- if (!ok) {
265
- throw new Error(
266
- `[fulgurjs] registerRemote("${r.name}"): ${name} must be a ${int10 ? "integer 0..10" : "finite positive number"}, got ${String(v)}`
267
- );
268
- }
269
- };
270
- check(r.timeout, "timeout");
271
- check(r.retries, "retries", true);
272
- if (r.breaker) {
273
- check(r.breaker.threshold, "breaker.threshold");
274
- check(r.breaker.resetMs, "breaker.resetMs");
275
- }
276
- }
277
- function registerRemotes2(list) {
278
- for (const r of list) {
279
- assertRemoteParams(r);
280
- const prev = remotes.get(r.name);
281
- remotes.set(r.name, {
282
- timeout: DEFAULT_TIMEOUT,
283
- retries: DEFAULT_RETRIES,
284
- ...prev,
285
- ...r,
286
- // 熔断参数随最新配置刷新;计数状态(fails/openUntil)跨注册保留——半开窗口不被重置
287
- breakerThreshold: r.breaker?.threshold ?? prev?.breakerThreshold ?? BREAKER_THRESHOLD,
288
- breakerResetMs: r.breaker?.resetMs ?? prev?.breakerResetMs ?? BREAKER_RESET_MS,
289
- state: prev?.state ?? "idle",
290
- breakerState: prev?.breakerState ?? { fails: 0, openUntil: 0 },
291
- entryInflight: prev?.entryInflight ?? /* @__PURE__ */ new Map(),
292
- debug: prev?.debug ?? { entry: r.entry, status: "idle" }
293
- });
294
- }
295
- }
296
- function registerRemote2(remote) {
297
- registerRemotes2([remote]);
298
- }
299
- function registerPlugins2(list) {
300
- plugins.push(...list);
301
- applyPlugins();
302
- }
303
- async function loadShare2(name, opts = {}) {
304
- const scopeName = opts.shareScope || "default";
305
- const shareKey = opts.shareKey || name;
306
- const scope = getScope(scopeName);
307
- const byName = scope[shareKey] || {};
308
- const versions = Object.keys(byName);
309
- const req = opts.requiredVersion;
310
- const satisfying = versions.filter(
311
- (v) => req === void 0 || req === false || satisfies(v, req)
312
- );
313
- let pick;
314
- if (opts.singleton && versions.length > 0) {
315
- const loadedVersions = versions.filter((v) => byName[v].loaded);
316
- pick = [...loadedVersions.length ? loadedVersions : versions].sort(compareVersions).pop();
317
- if (versions.length > 1 || !satisfying.includes(pick)) {
318
- const entry0 = byName[pick];
319
- const msg = `singleton skew "${shareKey}": req "${req ?? "any"}" use ${pick} from ${entry0.from}`;
320
- if (opts.strictVersion) {
321
- const err = new FgError(ErrorCodes.SHARE_STRICT_VERSION, msg, {
322
- shareKey,
323
- requiredVersion: req
324
- });
325
- emitError({ remote: entry0.from, error: err });
326
- throw err;
327
- }
328
- console.warn(`[fulgurjs:MFU-010] ${msg}`);
329
- }
330
- } else {
331
- pick = [...satisfying].sort(compareVersions).pop();
332
- if (!pick) {
333
- if (opts.strictVersion) {
334
- const err2 = new FgError(
335
- ErrorCodes.SHARE_STRICT_VERSION,
336
- `no satisfying version for "${shareKey}" (required "${req}") in share scope "${scopeName}"`
337
- );
338
- emitError({ remote: shareKey, error: err2 });
339
- throw err2;
340
- }
341
- if (opts.fallback) return opts.fallback();
342
- const err = new FgError(
343
- ErrorCodes.SHARE_NOT_AVAILABLE,
344
- `shared "${shareKey}" (${req ?? "any"}) unavailable in scope "${scopeName}" (no local fallback)`,
345
- { shareKey, requiredVersion: req }
346
- );
347
- emitError({ remote: shareKey, error: err });
348
- throw err;
349
- }
350
- }
351
- let entry = byName[pick];
352
- if (hooks.resolveShare) {
353
- const picked = await hooks.resolveShare({
354
- shareKey,
355
- shareScope: scopeName,
356
- requiredVersion: req,
357
- picked: entry,
358
- available: Object.values(byName)
359
- });
360
- if (picked) entry = picked;
361
- }
362
- entry.loaded = true;
363
- return entry.get();
364
- }
365
- function sanitizeUrl(u) {
366
- try {
367
- const x = new URL(u, typeof location < "u" ? location.href : "http://f.invalid");
368
- return x.protocol + "//" + x.host + x.pathname;
369
- } catch {
370
- return String(u).replace(/[?#].*$/, "");
371
- }
372
- }
373
- function withTimeout(p, ms, label) {
374
- return new Promise((resolve, reject) => {
375
- const t = setTimeout(() => reject(new Error(`timeout after ${ms}ms: ${label}`)), ms);
376
- p.then(
377
- (v) => {
378
- clearTimeout(t);
379
- resolve(v);
380
- },
381
- (e) => {
382
- clearTimeout(t);
383
- reject(e);
384
- }
385
- );
386
- });
387
- }
388
- const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
389
- function validateContainerInterface(container, remote) {
390
- if (!container || typeof container.get !== "function") {
391
- throw new Error(
392
- `remoteEntry of "${remote.name}" does not export a container interface (get/init)`
393
- );
394
- }
395
- if (container.name && remote.name && container.name !== remote.name) {
396
- const msg = `remoteEntry self-reported name "${container.name}" mismatches configured name "${remote.name}"`;
397
- if (remote.promise) {
398
- console.warn(`[fulgurjs] ${msg} (promise-based remote, continuing)`);
399
- } else {
400
- throw Object.assign(new Error(msg), { code: ErrorCodes.REMOTE_NAME_MISMATCH });
401
- }
402
- }
403
- }
404
- async function importEntry(url, remote) {
405
- const cached = remote.entryInflight.get(url);
406
- const inflight = cached ?? import(
407
- /* @vite-ignore */
408
- url
409
- );
410
- if (!cached) {
411
- remote.entryInflight.set(url, inflight);
412
- inflight.catch(() => remote.entryInflight.delete(url));
413
- }
414
- return withTimeout(inflight, remote.timeout ?? DEFAULT_TIMEOUT, `load remoteEntry ${url}`);
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 FgError(
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 FgError(
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 withTimeout(
446
- remote.promise(),
447
- remote.timeout ?? DEFAULT_TIMEOUT,
448
- `resolve promise remote "${remote.name}"`
449
- );
450
- const container2 = typeof resolved === "string" ? await importEntry(resolved, remote) : resolved;
451
- validateContainerInterface(container2, remote);
452
- return container2;
453
- });
454
- } else {
455
- for (const url of [remote.entry, ...remote.fallback ?? []]) {
456
- loaders.push(async () => {
457
- const container2 = await importEntry(url, remote);
458
- validateContainerInterface(container2, remote);
459
- return container2;
460
- });
461
- }
462
- }
463
- let container;
464
- let lastErr;
465
- let succeeded = false;
466
- const maxRetries = overrides?.retries ?? remote.retries ?? DEFAULT_RETRIES;
467
- for (const loader of loaders) {
468
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
469
- const start = performance.now();
470
- try {
471
- container = await loader();
472
- remote.lastLoadMs = Math.round(performance.now() - start);
473
- remote.debug.lastLoadMs = remote.lastLoadMs;
474
- succeeded = true;
475
- break;
476
- } catch (err) {
477
- lastErr = err;
478
- if (err?.code) throw err;
479
- if (attempt < maxRetries) {
480
- await sleep(Math.min(BACKOFF_MAX_MS, 200 * 2 ** attempt) * (1 - BACKOFF_JITTER * (1 - 2 * Math.random())));
481
- }
482
- }
483
- }
484
- if (succeeded) break;
485
- }
486
- if (!succeeded || !container) {
487
- const shown = sanitizeUrl(remote.entry);
488
- const hints = [];
489
- if (/^https?:\/\//.test(remote.entry) && typeof location !== "undefined") {
490
- hints.push(`1. is the remote dev server running? open ${shown} in a browser \u2014 it must return JS, not HTML`);
491
- hints.push(`2. is the URL correct in federation({ remotes })? dev/prod entries can differ`);
492
- hints.push(`3. CORS: the remote dev server must allow cross-origin requests (check its server.cors)`);
493
- } else {
494
- hints.push(`1. is the remote deployed? open ${shown} in a browser \u2014 it must return JS`);
495
- hints.push(`2. NGINX/CDN routing: the entry path must serve the remoteEntry JS (check try_files)`);
496
- }
497
- throw new FgError(
498
- ErrorCodes.REMOTE_LOAD_FAILED,
499
- `failed to load remote "${remote.name}" from ${shown}: ${String(lastErr?.message ?? lastErr)}
500
- ` + hints.join("\n"),
501
- { remote: remote.name }
502
- );
503
- }
504
- validateContainerInterface(container, remote);
505
- const prevScope = containerInitScopes.get(container);
506
- if (prevScope && prevScope !== scopeKey) {
507
- throw new FgError(
508
- ErrorCodes.CONTAINER_REINIT_CONFLICT,
509
- `container "${remote.name}" already initialized with share scope "${prevScope}", cannot re-init with "${scopeKey}"`
510
- );
511
- }
512
- if (typeof container.init === "function") {
513
- await container.init(shareScopeMap2);
514
- }
515
- containerInitScopes.set(container, scopeKey);
516
- remote.container = container;
517
- remote.state = "loaded";
518
- remote.debug.status = "loaded";
519
- b.fails = 0;
520
- return container;
521
- })();
522
- remote.containerPromise = containerPromise;
523
- try {
524
- return await containerPromise;
525
- } catch (err) {
526
- remote.containerPromise = void 0;
527
- remote.state = "failed";
528
- remote.debug.status = "failed";
529
- remote.debug.error = String(err?.message ?? err);
530
- b.fails += 1;
531
- if (b.fails >= remote.breakerThreshold) {
532
- b.openUntil = Date.now() + remote.breakerResetMs;
533
- b.fails = 0;
534
- }
535
- if (err instanceof FgError) {
536
- emitError({ remote: remote.name, error: err });
537
- throw err;
538
- }
539
- if (err?.code) {
540
- emitError({ remote: remote.name, error: err });
541
- throw err;
542
- }
543
- const wrapped = new FgError(ErrorCodes.REMOTE_LOAD_FAILED, String(err?.message ?? err), {
544
- remote: remote.name
545
- });
546
- emitError({ remote: remote.name, error: wrapped });
547
- throw wrapped;
548
- }
549
- }
550
- function parseSpec2(spec) {
551
- const idx = spec.indexOf("/");
552
- if (idx === -1) return { remote: spec, module: "" };
553
- return { remote: spec.slice(0, idx), module: normalizeModuleName(spec.slice(idx + 1)) };
554
- }
555
- function normalizeModuleName(m) {
556
- if (!m) return "";
557
- return m.startsWith(".") ? m : `./${m}`;
558
- }
559
- async function loadRemote2(spec, opts) {
560
- const { remote: name, module } = parseSpec2(spec);
561
- try {
562
- hooks.beforeLoadRemote?.({ remote: name, module });
563
- } catch (hookErr) {
564
- console.warn("[fulgurjs] beforeLoadRemote hook error (ignored):", hookErr);
565
- }
566
- const remote = remotes.get(name);
567
- if (!remote) {
568
- throw new FgError(
569
- ErrorCodes.REMOTE_UNKNOWN,
570
- `unknown remote "${name}". Register it via federation({ remotes }) or registerRemote().`,
571
- { remote: name }
572
- );
573
- }
574
- let container;
575
- try {
576
- const stylesReady = module && remote.manifestUrl ? preloadRemote2(spec) : Promise.resolve();
577
- [container] = await Promise.all([acquireContainer(remote, { retries: opts?.retries }), stylesReady]);
578
- } catch (err) {
579
- if (opts?.fallbackModule) {
580
- console.error(`[fulgurjs] loadRemote("${spec}") failed; returning fallbackModule (\u663E\u5F0F\u964D\u7EA7\uFF0C\u9519\u8BEF\u5DF2\u900F\u51FA)`, err);
581
- emitError({ remote: name, error: err });
582
- return await opts.fallbackModule();
583
- }
584
- throw err;
585
- }
586
- if (!module) return container;
587
- const cacheKey = `${name}@${remote.shareScope || "default"}#${module}`;
588
- if (!loadedModules.has(cacheKey)) {
589
- loadedModules.set(
590
- cacheKey,
591
- container.get(module).catch((err) => {
592
- loadedModules.delete(cacheKey);
593
- if (err instanceof FgError || err?.code) throw err;
594
- throw new FgError(
595
- ErrorCodes.REMOTE_LOAD_FAILED,
596
- `failed to load module "${module}" from remote "${name}": ${String(err?.message ?? err)}`,
597
- { remote: name, module }
598
- );
599
- })
600
- );
601
- }
602
- let ns;
603
- try {
604
- ns = await loadedModules.get(cacheKey);
605
- } catch (err) {
606
- if (opts?.fallbackModule) {
607
- console.error(`[fulgurjs] loadRemote("${spec}") failed; returning fallbackModule (\u663E\u5F0F\u964D\u7EA7\uFF0C\u9519\u8BEF\u5DF2\u900F\u51FA)`, err);
608
- emitError({ remote: name, error: err });
609
- return await opts.fallbackModule();
610
- }
611
- throw err;
612
- }
613
- try {
614
- hooks.afterLoadRemote?.({ remote: name, module, module_ns: ns });
615
- } catch (hookErr) {
616
- console.warn("[fulgurjs] afterLoadRemote hook error (ignored):", hookErr);
617
- }
618
- if (ns && typeof ns === "object" && Object.keys(ns).length === 0) {
619
- const err = new FgError(
620
- ErrorCodes.EMPTY_EXPORTS,
621
- `no exports: ${module} @ ${name}`,
622
- { remote: name, module }
623
- );
624
- emitError({ remote: name, error: err });
625
- console.error(`[fulgurjs:MFU-009] ${err.message}`);
626
- }
627
- return ns;
628
- }
629
- function getContainer2(name) {
630
- const remote = remotes.get(name);
631
- if (!remote) {
632
- throw new FgError(ErrorCodes.REMOTE_UNKNOWN, `unknown remote "${name}"`, { remote: name });
633
- }
634
- return acquireContainer(remote);
635
- }
636
- async function preloadRemote2(spec, opts = {}) {
637
- const { remote: name, module } = parseSpec2(spec);
638
- const remote = remotes.get(name);
639
- if (!remote) {
640
- throw new FgError(ErrorCodes.REMOTE_UNKNOWN, `unknown remote "${name}"`, { remote: name });
641
- }
642
- const mode = opts.mode ?? "preload";
643
- const waitForStylesheet = (link, href) => {
644
- if (link.sheet) return Promise.resolve();
645
- const pending = stylesheetLoads.get(link);
646
- if (pending) return pending;
647
- const loaded = new Promise((resolve) => {
648
- const finish = () => {
649
- link.removeEventListener("load", finish);
650
- link.removeEventListener("error", failed);
651
- resolve();
652
- };
653
- const failed = () => {
654
- emitError({
655
- remote: name,
656
- error: new FgError(ErrorCodes.PRELOAD_FAILED, `failed to load remote stylesheet ${href}`, {
657
- remote: name
658
- })
659
- });
660
- finish();
661
- };
662
- link.addEventListener("load", finish, { once: true });
663
- link.addEventListener("error", failed, { once: true });
664
- if (link.sheet) finish();
665
- });
666
- stylesheetLoads.set(link, loaded);
667
- return loaded;
668
- };
669
- const inject = (href, as) => {
670
- if (typeof document === "undefined") return Promise.resolve();
671
- const existing = document.querySelector(`link[href="${href}"]`);
672
- if (existing) {
673
- return as === "style" && mode === "preload" ? stylesheetLoads.get(existing) ?? Promise.resolve() : Promise.resolve();
674
- }
675
- const link = document.createElement("link");
676
- link.rel = as === "style" ? "stylesheet" : "modulepreload";
677
- link.href = href;
678
- if (mode === "prefetch") {
679
- ;
680
- link.fetchPriority = "low";
681
- }
682
- const loaded = as === "style" ? waitForStylesheet(link, href) : Promise.resolve();
683
- document.head.appendChild(link);
684
- return as === "style" && mode === "preload" ? loaded : Promise.resolve();
685
- };
686
- const resolveAssetUrl = (asset) => {
687
- const pageUrl = typeof document !== "undefined" ? document.baseURI : void 0;
688
- const base = new URL(remote.entry, pageUrl || "http://fulgurjs.invalid/");
689
- if (!base.pathname.endsWith("/") && !base.pathname.split("/").pop().includes(".")) {
690
- base.pathname += "/";
691
- }
692
- return new URL(asset, base).href;
693
- };
694
- try {
695
- if (remote.manifestUrl) {
696
- let manifestPromise = remoteManifests.get(remote.manifestUrl);
697
- if (!manifestPromise) {
698
- manifestPromise = fetch(remote.manifestUrl, {
699
- cache: "no-cache",
700
- signal: AbortSignal.timeout(8e3)
701
- }).then((res) => res.ok ? res.json() : void 0);
702
- remoteManifests.set(remote.manifestUrl, manifestPromise);
703
- manifestPromise.then(
704
- (manifest2) => {
705
- if (!manifest2) remoteManifests.delete(remote.manifestUrl);
706
- },
707
- () => remoteManifests.delete(remote.manifestUrl)
708
- );
709
- }
710
- const manifest = await manifestPromise;
711
- if (manifest) {
712
- const sv = manifest.schemaVersion;
713
- if (typeof sv === "number" && (!Number.isInteger(sv) || sv > 1)) {
714
- emitError({
715
- remote: name,
716
- error: new FgError(ErrorCodes.PRELOAD_FAILED, `unsupported manifest schemaVersion ${String(sv)} (expected 1)`, {
717
- remote: name
718
- })
719
- });
720
- await inject(remote.entry, "modulepreload");
721
- return;
722
- }
723
- await inject(remote.entry, "modulepreload");
724
- const exposes = manifest.exposes;
725
- const entries = Array.isArray(exposes) ? exposes.filter((item) => item && typeof item === "object").map((item) => [String(item.name ?? ""), item]) : exposes && typeof exposes === "object" ? Object.entries(exposes) : [];
726
- const selected = module ? entries.filter(([key, item]) => normalizeModuleName(String(item?.name ?? key)) === module) : entries;
727
- const cssLoads = [];
728
- for (const [, item] of selected) {
729
- if (typeof item?.file === "string") {
730
- await inject(resolveAssetUrl(item.file), "modulepreload");
731
- }
732
- if (Array.isArray(item?.css)) {
733
- for (const css of item.css) {
734
- if (typeof css !== "string") continue;
735
- const loading = inject(resolveAssetUrl(css), "style");
736
- if (mode === "preload") cssLoads.push(loading);
737
- }
738
- }
739
- }
740
- await Promise.all(cssLoads);
741
- return;
742
- }
743
- }
744
- await inject(remote.entry, "modulepreload");
745
- } catch (err) {
746
- emitError({
747
- remote: name,
748
- error: new FgError(ErrorCodes.PRELOAD_FAILED, String(err?.message ?? err), {
749
- remote: name
750
- })
751
- });
752
- }
753
- }
754
- const attachDebug = () => {
755
- if (typeof window === "undefined") return;
756
- window.__FULGURJS_SCOPE__ = shareScopeMap2;
757
- window.__FULGURJS_INFO__ = {
758
- get remotes() {
759
- const out = {};
760
- for (const [k, v] of remotes) {
761
- out[k] = { ...v.debug, entry: v.entry };
762
- }
763
- return out;
764
- },
765
- errors: []
766
- };
767
- };
768
- attachDebug();
769
- return {
770
- shareScopeMap: shareScopeMap2,
771
- initSharing: initSharing2,
772
- registerShare: registerShare2,
773
- registerRemotes: registerRemotes2,
774
- registerRemote: registerRemote2,
775
- registerPlugins: registerPlugins2,
776
- loadShare: loadShare2,
777
- loadRemote: loadRemote2,
778
- getContainer: getContainer2,
779
- preloadRemote: preloadRemote2,
780
- parseSpec: parseSpec2
781
- };
782
- }
783
- var g = globalThis;
784
- var runtime = g.__FULGURJS_RUNTIME__ ?? createRuntime();
785
- g.__FULGURJS_RUNTIME__ = runtime;
786
- try {
787
- Object.freeze(runtime);
788
- } catch {
789
- }
790
- var initSharing = runtime.initSharing;
791
- var registerShare = runtime.registerShare;
792
- var registerRemotes = runtime.registerRemotes;
793
- var registerRemote = runtime.registerRemote;
794
- var registerPlugins = runtime.registerPlugins;
795
- var loadShare = runtime.loadShare;
796
- var loadRemote = runtime.loadRemote;
797
- var getContainer = runtime.getContainer;
798
- var preloadRemote = runtime.preloadRemote;
799
- var parseSpec = runtime.parseSpec;
800
- var shareScopeMap = runtime.shareScopeMap;
801
-
802
- // src/vue.ts
803
- var ERROR_STYLE = {
804
- padding: "16px",
805
- border: "1px solid #fde2e2",
806
- borderRadius: "4px",
807
- background: "#fef0f0",
808
- color: "#c45656",
809
- fontSize: "13px",
810
- lineHeight: "1.6"
811
- };
812
- var RemoteErrorPlaceholder = defineComponent({
813
- name: "FulgurjsRemoteError",
814
- props: {
815
- error: { type: Object, default: void 0 }
816
- },
817
- setup(props) {
818
- return () => {
819
- const err = props.error;
820
- const code = err?.code ?? "UNKNOWN";
821
- const message = err?.message ?? String(props.error ?? "unknown error");
822
- return h("div", { style: ERROR_STYLE }, [
823
- h("p", { style: "margin:0 0 4px;font-weight:600" }, `\u8FDC\u7A0B\u7EC4\u4EF6\u52A0\u8F7D\u5931\u8D25\uFF08\u9519\u8BEF\u7801 ${code}\uFF09`),
824
- h("p", { style: "margin:0 0 8px;word-break:break-all" }, message),
825
- h(
826
- "p",
827
- { style: "margin:0" },
828
- "\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"
829
- )
830
- ]);
831
- };
832
- }
833
- });
834
- function remoteComponent(spec, opts = {}) {
835
- return defineAsyncComponent({
836
- loader: () => loadRemote(spec, { retries: opts.retries }).then((m) => m.default ?? m),
837
- loadingComponent: opts.loadingComponent,
838
- errorComponent: opts.errorComponent ?? RemoteErrorPlaceholder,
839
- delay: opts.delay,
840
- timeout: opts.timeout
841
- });
842
- }
843
- export {
844
- remoteComponent
845
- };
1
+ import { loadRemote } from "./runtime.js";
2
+ import { createRemoteComponent, createHostPages as createHostPagesWithLoader } from "./vue-adapter.js";
3
+ export const remoteComponent = createRemoteComponent(loadRemote);
4
+ export const createHostPages = (options) => createHostPagesWithLoader(options, loadRemote);