@fulgurjs/federation 5.2.5 → 5.3.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.
Files changed (101) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/DESIGN.md +7 -0
  3. package/README.en.md +86 -4
  4. package/README.md +125 -3
  5. package/dist/bridge-app-react.cjs +185 -0
  6. package/dist/bridge-app-react.d.cts +37 -0
  7. package/dist/bridge-app-react.d.ts +37 -0
  8. package/dist/bridge-app-react.js +115 -0
  9. package/dist/bridge-app-vue.cjs +111 -0
  10. package/dist/bridge-app-vue.d.cts +34 -0
  11. package/dist/bridge-app-vue.d.ts +34 -0
  12. package/dist/bridge-app-vue.js +51 -0
  13. package/dist/bridge-core-6uLJiMVa.d.ts +42 -0
  14. package/dist/bridge-core.cjs +224 -0
  15. package/dist/bridge-core.d.cts +51 -0
  16. package/dist/bridge-core.d.ts +51 -0
  17. package/dist/bridge-core.js +21 -0
  18. package/dist/bridge-errors.cjs +114 -0
  19. package/dist/bridge-errors.d.cts +36 -0
  20. package/dist/bridge-errors.d.ts +36 -0
  21. package/dist/bridge-errors.js +15 -0
  22. package/dist/bridge-host-react-C55RmGll.d.ts +35 -0
  23. package/dist/bridge-host-react.cjs +392 -0
  24. package/dist/bridge-host-react.d.cts +45 -0
  25. package/dist/bridge-host-react.d.ts +45 -0
  26. package/dist/bridge-host-react.js +183 -0
  27. package/dist/bridge-host-vue-BAxDKqGH.d.ts +32 -0
  28. package/dist/bridge-host-vue.cjs +426 -0
  29. package/dist/bridge-host-vue.d.cts +39 -0
  30. package/dist/bridge-host-vue.d.ts +39 -0
  31. package/dist/bridge-host-vue.js +211 -0
  32. package/dist/bridge-react.d.ts +12 -0
  33. package/dist/bridge-react.js +3 -0
  34. package/dist/bridge-vue.d.ts +8 -0
  35. package/dist/bridge-vue.js +3 -0
  36. package/dist/bridge.d.ts +16 -0
  37. package/dist/bridge.js +5 -0
  38. package/dist/chunk-E55P4TIY.js +76 -0
  39. package/dist/chunk-EH34L46P.js +207 -0
  40. package/dist/chunk-FVYGSLAQ.js +179 -0
  41. package/dist/chunk-LWJBHUJQ.js +114 -0
  42. package/dist/chunk-OF2JCXTZ.js +15 -0
  43. package/dist/chunk-TSYLDY6H.js +64 -0
  44. package/dist/chunk-UJFCPQGN.js +1210 -0
  45. package/dist/context.js +8 -64
  46. package/dist/index.cjs +84 -8
  47. package/dist/index.js +84 -8
  48. package/dist/react.d.ts +44 -2
  49. package/dist/react.js +2 -0
  50. package/dist/runtime-entry.d.ts +41 -2
  51. package/dist/runtime-entry.js +1 -1
  52. package/dist/runtime.js +1 -1
  53. package/dist/vue.js +2 -0
  54. package/docs/webpack-mf-/345/257/271/347/205/247/344/270/216/347/274/272/345/217/243.md +1 -1
  55. package/docs//350/277/201/347/247/273/346/214/207/345/215/227.md +1 -1
  56. package/examples/README.en.md +20 -0
  57. package/examples/README.md +20 -0
  58. package/examples/bridge/README.md +36 -0
  59. package/examples/bridge/react-host/README.md +23 -0
  60. package/examples/bridge/react-host/fulgurjs.config.ts +20 -0
  61. package/examples/bridge/react-host/index.html +11 -0
  62. package/examples/bridge/react-host/package-lock.json +2457 -0
  63. package/examples/bridge/react-host/package.json +24 -0
  64. package/examples/bridge/react-host/src/env.d.ts +7 -0
  65. package/examples/bridge/react-host/src/host-session.ts +26 -0
  66. package/examples/bridge/react-host/src/main.tsx +62 -0
  67. package/examples/bridge/react-host/tsconfig.json +13 -0
  68. package/examples/bridge/react-host/vite.config.ts +9 -0
  69. package/examples/bridge/react-remote/README.md +20 -0
  70. package/examples/bridge/react-remote/fulgurjs.config.ts +17 -0
  71. package/examples/bridge/react-remote/index.html +11 -0
  72. package/examples/bridge/react-remote/package-lock.json +2382 -0
  73. package/examples/bridge/react-remote/package.json +24 -0
  74. package/examples/bridge/react-remote/src/bridge.tsx +66 -0
  75. package/examples/bridge/react-remote/src/env.d.ts +7 -0
  76. package/examples/bridge/react-remote/src/main.tsx +7 -0
  77. package/examples/bridge/react-remote/tsconfig.json +13 -0
  78. package/examples/bridge/react-remote/vite.config.ts +9 -0
  79. package/examples/bridge/vue-host/README.md +24 -0
  80. package/examples/bridge/vue-host/fulgurjs.config.ts +22 -0
  81. package/examples/bridge/vue-host/index.html +11 -0
  82. package/examples/bridge/vue-host/package-lock.json +1885 -0
  83. package/examples/bridge/vue-host/package.json +24 -0
  84. package/examples/bridge/vue-host/src/App.vue +70 -0
  85. package/examples/bridge/vue-host/src/env.d.ts +7 -0
  86. package/examples/bridge/vue-host/src/host-session.ts +29 -0
  87. package/examples/bridge/vue-host/src/main.ts +4 -0
  88. package/examples/bridge/vue-host/tsconfig.json +13 -0
  89. package/examples/bridge/vue-host/vite.config.ts +9 -0
  90. package/examples/bridge/vue-remote/README.md +19 -0
  91. package/examples/bridge/vue-remote/fulgurjs.config.ts +16 -0
  92. package/examples/bridge/vue-remote/index.html +11 -0
  93. package/examples/bridge/vue-remote/package-lock.json +1856 -0
  94. package/examples/bridge/vue-remote/package.json +21 -0
  95. package/examples/bridge/vue-remote/src/App.vue +12 -0
  96. package/examples/bridge/vue-remote/src/bridge.ts +57 -0
  97. package/examples/bridge/vue-remote/src/env.d.ts +7 -0
  98. package/examples/bridge/vue-remote/src/main.ts +15 -0
  99. package/examples/bridge/vue-remote/tsconfig.json +13 -0
  100. package/examples/bridge/vue-remote/vite.config.ts +9 -0
  101. package/package.json +43 -2
@@ -0,0 +1,1210 @@
1
+ // src/semver.ts
2
+ var VERSION_RE = /^v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-((?:[0-9A-Za-z-]+)(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z.-]+)?$/;
3
+ function parseVersion(input) {
4
+ const m = VERSION_RE.exec(String(input).trim());
5
+ if (!m) return null;
6
+ return {
7
+ major: Number(m[1]),
8
+ minor: Number(m[2] ?? 0),
9
+ patch: Number(m[3] ?? 0),
10
+ pre: m[4] ? m[4].split(".") : []
11
+ };
12
+ }
13
+ function preCompare(a, b) {
14
+ if (a.length === 0 && b.length === 0) return 0;
15
+ if (a.length === 0) return 1;
16
+ if (b.length === 0) return -1;
17
+ const len = Math.max(a.length, b.length);
18
+ for (let i = 0; i < len; i++) {
19
+ const x = a[i];
20
+ const y = b[i];
21
+ if (x === void 0) return -1;
22
+ if (y === void 0) return 1;
23
+ const xn = /^\d+$/.test(x);
24
+ const yn = /^\d+$/.test(y);
25
+ if (xn && yn) {
26
+ const d = Number(x) - Number(y);
27
+ if (d !== 0) return d;
28
+ } else if (xn !== yn) {
29
+ return xn ? -1 : 1;
30
+ } else if (x !== y) {
31
+ return x < y ? -1 : 1;
32
+ }
33
+ }
34
+ return 0;
35
+ }
36
+ function compareVersions(a, b) {
37
+ const va = parseVersion(a);
38
+ const vb = parseVersion(b);
39
+ if (!va || !vb) return a < b ? -1 : a > b ? 1 : 0;
40
+ if (va.major !== vb.major) return va.major - vb.major;
41
+ if (va.minor !== vb.minor) return va.minor - vb.minor;
42
+ if (va.patch !== vb.patch) return va.patch - vb.patch;
43
+ return preCompare(va.pre, vb.pre);
44
+ }
45
+ var ANY_RANGE = /^[a-z]+[+:-]/i;
46
+ function satisfies(version, range) {
47
+ if (range === false || range == null || range === "") return true;
48
+ const rangeStr = String(range).trim();
49
+ if (!rangeStr || rangeStr === "*" || rangeStr === "latest" || ANY_RANGE.test(rangeStr)) return true;
50
+ return rangeStr.split("||").some((part) => satisfiesSet(version, part.trim()));
51
+ }
52
+ function satisfiesSet(version, set) {
53
+ if (!set || set === "*") return !parseVersion(version)?.pre.length;
54
+ const hyphen = set.split(/\s+-\s+/);
55
+ if (hyphen.length === 2) {
56
+ const lo = hyphen[0].trim();
57
+ const hi = hyphen[1].trim();
58
+ if (!satisfies(version, `>=${lo}`)) return false;
59
+ if (!satisfies(version, `<=${hi}`)) return false;
60
+ return true;
61
+ }
62
+ const v = parseVersion(version);
63
+ if (!v) return false;
64
+ const comps = parseComparators(set);
65
+ if (comps === null) return true;
66
+ for (const c of comps) {
67
+ if (!testComparator(v, version, c)) return false;
68
+ }
69
+ if (v.pre.length > 0 && !comps.some((c) => c.ver.pre.length > 0)) {
70
+ const tupleMatch = comps.some(
71
+ (c) => c.ver.major === v.major && c.ver.minor === v.minor && c.ver.patch === v.patch
72
+ );
73
+ if (!tupleMatch) return false;
74
+ }
75
+ return true;
76
+ }
77
+ function parseComparators(set) {
78
+ const comps = [];
79
+ const tokens = set.trim().split(/\s+/).filter(Boolean);
80
+ if (tokens.length === 0) return null;
81
+ for (const token of tokens) {
82
+ const m = /^(>=|<=|>|<|=|\^|~)?(.*)$/.exec(token);
83
+ if (!m) return null;
84
+ const op = m[1];
85
+ const raw = m[2];
86
+ const c = buildComparator(op, raw);
87
+ if (c === null) return null;
88
+ comps.push(...c);
89
+ }
90
+ return comps;
91
+ }
92
+ function buildComparator(op, raw) {
93
+ const parsed = parseLoose(raw);
94
+ if (!parsed) return null;
95
+ const { ver, partial } = parsed;
96
+ switch (op) {
97
+ case "^": {
98
+ const upper = caretUpper(ver);
99
+ if (!upper) return null;
100
+ return [{ op: ">=", ver }, { op: "<", ver: upper }];
101
+ }
102
+ case "~": {
103
+ const upper = partial >= 2 ? { major: ver.major, minor: ver.minor + 1, patch: 0, pre: [] } : { major: ver.major + 1, minor: 0, patch: 0, pre: [] };
104
+ return [{ op: ">=", ver }, { op: "<", ver: upper }];
105
+ }
106
+ case ">=":
107
+ return [{ op: ">=", ver }];
108
+ case ">":
109
+ if (partial === 1) return [{ op: ">=", ver: { major: ver.major + 1, minor: 0, patch: 0, pre: [] } }];
110
+ if (partial === 2)
111
+ return [{ op: ">=", ver: { major: ver.major, minor: ver.minor + 1, patch: 0, pre: [] } }];
112
+ return [{ op: ">", ver }];
113
+ case "<":
114
+ if (partial === 1) return [{ op: "<", ver: { major: ver.major, minor: 0, patch: 0, pre: [] } }];
115
+ if (partial === 2)
116
+ return [{ op: "<", ver: { major: ver.major, minor: ver.minor, patch: 0, pre: [] } }];
117
+ return [{ op: "<", ver }];
118
+ case "<=":
119
+ return [{ op: "<=", ver }];
120
+ case "=":
121
+ case void 0:
122
+ if (partial === 1)
123
+ return [
124
+ { op: ">=", ver },
125
+ { op: "<", ver: { major: ver.major + 1, minor: 0, patch: 0, pre: [] } }
126
+ ];
127
+ if (partial === 2)
128
+ return [
129
+ { op: ">=", ver },
130
+ { op: "<", ver: { major: ver.major, minor: ver.minor + 1, patch: 0, pre: [] } }
131
+ ];
132
+ return [{ op: "=", ver }];
133
+ default:
134
+ return null;
135
+ }
136
+ }
137
+ function caretUpper(ver) {
138
+ if (ver.major > 0) return { major: ver.major + 1, minor: 0, patch: 0, pre: [] };
139
+ if (ver.minor > 0) return { major: 0, minor: ver.minor + 1, patch: 0, pre: [] };
140
+ return { major: 0, minor: 0, patch: ver.patch + 1, pre: [] };
141
+ }
142
+ function parseLoose(raw) {
143
+ const cleaned = raw.replace(/^v/, "").trim();
144
+ if (!cleaned || cleaned === "*" || cleaned === "x" || cleaned === "X") {
145
+ return { ver: { major: 0, minor: 0, patch: 0, pre: [] }, partial: 0 };
146
+ }
147
+ const parts = cleaned.split(".");
148
+ const nums = [];
149
+ let partial = 0;
150
+ for (const p of parts.slice(0, 3)) {
151
+ if (/^\d+$/.test(p)) {
152
+ nums.push(Number(p));
153
+ partial++;
154
+ } else if (p === "x" || p === "X" || p === "*") {
155
+ break;
156
+ } else {
157
+ break;
158
+ }
159
+ }
160
+ const rest = cleaned.split("-");
161
+ const pre = rest.length > 1 ? rest.slice(1).join("-").split("+")[0].split(".") : [];
162
+ if (partial === 0 && pre.length === 0) return { ver: { major: 0, minor: 0, patch: 0, pre: [] }, partial: 0 };
163
+ return {
164
+ ver: { major: nums[0] ?? 0, minor: nums[1] ?? 0, patch: nums[2] ?? 0, pre },
165
+ partial
166
+ };
167
+ }
168
+ function cmpToBool(cmp, op) {
169
+ switch (op) {
170
+ case ">=":
171
+ return cmp >= 0;
172
+ case ">":
173
+ return cmp > 0;
174
+ case "<":
175
+ return cmp < 0;
176
+ case "<=":
177
+ return cmp <= 0;
178
+ case "=":
179
+ return cmp === 0;
180
+ }
181
+ }
182
+ function testComparator(v, versionStr, c) {
183
+ const full = `${v.major}.${v.minor}.${v.patch}` + (v.pre.length ? `-${v.pre.join(".")}` : "");
184
+ if (c.upper) {
185
+ const cmpLo = compareVersions(full, `${c.ver.major}.${c.ver.minor}.${c.ver.patch}` + (c.ver.pre.length ? `-${c.ver.pre.join(".")}` : ""));
186
+ if (!(cmpLo >= 0)) return false;
187
+ const cmpHi = compareVersions(full, `${c.upper.major}.${c.upper.minor}.${c.upper.patch}`);
188
+ if (!(cmpHi < 0)) return false;
189
+ return true;
190
+ }
191
+ const cmp = compareVersions(full, `${c.ver.major}.${c.ver.minor}.${c.ver.patch}` + (c.ver.pre.length ? `-${c.ver.pre.join(".")}` : ""));
192
+ const baseOk = cmpToBool(cmp, c.op);
193
+ if (c.ver.pre.length > 0 && v.pre.length === 0 && (c.op === ">" || c.op === "<")) {
194
+ return false;
195
+ }
196
+ return baseOk;
197
+ }
198
+
199
+ // src/runtime/errors.ts
200
+ var FgError = class extends Error {
201
+ code;
202
+ details;
203
+ constructor(code, message, details, options) {
204
+ super(`[fulgurjs:${code}] ${message}`, options);
205
+ this.name = "FgError";
206
+ this.code = code;
207
+ this.details = details;
208
+ }
209
+ };
210
+ var ErrorCodes = {
211
+ /** 远程容器加载失败(网络/超时/重试耗尽/熔断) */
212
+ REMOTE_LOAD_FAILED: "MFU-001",
213
+ /** remoteEntry 自报名与 remotes 配置的名称不匹配 */
214
+ REMOTE_NAME_MISMATCH: "MFU-002",
215
+ /** strictVersion 开启且共享版本不满足 */
216
+ SHARE_STRICT_VERSION: "MFU-003",
217
+ /** 共享模块缺失且无本地 fallback(import: false) */
218
+ SHARE_NOT_AVAILABLE: "MFU-004",
219
+ /** 同一容器用不同 share scope 重复 init */
220
+ CONTAINER_REINIT_CONFLICT: "MFU-005",
221
+ /** 请求的模块未被该远程 exposes */
222
+ MODULE_NOT_EXPOSED: "MFU-006",
223
+ /** 预加载失败 */
224
+ PRELOAD_FAILED: "MFU-007",
225
+ /** 未知远程 */
226
+ REMOTE_UNKNOWN: "MFU-008",
227
+ /** 加载到的模块没有任何导出(常见:exposes 指向了不导出内容的文件) */
228
+ EMPTY_EXPORTS: "MFU-009",
229
+ /** singleton 共享协商版本与消费方 requiredVersion 不一致(使用作用域版本,仅告警) */
230
+ SINGLETON_SKEW: "MFU-010",
231
+ /** setup 生命周期入口导出形态非法(默认导出不是函数,或具名 onSession 不是函数) */
232
+ SETUP_INVALID_EXPORT: "MFU-011",
233
+ /** setup/onSession 执行抛错(该次 loadRemote 拒绝;仅清失败阶段缓存,允许重试) */
234
+ SETUP_RUN_FAILED: "MFU-012",
235
+ /** 远程声明了 onSession 但宿主 AppContext 未提供 sessionKey(登录代次) */
236
+ SETUP_SESSION_KEY_MISSING: "MFU-013",
237
+ /** setup/onSession 执行的同步段内递归 loadRemote 同一远程 */
238
+ SETUP_RECURSION: "MFU-014"
239
+ };
240
+
241
+ // src/runtime/index.ts
242
+ var SETUP_META_KEY = "__fulgurjsSetup";
243
+ var APP_CONTEXT_GLOBAL_KEY = "__FULGURJS_APP_CONFIG__";
244
+ var DEFAULT_TIMEOUT = 15e3;
245
+ var DEFAULT_RETRIES = 2;
246
+ var BREAKER_THRESHOLD = 5;
247
+ var BREAKER_RESET_MS = 3e4;
248
+ var BACKOFF_MAX_MS = 4e3;
249
+ var BACKOFF_JITTER = 0.2;
250
+ function createRuntime() {
251
+ const shareScopeMap2 = /* @__PURE__ */ Object.create(null);
252
+ const remotes = /* @__PURE__ */ new Map();
253
+ const plugins = [];
254
+ const reportedSingletonSkews = /* @__PURE__ */ new Set();
255
+ const loadedModules = /* @__PURE__ */ new Map();
256
+ const remoteManifests = /* @__PURE__ */ new Map();
257
+ const stylesheetLoads = /* @__PURE__ */ new WeakMap();
258
+ const containerInitScopes = /* @__PURE__ */ new WeakMap();
259
+ const hooks = {};
260
+ const applyPlugins = () => {
261
+ for (const p of plugins) {
262
+ try {
263
+ p.init?.(hooks);
264
+ } catch (err) {
265
+ console.warn("[fulgurjs] \u8FD0\u884C\u65F6\u6269\u5C55\u521D\u59CB\u5316\u5931\u8D25\uFF0C\u8BE5\u6269\u5C55\u5C06\u4E0D\u53EF\u7528\uFF1A", p.name, err);
266
+ }
267
+ }
268
+ };
269
+ const lifecycleStates = /* @__PURE__ */ new Map();
270
+ let lifecycleSyncRemote;
271
+ const lifecycleOf = (name) => {
272
+ let st = lifecycleStates.get(name);
273
+ if (!st) lifecycleStates.set(name, st = {});
274
+ return st;
275
+ };
276
+ const currentAppContext = () => globalThis[APP_CONTEXT_GLOBAL_KEY] ?? {};
277
+ async function invokeLifecycleFn(remoteName, fn, ctx, phase) {
278
+ const wrap = (err) => {
279
+ if (err instanceof FgError) return err;
280
+ return new FgError(
281
+ ErrorCodes.SETUP_RUN_FAILED,
282
+ `\u73B0\u8C61\uFF1A\u8FDC\u7A0B\u5E94\u7528 "${remoteName}" \u7684 ${phase} \u521D\u59CB\u5316\u5931\u8D25\u3002
283
+ \u539F\u56E0\uFF1A${String(err?.message ?? err)}
284
+ \u4FEE\u6CD5\uFF1A\u68C0\u67E5\u5E76\u4FEE\u590D\u8BE5 ${phase} \u51FD\u6570\u540E\u91CD\u65B0\u52A0\u8F7D\uFF1B\u5931\u8D25\u7F13\u5B58\u5DF2\u6E05\u9664\uFF0C\u53EF\u4EE5\u76F4\u63A5\u91CD\u8BD5\u3002`,
285
+ { remote: remoteName, phase },
286
+ { cause: err }
287
+ );
288
+ };
289
+ lifecycleSyncRemote = remoteName;
290
+ let result;
291
+ try {
292
+ result = fn(ctx);
293
+ } catch (err) {
294
+ throw wrap(err);
295
+ } finally {
296
+ lifecycleSyncRemote = void 0;
297
+ }
298
+ try {
299
+ await result;
300
+ } catch (err) {
301
+ throw wrap(err);
302
+ }
303
+ }
304
+ async function runSetupPhase(remote, container, setupKey, st) {
305
+ remote.debug.setup = "pending";
306
+ try {
307
+ if (remote.manifestUrl) {
308
+ try {
309
+ await preloadRemote2(`${remote.name}/${setupKey}`);
310
+ } catch {
311
+ }
312
+ }
313
+ const mod = await container.get(setupKey);
314
+ const setupFn = mod?.default;
315
+ if (typeof setupFn !== "function") {
316
+ throw new FgError(
317
+ ErrorCodes.SETUP_INVALID_EXPORT,
318
+ `\u73B0\u8C61\uFF1A\u8FDC\u7A0B\u5E94\u7528 "${remote.name}" \u7684 setup \u6A21\u5757 "${setupKey}" \u65E0\u6CD5\u521D\u59CB\u5316\u3002
319
+ \u539F\u56E0\uFF1A\u9ED8\u8BA4\u5BFC\u51FA\u7C7B\u578B\u4E3A ${mod == null ? String(mod) : typeof setupFn}\uFF0C\u5E94\u4E3A\u51FD\u6570\u3002
320
+ \u4FEE\u6CD5\uFF1A\u5728 setup \u6307\u5411\u7684\u6587\u4EF6\u4E2D\u9ED8\u8BA4\u5BFC\u51FA\u51FD\u6570\uFF0C\u4F8B\u5982 export default async function setup(context) { ... }\uFF1B\u5177\u540D onSession \u53EF\u9009\u3002`,
321
+ { remote: remote.name, module: setupKey }
322
+ );
323
+ }
324
+ if (mod.onSession !== void 0 && typeof mod.onSession !== "function") {
325
+ throw new FgError(
326
+ ErrorCodes.SETUP_INVALID_EXPORT,
327
+ `\u73B0\u8C61\uFF1A\u8FDC\u7A0B\u5E94\u7528 "${remote.name}" \u7684 setup \u6A21\u5757 "${setupKey}" \u65E0\u6CD5\u521D\u59CB\u5316\u3002
328
+ \u539F\u56E0\uFF1AonSession \u5BFC\u51FA\u7C7B\u578B\u4E3A ${typeof mod.onSession}\uFF0C\u5E94\u4E3A\u51FD\u6570\u3002
329
+ \u4FEE\u6CD5\uFF1A\u5C06 onSession \u6539\u4E3A\u5177\u540D\u5BFC\u51FA\u7684\u51FD\u6570\uFF0C\u6216\u5220\u9664\u8BE5\u5BFC\u51FA\u3002`,
330
+ { remote: remote.name, module: setupKey }
331
+ );
332
+ }
333
+ st.setupExports = mod;
334
+ await invokeLifecycleFn(
335
+ remote.name,
336
+ setupFn,
337
+ { appContext: currentAppContext(), signal: (st.controller ??= new AbortController()).signal },
338
+ "setup"
339
+ );
340
+ remote.debug.setup = "ready";
341
+ } catch (err) {
342
+ remote.debug.setup = "failed";
343
+ st.setupExports = void 0;
344
+ throw err;
345
+ }
346
+ }
347
+ async function ensureSessionPhase(remote, st, onSession) {
348
+ const appContext = currentAppContext();
349
+ const sessionKey = appContext?.sessionKey;
350
+ if (typeof sessionKey !== "string" || sessionKey === "") {
351
+ throw new FgError(
352
+ ErrorCodes.SETUP_SESSION_KEY_MISSING,
353
+ `\u73B0\u8C61\uFF1A\u8FDC\u7A0B\u5E94\u7528 "${remote.name}" \u58F0\u660E\u4E86 onSession\uFF0C\u4F46\u5BBF\u4E3B\u5C1A\u672A\u63D0\u4F9B\u6709\u6548\u7684 AppContext.sessionKey\u3002
354
+ \u539F\u56E0\uFF1A\u5F53\u524D\u503C\u4E3A ${sessionKey === void 0 ? "\u7F3A\u5931" : JSON.stringify(sessionKey)}\u3002
355
+ \u4FEE\u6CD5\uFF1A\u5BBF\u4E3B\u6BCF\u6B21\u767B\u5F55\u6216\u91CD\u767B\u65F6\u751F\u6210\u65B0\u7684\u975E\u654F\u611F\u767B\u5F55\u4EE3\u6B21 ID\uFF0C\u5E76\u5728\u52A0\u8F7D\u8FDC\u7A0B\u9875\u9762\u524D\u8C03\u7528 provideAppContext({ ..., sessionKey: '\u767B\u5F55\u4EE3\u6B21 ID' })\uFF1B\u4E0D\u8981\u4F7F\u7528 token \u539F\u6587\u3002`,
356
+ { remote: remote.name }
357
+ );
358
+ }
359
+ if (st.sessionPromise && st.sessionKey === sessionKey) return st.sessionPromise;
360
+ st.controller?.abort();
361
+ const controller = new AbortController();
362
+ st.controller = controller;
363
+ st.sessionKey = sessionKey;
364
+ const prev = st.sessionPromise ? st.sessionPromise.catch(() => {
365
+ }) : Promise.resolve();
366
+ const run = (async () => {
367
+ await prev;
368
+ if (controller.signal.aborted) return;
369
+ await invokeLifecycleFn(remote.name, onSession, { appContext, sessionKey, signal: controller.signal }, "onSession");
370
+ })();
371
+ st.sessionPromise = run;
372
+ run.catch(() => {
373
+ if (st.sessionKey === sessionKey) {
374
+ st.sessionKey = void 0;
375
+ st.sessionPromise = void 0;
376
+ }
377
+ });
378
+ return run;
379
+ }
380
+ async function ensureLifecycle(remote, container) {
381
+ const setupKey = container?.[SETUP_META_KEY];
382
+ if (typeof setupKey !== "string" || setupKey === "") return;
383
+ const st = lifecycleOf(remote.name);
384
+ if (!st.setupPromise) {
385
+ st.setupPromise = runSetupPhase(remote, container, setupKey, st).catch((err) => {
386
+ st.setupPromise = void 0;
387
+ throw err;
388
+ });
389
+ }
390
+ await st.setupPromise;
391
+ const onSession = st.setupExports?.onSession;
392
+ if (typeof onSession === "function") {
393
+ await ensureSessionPhase(remote, st, onSession);
394
+ }
395
+ }
396
+ function clearSessionState2() {
397
+ for (const st of lifecycleStates.values()) {
398
+ try {
399
+ st.controller?.abort();
400
+ } catch {
401
+ }
402
+ st.sessionKey = void 0;
403
+ st.sessionPromise = void 0;
404
+ }
405
+ }
406
+ const getScope = (name) => shareScopeMap2[name] ??= /* @__PURE__ */ Object.create(null);
407
+ function emitError(info) {
408
+ try {
409
+ hooks.onRemoteError?.(info);
410
+ } catch {
411
+ }
412
+ if (typeof window !== "undefined") {
413
+ window.dispatchEvent(new CustomEvent("fulgurjs:error", { detail: info }));
414
+ }
415
+ }
416
+ function initSharing2(scopeName = "default") {
417
+ getScope(scopeName);
418
+ return shareScopeMap2;
419
+ }
420
+ function registerShare2(scopeName, name, version, get, opts = {}) {
421
+ const scope = getScope(scopeName || "default");
422
+ const own = Object.hasOwn(scope, name) ? scope[name] : void 0;
423
+ let byName = own && typeof own === "object" ? own : void 0;
424
+ if (!byName) {
425
+ byName = /* @__PURE__ */ Object.create(null);
426
+ scope[name] = byName;
427
+ }
428
+ if (Object.hasOwn(byName, version) && byName[version]) {
429
+ return;
430
+ }
431
+ byName[version] = { version, get, from: opts.from ?? "unknown", eager: !!opts.eager, loaded: !!opts.loaded };
432
+ }
433
+ function assertRemoteParams(r) {
434
+ const check = (v, name, int10) => {
435
+ if (v === void 0) return;
436
+ const ok = typeof v === "number" && (int10 ? Number.isInteger(v) && v >= 0 && v <= 10 : Number.isFinite(v) && v > 0);
437
+ if (!ok) {
438
+ throw new Error(
439
+ `[fulgurjs] \u8FDC\u7A0B\u5E94\u7528 "${r.name}" \u7684 ${name} \u914D\u7F6E\u65E0\u6548\uFF1A\u5F53\u524D\u4E3A ${String(v)}\uFF0C\u5E94\u4E3A${int10 ? " 0 \u5230 10 \u7684\u6574\u6570" : "\u5927\u4E8E 0 \u7684\u6709\u9650\u6570\u5B57"}\u3002\u8BF7\u4FEE\u6B63 registerRemote \u914D\u7F6E\u3002`
440
+ );
441
+ }
442
+ };
443
+ check(r.timeout, "timeout");
444
+ check(r.retries, "retries", true);
445
+ if (r.breaker) {
446
+ check(r.breaker.threshold, "breaker.threshold");
447
+ check(r.breaker.resetMs, "breaker.resetMs");
448
+ }
449
+ }
450
+ function registerRemotes2(list) {
451
+ for (const r of list) {
452
+ assertRemoteParams(r);
453
+ const prev = remotes.get(r.name);
454
+ remotes.set(r.name, {
455
+ timeout: DEFAULT_TIMEOUT,
456
+ retries: DEFAULT_RETRIES,
457
+ ...prev,
458
+ ...r,
459
+ // 熔断参数随最新配置刷新;计数状态(fails/openUntil)跨注册保留——半开窗口不被重置
460
+ breakerThreshold: r.breaker?.threshold ?? prev?.breakerThreshold ?? BREAKER_THRESHOLD,
461
+ breakerResetMs: r.breaker?.resetMs ?? prev?.breakerResetMs ?? BREAKER_RESET_MS,
462
+ state: prev?.state ?? "idle",
463
+ breakerState: prev?.breakerState ?? { fails: 0, openUntil: 0 },
464
+ entryInflight: prev?.entryInflight ?? /* @__PURE__ */ new Map(),
465
+ entryFailCounts: prev?.entryFailCounts ?? /* @__PURE__ */ new Map(),
466
+ debug: prev?.debug ?? { entry: r.entry, status: "idle", setup: "none" }
467
+ });
468
+ }
469
+ }
470
+ function registerRemote2(remote) {
471
+ registerRemotes2([remote]);
472
+ }
473
+ function registerPlugins2(list) {
474
+ plugins.push(...list);
475
+ applyPlugins();
476
+ }
477
+ async function loadShare2(name, opts = {}) {
478
+ const scopeName = opts.shareScope || "default";
479
+ const shareKey = opts.shareKey || name;
480
+ const scope = getScope(scopeName);
481
+ const byName = scope[shareKey] || {};
482
+ const versions = Object.keys(byName);
483
+ const req = opts.requiredVersion;
484
+ const satisfying = versions.filter(
485
+ (v) => req === void 0 || req === false || satisfies(v, req)
486
+ );
487
+ let pick;
488
+ if (opts.singleton && versions.length > 0) {
489
+ const loadedVersions = versions.filter((v) => byName[v].loaded);
490
+ pick = [...loadedVersions.length ? loadedVersions : versions].sort(compareVersions).pop();
491
+ if (!satisfying.includes(pick)) {
492
+ const entry0 = byName[pick];
493
+ const msg = `\u73B0\u8C61\uFF1A\u5171\u4EAB\u5355\u4F8B "${shareKey}" \u7684\u5B9E\u9645\u7248\u672C\u4E0D\u6EE1\u8DB3\u5F53\u524D\u5E94\u7528\u8981\u6C42\u3002
494
+ \u539F\u56E0\uFF1A\u5E94\u7528\u8981\u6C42 ${req ?? "\u4EFB\u610F\u7248\u672C"}\uFF0C\u4F5C\u7528\u57DF\u4E2D\u6709 ${versions.join("\u3001")}\uFF1B\u4E3A\u4FDD\u8BC1\u5168\u9875\u53EA\u4F7F\u7528\u4E00\u4E2A\u5B9E\u4F8B\uFF0C\u6700\u7EC8\u590D\u7528 ${entry0.from} \u63D0\u4F9B\u7684 ${pick}\u3002
495
+ \u5F71\u54CD\uFF1A\u53EF\u80FD\u51FA\u73B0\u72B6\u6001\u4E0D\u4E00\u81F4\u6216\u8FD0\u884C\u9519\u8BEF\uFF0C\u5177\u4F53\u53D6\u51B3\u4E8E\u4E24\u7AEF API \u662F\u5426\u517C\u5BB9\u3002
496
+ \u4FEE\u6CD5\uFF1A\u7EDF\u4E00\u5BBF\u4E3B\u4E0E\u8FDC\u7A0B\u7684\u4F9D\u8D56\u7248\u672C\uFF0C\u6216\u6838\u5BF9\u540E\u8C03\u6574 shared.requiredVersion\uFF1B\u82E5\u5FC5\u987B\u62D2\u7EDD\u4E0D\u517C\u5BB9\u7248\u672C\uFF0C\u8BF7\u542F\u7528 strictVersion\u3002`;
497
+ if (opts.strictVersion) {
498
+ const err = new FgError(ErrorCodes.SHARE_STRICT_VERSION, msg, {
499
+ shareKey,
500
+ requiredVersion: req
501
+ });
502
+ emitError({ remote: entry0.from, error: err });
503
+ throw err;
504
+ }
505
+ const warningKey = `${scopeName}|${shareKey}|${req ?? "*"}|${pick}|${versions.join(",")}`;
506
+ if (!reportedSingletonSkews.has(warningKey)) {
507
+ reportedSingletonSkews.add(warningKey);
508
+ console.warn(`[fulgurjs:MFU-010] ${msg}`);
509
+ }
510
+ }
511
+ } else {
512
+ pick = [...satisfying].sort(compareVersions).pop();
513
+ if (!pick) {
514
+ if (opts.strictVersion) {
515
+ const err2 = new FgError(
516
+ ErrorCodes.SHARE_STRICT_VERSION,
517
+ `\u73B0\u8C61\uFF1A\u5171\u4EAB\u4F5C\u7528\u57DF "${scopeName}" \u4E2D\u627E\u4E0D\u5230\u7B26\u5408\u8981\u6C42\u7684 "${shareKey}"\u3002
518
+ \u539F\u56E0\uFF1A\u8981\u6C42\u7248\u672C ${req}\uFF0C\u5F53\u524D\u53EF\u7528\u7248\u672C\u4E3A ${versions.join("\u3001") || "\u65E0"}\u3002
519
+ \u4FEE\u6CD5\uFF1A\u7EDF\u4E00\u5BBF\u4E3B\u4E0E\u8FDC\u7A0B\u7684\u4F9D\u8D56\u7248\u672C\uFF0C\u6216\u4FEE\u6B63 shared.requiredVersion\u3002`
520
+ );
521
+ emitError({ remote: shareKey, error: err2 });
522
+ throw err2;
523
+ }
524
+ if (opts.fallback) return opts.fallback();
525
+ const err = new FgError(
526
+ ErrorCodes.SHARE_NOT_AVAILABLE,
527
+ `\u73B0\u8C61\uFF1A\u65E0\u6CD5\u52A0\u8F7D\u5171\u4EAB\u4F9D\u8D56 "${shareKey}"\u3002
528
+ \u539F\u56E0\uFF1A\u4F5C\u7528\u57DF "${scopeName}" \u4E2D\u6CA1\u6709\u7B26\u5408 ${req ?? "\u4EFB\u610F\u7248\u672C"} \u8981\u6C42\u7684\u7248\u672C\uFF0C\u4E14\u672A\u914D\u7F6E\u672C\u5730\u526F\u672C\u3002
529
+ \u4FEE\u6CD5\uFF1A\u5728\u5BBF\u4E3B\u6216\u8FDC\u7A0B\u6CE8\u518C\u8BE5\u4F9D\u8D56\uFF0C\u6216\u4E3A shared \u914D\u7F6E\u53EF\u7528\u7684\u672C\u5730\u526F\u672C\u3002`,
530
+ { shareKey, requiredVersion: req }
531
+ );
532
+ emitError({ remote: shareKey, error: err });
533
+ throw err;
534
+ }
535
+ }
536
+ let entry = byName[pick];
537
+ if (hooks.resolveShare) {
538
+ const picked = await hooks.resolveShare({
539
+ shareKey,
540
+ shareScope: scopeName,
541
+ requiredVersion: req,
542
+ picked: entry,
543
+ available: Object.values(byName)
544
+ });
545
+ if (picked) entry = picked;
546
+ }
547
+ entry.loaded = true;
548
+ return entry.get();
549
+ }
550
+ function sanitizeUrl(u) {
551
+ try {
552
+ const x = new URL(u, typeof location < "u" ? location.href : "http://f.invalid");
553
+ return x.protocol + "//" + x.host + x.pathname;
554
+ } catch {
555
+ return String(u).replace(/[?#].*$/, "");
556
+ }
557
+ }
558
+ function withTimeout(p, ms, label) {
559
+ return new Promise((resolve, reject) => {
560
+ const t = setTimeout(() => reject(new Error(`\u7B49\u5F85\u8D85\u8FC7 ${ms} \u6BEB\u79D2\uFF1A${label}`)), ms);
561
+ p.then(
562
+ (v) => {
563
+ clearTimeout(t);
564
+ resolve(v);
565
+ },
566
+ (e) => {
567
+ clearTimeout(t);
568
+ reject(e);
569
+ }
570
+ );
571
+ });
572
+ }
573
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
574
+ function validateContainerInterface(container, remote) {
575
+ if (!container || typeof container.get !== "function") {
576
+ throw new Error(
577
+ `\u8FDC\u7A0B\u5E94\u7528 "${remote.name}" \u7684 remoteEntry \u672A\u5BFC\u51FA\u5BB9\u5668\u63A5\u53E3\u3002\u8BF7\u786E\u8BA4\u5165\u53E3\u5BFC\u51FA\u4E86 get()\uFF0C\u5E76\u68C0\u67E5\u8BE5\u5730\u5740\u662F\u5426\u8FD4\u56DE\u6B63\u786E\u7684\u8054\u90A6\u5165\u53E3 JS\u3002`
578
+ );
579
+ }
580
+ if (container.name && remote.name && container.name !== remote.name) {
581
+ const msg = `\u8FDC\u7A0B\u5165\u53E3\u81EA\u62A5\u540D\u79F0 "${container.name}" \u4E0E\u5BBF\u4E3B\u914D\u7F6E\u540D\u79F0 "${remote.name}" \u4E0D\u4E00\u81F4\u3002\u8BF7\u7EDF\u4E00\u4E24\u4FA7 federation({ name }) \u4E0E remotes \u7684\u540D\u79F0\u3002`;
582
+ if (remote.promise) {
583
+ console.warn(`[fulgurjs] ${msg} \u5F53\u524D\u662F\u52A8\u6001 Promise \u8FDC\u7A0B\uFF0C\u7EE7\u7EED\u52A0\u8F7D\u3002`);
584
+ } else {
585
+ throw Object.assign(new Error(msg), { code: ErrorCodes.REMOTE_NAME_MISMATCH });
586
+ }
587
+ }
588
+ }
589
+ async function importEntry(url, remote) {
590
+ const fails = remote.entryFailCounts.get(url) ?? 0;
591
+ const attemptUrl = fails > 0 ? `${url}${url.includes("?") ? "&" : "?"}fulgurjs_retry=${fails}` : url;
592
+ const cached = remote.entryInflight.get(url);
593
+ const inflight = cached ?? import(
594
+ /* @vite-ignore */
595
+ attemptUrl
596
+ );
597
+ if (!cached) {
598
+ remote.entryInflight.set(url, inflight);
599
+ inflight.catch(() => {
600
+ remote.entryInflight.delete(url);
601
+ remote.entryFailCounts.set(url, (remote.entryFailCounts.get(url) ?? 0) + 1);
602
+ });
603
+ }
604
+ return withTimeout(inflight, remote.timeout ?? DEFAULT_TIMEOUT, `\u52A0\u8F7D\u8FDC\u7A0B\u5165\u53E3 ${sanitizeUrl(url)}`);
605
+ }
606
+ async function acquireContainer(remote, overrides) {
607
+ const scopeKey = remote.shareScope || "default";
608
+ if (remote.container) {
609
+ const prevScope = containerInitScopes.get(remote.container);
610
+ if (prevScope && prevScope !== scopeKey) {
611
+ throw new FgError(
612
+ ErrorCodes.CONTAINER_REINIT_CONFLICT,
613
+ `\u8FDC\u7A0B\u5BB9\u5668 "${remote.name}" \u5DF2\u4F7F\u7528\u5171\u4EAB\u4F5C\u7528\u57DF "${prevScope}" \u521D\u59CB\u5316\uFF0C\u4E0D\u80FD\u518D\u4EE5 "${scopeKey}" \u521D\u59CB\u5316\u3002\u8BF7\u7EDF\u4E00 shareScope \u914D\u7F6E\u3002`
614
+ );
615
+ }
616
+ return remote.container;
617
+ }
618
+ if (remote.containerPromise) return remote.containerPromise;
619
+ const b = remote.breakerState;
620
+ if (b.openUntil > Date.now()) {
621
+ const err = new FgError(
622
+ ErrorCodes.REMOTE_LOAD_FAILED,
623
+ `\u8FDC\u7A0B\u5E94\u7528 "${remote.name}" \u8FDE\u7EED\u52A0\u8F7D\u5931\u8D25\uFF0C\u7194\u65AD\u5668\u5DF2\u5F00\u542F\uFF1B\u5C06\u5728 ${new Date(b.openUntil).toISOString()} \u540E\u5141\u8BB8\u91CD\u8BD5\u3002\u8BF7\u5148\u68C0\u67E5\u8FDC\u7A0B\u5165\u53E3\u670D\u52A1\u548C\u7F51\u7EDC\u3002`,
624
+ { remote: remote.name }
625
+ );
626
+ emitError({ remote: remote.name, error: err });
627
+ throw err;
628
+ }
629
+ remote.state = "loading";
630
+ remote.debug.status = "loading";
631
+ const containerPromise = (async () => {
632
+ const loaders = [];
633
+ if (remote.promise) {
634
+ loaders.push(async () => {
635
+ const resolved = await withTimeout(
636
+ remote.promise(),
637
+ remote.timeout ?? DEFAULT_TIMEOUT,
638
+ `\u89E3\u6790\u52A8\u6001\u8FDC\u7A0B\u5E94\u7528 "${remote.name}"`
639
+ );
640
+ const container2 = typeof resolved === "string" ? await importEntry(resolved, remote) : resolved;
641
+ validateContainerInterface(container2, remote);
642
+ return container2;
643
+ });
644
+ } else {
645
+ for (const url of [remote.entry, ...remote.fallback ?? []]) {
646
+ loaders.push(async () => {
647
+ const container2 = await importEntry(url, remote);
648
+ validateContainerInterface(container2, remote);
649
+ return container2;
650
+ });
651
+ }
652
+ }
653
+ let container;
654
+ let lastErr;
655
+ let succeeded = false;
656
+ const maxRetries = overrides?.retries ?? remote.retries ?? DEFAULT_RETRIES;
657
+ for (const loader of loaders) {
658
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
659
+ const start = performance.now();
660
+ try {
661
+ container = await loader();
662
+ remote.lastLoadMs = Math.round(performance.now() - start);
663
+ remote.debug.lastLoadMs = remote.lastLoadMs;
664
+ succeeded = true;
665
+ break;
666
+ } catch (err) {
667
+ lastErr = err;
668
+ if (err?.code) throw err;
669
+ if (attempt < maxRetries) {
670
+ await sleep(Math.min(BACKOFF_MAX_MS, 200 * 2 ** attempt) * (1 - BACKOFF_JITTER * (1 - 2 * Math.random())));
671
+ }
672
+ }
673
+ }
674
+ if (succeeded) break;
675
+ }
676
+ if (!succeeded || !container) {
677
+ const shown = sanitizeUrl(remote.entry);
678
+ const hints = [];
679
+ if (/^https?:\/\//.test(remote.entry) && typeof location !== "undefined") {
680
+ hints.push(`1. \u5728\u6D4F\u89C8\u5668\u6253\u5F00 ${shown}\uFF0C\u786E\u8BA4\u8FDC\u7A0B\u5F00\u53D1\u670D\u52A1\u5DF2\u542F\u52A8\uFF0C\u5E76\u8FD4\u56DE JS \u800C\u975E HTML\u3002`);
681
+ hints.push(`2. \u6838\u5BF9 federation({ remotes }) \u4E2D\u7684\u5730\u5740\uFF1B\u5F00\u53D1\u548C\u751F\u4EA7\u5730\u5740\u53EF\u4EE5\u4E0D\u540C\u3002`);
682
+ hints.push(`3. \u82E5\u8DE8\u57DF\uFF0C\u68C0\u67E5\u8FDC\u7A0B\u5F00\u53D1\u670D\u52A1\u7684 server.cors \u914D\u7F6E\u3002`);
683
+ } else {
684
+ hints.push(`1. \u5728\u6D4F\u89C8\u5668\u6253\u5F00 ${shown}\uFF0C\u786E\u8BA4\u8FDC\u7A0B\u5E94\u7528\u5DF2\u90E8\u7F72\uFF0C\u4E14\u5165\u53E3\u8FD4\u56DE JS\u3002`);
685
+ hints.push(`2. \u68C0\u67E5 Nginx/CDN \u7684\u5165\u53E3\u8DEF\u5F84\u6620\u5C04\uFF1B\u8BE5\u5730\u5740\u4E0D\u80FD\u56DE\u9000\u5230 index.html\u3002`);
686
+ }
687
+ throw new FgError(
688
+ ErrorCodes.REMOTE_LOAD_FAILED,
689
+ `\u73B0\u8C61\uFF1A\u65E0\u6CD5\u4ECE ${shown} \u52A0\u8F7D\u8FDC\u7A0B\u5E94\u7528 "${remote.name}"\u3002
690
+ \u539F\u56E0\uFF1A${String(lastErr?.message ?? lastErr)}
691
+ \u4FEE\u6CD5\uFF1A
692
+ ${hints.join("\n")}`,
693
+ { remote: remote.name }
694
+ );
695
+ }
696
+ validateContainerInterface(container, remote);
697
+ const prevScope = containerInitScopes.get(container);
698
+ if (prevScope && prevScope !== scopeKey) {
699
+ throw new FgError(
700
+ ErrorCodes.CONTAINER_REINIT_CONFLICT,
701
+ `\u8FDC\u7A0B\u5BB9\u5668 "${remote.name}" \u5DF2\u4F7F\u7528\u5171\u4EAB\u4F5C\u7528\u57DF "${prevScope}" \u521D\u59CB\u5316\uFF0C\u4E0D\u80FD\u518D\u4EE5 "${scopeKey}" \u521D\u59CB\u5316\u3002\u8BF7\u7EDF\u4E00 shareScope \u914D\u7F6E\u3002`
702
+ );
703
+ }
704
+ if (typeof container.init === "function") {
705
+ await container.init(shareScopeMap2);
706
+ }
707
+ containerInitScopes.set(container, scopeKey);
708
+ remote.container = container;
709
+ remote.state = "loaded";
710
+ remote.debug.status = "loaded";
711
+ b.fails = 0;
712
+ return container;
713
+ })();
714
+ remote.containerPromise = containerPromise;
715
+ try {
716
+ return await containerPromise;
717
+ } catch (err) {
718
+ remote.containerPromise = void 0;
719
+ remote.state = "failed";
720
+ remote.debug.status = "failed";
721
+ remote.debug.error = String(err?.message ?? err);
722
+ b.fails += 1;
723
+ if (b.fails >= remote.breakerThreshold) {
724
+ b.openUntil = Date.now() + remote.breakerResetMs;
725
+ b.fails = 0;
726
+ }
727
+ if (err instanceof FgError) {
728
+ emitError({ remote: remote.name, error: err });
729
+ throw err;
730
+ }
731
+ if (err?.code) {
732
+ emitError({ remote: remote.name, error: err });
733
+ throw err;
734
+ }
735
+ const wrapped = new FgError(ErrorCodes.REMOTE_LOAD_FAILED, `\u8FDC\u7A0B\u5E94\u7528 "${remote.name}" \u52A0\u8F7D\u5931\u8D25\u3002\u5E95\u5C42\u539F\u56E0\uFF1A${String(err?.message ?? err)}\u3002\u8BF7\u68C0\u67E5\u8FDC\u7A0B\u5165\u53E3\u3001\u7F51\u7EDC\u53CA\u5171\u4EAB\u4F9D\u8D56\u3002`, {
736
+ remote: remote.name
737
+ });
738
+ emitError({ remote: remote.name, error: wrapped });
739
+ throw wrapped;
740
+ }
741
+ }
742
+ function parseSpec2(spec) {
743
+ const idx = spec.indexOf("/");
744
+ if (idx === -1) return { remote: spec, module: "" };
745
+ return { remote: spec.slice(0, idx), module: normalizeModuleName(spec.slice(idx + 1)) };
746
+ }
747
+ function normalizeModuleName(m) {
748
+ if (!m) return "";
749
+ return m.startsWith(".") ? m : `./${m}`;
750
+ }
751
+ async function loadRemote2(spec, opts) {
752
+ const { remote: name, module } = parseSpec2(spec);
753
+ if (module && lifecycleSyncRemote === name) {
754
+ throw new FgError(
755
+ ErrorCodes.SETUP_RECURSION,
756
+ `\u73B0\u8C61\uFF1A\u52A0\u8F7D "${spec}" \u65F6\uFF0C\u8FDC\u7A0B\u5E94\u7528 "${name}" \u7684 setup/onSession \u53C8\u540C\u6B65\u52A0\u8F7D\u4E86\u81EA\u8EAB\u6A21\u5757\uFF0C\u5F62\u6210\u5FAA\u73AF\u7B49\u5F85\u3002
757
+ \u539F\u56E0\uFF1A\u521D\u59CB\u5316\u5C1A\u672A\u5B8C\u6210\uFF0C\u4E0D\u80FD\u7B49\u5F85\u81EA\u8EAB\u7684\u521D\u59CB\u5316\u7ED3\u679C\u3002
758
+ \u4FEE\u6CD5\uFF1A\u4E0D\u8981\u5728 setup/onSession \u4E2D\u52A0\u8F7D\u540C\u4E00\u8FDC\u7A0B\u7684\u6A21\u5757\uFF1B\u8BF7\u5728\u9875\u9762\u7EC4\u4EF6\u4E2D\u52A0\u8F7D\uFF0C\u6216\u901A\u8FC7 context \u89E3\u8026\u3002`,
759
+ { remote: name }
760
+ );
761
+ }
762
+ try {
763
+ hooks.beforeLoadRemote?.({ remote: name, module });
764
+ } catch (hookErr) {
765
+ console.warn("[fulgurjs] beforeLoadRemote \u89C2\u6D4B\u94A9\u5B50\u6267\u884C\u5931\u8D25\uFF1B\u6A21\u5757\u52A0\u8F7D\u5C06\u7EE7\u7EED\u3002\u8BF7\u68C0\u67E5\u8BE5\u94A9\u5B50\uFF1A", hookErr);
766
+ }
767
+ const remote = remotes.get(name);
768
+ if (!remote) {
769
+ throw new FgError(
770
+ ErrorCodes.REMOTE_UNKNOWN,
771
+ `\u672A\u77E5\u8FDC\u7A0B\u5E94\u7528 "${name}"\u3002\u8BF7\u5728 federation({ remotes }) \u4E2D\u914D\u7F6E\uFF0C\u6216\u5728\u52A0\u8F7D\u524D\u8C03\u7528 registerRemote()\u3002`,
772
+ { remote: name }
773
+ );
774
+ }
775
+ let container;
776
+ try {
777
+ const stylesReady = module && remote.manifestUrl ? preloadRemote2(spec) : Promise.resolve();
778
+ [container] = await Promise.all([acquireContainer(remote, { retries: opts?.retries }), stylesReady]);
779
+ } catch (err) {
780
+ if (opts?.fallbackModule) {
781
+ console.error(`[fulgurjs] \u8FDC\u7A0B\u6A21\u5757 "${spec}" \u52A0\u8F7D\u5931\u8D25\uFF0C\u6B63\u5728\u4F7F\u7528\u663E\u5F0F\u914D\u7F6E\u7684 fallbackModule\uFF1B\u539F\u59CB\u9519\u8BEF\uFF1A`, err);
782
+ emitError({ remote: name, error: err });
783
+ return await opts.fallbackModule();
784
+ }
785
+ throw err;
786
+ }
787
+ if (!module) return container;
788
+ await ensureLifecycle(remote, container);
789
+ const cacheKey = `${name}@${remote.shareScope || "default"}#${module}`;
790
+ if (!loadedModules.has(cacheKey)) {
791
+ loadedModules.set(
792
+ cacheKey,
793
+ container.get(module).catch((err) => {
794
+ loadedModules.delete(cacheKey);
795
+ if (err instanceof FgError || err?.code) throw err;
796
+ throw new FgError(
797
+ ErrorCodes.REMOTE_LOAD_FAILED,
798
+ `\u65E0\u6CD5\u4ECE\u8FDC\u7A0B\u5E94\u7528 "${name}" \u52A0\u8F7D\u6A21\u5757 "${module}"\u3002\u5E95\u5C42\u539F\u56E0\uFF1A${String(err?.message ?? err)}\u3002\u8BF7\u6838\u5BF9 exposes \u952E\u548C\u8FDC\u7A0B\u6784\u5EFA\u4EA7\u7269\u3002`,
799
+ { remote: name, module }
800
+ );
801
+ })
802
+ );
803
+ }
804
+ let ns;
805
+ try {
806
+ ns = await loadedModules.get(cacheKey);
807
+ } catch (err) {
808
+ if (opts?.fallbackModule) {
809
+ console.error(`[fulgurjs] \u8FDC\u7A0B\u6A21\u5757 "${spec}" \u52A0\u8F7D\u5931\u8D25\uFF0C\u6B63\u5728\u4F7F\u7528\u663E\u5F0F\u914D\u7F6E\u7684 fallbackModule\uFF1B\u539F\u59CB\u9519\u8BEF\uFF1A`, err);
810
+ emitError({ remote: name, error: err });
811
+ return await opts.fallbackModule();
812
+ }
813
+ throw err;
814
+ }
815
+ try {
816
+ hooks.afterLoadRemote?.({ remote: name, module, module_ns: ns });
817
+ } catch (hookErr) {
818
+ console.warn("[fulgurjs] afterLoadRemote \u89C2\u6D4B\u94A9\u5B50\u6267\u884C\u5931\u8D25\uFF1B\u5DF2\u52A0\u8F7D\u6A21\u5757\u4ECD\u53EF\u4F7F\u7528\u3002\u8BF7\u68C0\u67E5\u8BE5\u94A9\u5B50\uFF1A", hookErr);
819
+ }
820
+ if (ns && typeof ns === "object" && Object.keys(ns).length === 0) {
821
+ const err = new FgError(
822
+ ErrorCodes.EMPTY_EXPORTS,
823
+ `\u8FDC\u7A0B\u5E94\u7528 "${name}" \u7684\u6A21\u5757 "${module}" \u6CA1\u6709\u4EFB\u4F55\u5BFC\u51FA\u3002\u8BF7\u68C0\u67E5 exposes \u6307\u5411\u7684\u6587\u4EF6\u53CA\u5176 export \u58F0\u660E\u3002`,
824
+ { remote: name, module }
825
+ );
826
+ emitError({ remote: name, error: err });
827
+ console.error(err.message);
828
+ }
829
+ return ns;
830
+ }
831
+ function getContainer2(name) {
832
+ const remote = remotes.get(name);
833
+ if (!remote) {
834
+ throw new FgError(ErrorCodes.REMOTE_UNKNOWN, `\u672A\u77E5\u8FDC\u7A0B\u5E94\u7528 "${name}"\u3002\u8BF7\u5148\u6CE8\u518C\u8BE5\u8FDC\u7A0B\u5E94\u7528\u3002`, { remote: name });
835
+ }
836
+ return acquireContainer(remote);
837
+ }
838
+ async function preloadRemote2(spec, opts = {}) {
839
+ const { remote: name, module } = parseSpec2(spec);
840
+ const remote = remotes.get(name);
841
+ if (!remote) {
842
+ throw new FgError(ErrorCodes.REMOTE_UNKNOWN, `\u672A\u77E5\u8FDC\u7A0B\u5E94\u7528 "${name}"\u3002\u8BF7\u5148\u6CE8\u518C\u8BE5\u8FDC\u7A0B\u5E94\u7528\u3002`, { remote: name });
843
+ }
844
+ const mode = opts.mode ?? "preload";
845
+ const waitForStylesheet = (link, href) => {
846
+ if (link.sheet) return Promise.resolve();
847
+ const pending = stylesheetLoads.get(link);
848
+ if (pending) return pending;
849
+ const loaded = new Promise((resolve) => {
850
+ const finish = () => {
851
+ link.removeEventListener("load", finish);
852
+ link.removeEventListener("error", failed);
853
+ resolve();
854
+ };
855
+ const failed = () => {
856
+ emitError({
857
+ remote: name,
858
+ error: new FgError(ErrorCodes.PRELOAD_FAILED, `\u8FDC\u7A0B\u5E94\u7528 "${name}" \u7684\u6837\u5F0F\u8D44\u6E90\u52A0\u8F7D\u5931\u8D25\uFF1A${href}\u3002\u8BF7\u68C0\u67E5\u6587\u4EF6\u662F\u5426\u5B58\u5728\u4EE5\u53CA\u8DE8\u57DF\u548C\u7F13\u5B58\u914D\u7F6E\u3002`, {
859
+ remote: name
860
+ })
861
+ });
862
+ finish();
863
+ };
864
+ link.addEventListener("load", finish, { once: true });
865
+ link.addEventListener("error", failed, { once: true });
866
+ if (link.sheet) finish();
867
+ });
868
+ stylesheetLoads.set(link, loaded);
869
+ return loaded;
870
+ };
871
+ const inject = (href, as) => {
872
+ if (typeof document === "undefined") return Promise.resolve();
873
+ const existing = document.querySelector(`link[href="${href}"]`);
874
+ if (existing) {
875
+ return as === "style" && mode === "preload" ? stylesheetLoads.get(existing) ?? Promise.resolve() : Promise.resolve();
876
+ }
877
+ const link = document.createElement("link");
878
+ link.rel = as === "style" ? "stylesheet" : "modulepreload";
879
+ link.href = href;
880
+ if (mode === "prefetch") {
881
+ ;
882
+ link.fetchPriority = "low";
883
+ }
884
+ const loaded = as === "style" ? waitForStylesheet(link, href) : Promise.resolve();
885
+ document.head.appendChild(link);
886
+ return as === "style" && mode === "preload" ? loaded : Promise.resolve();
887
+ };
888
+ const resolveAssetUrl = (asset) => {
889
+ const pageUrl = typeof document !== "undefined" ? document.baseURI : void 0;
890
+ const base = new URL(remote.entry, pageUrl || "http://fulgurjs.invalid/");
891
+ if (!base.pathname.endsWith("/") && !base.pathname.split("/").pop().includes(".")) {
892
+ base.pathname += "/";
893
+ }
894
+ return new URL(asset, base).href;
895
+ };
896
+ try {
897
+ if (remote.manifestUrl) {
898
+ let manifestPromise = remoteManifests.get(remote.manifestUrl);
899
+ if (!manifestPromise) {
900
+ manifestPromise = fetch(remote.manifestUrl, {
901
+ cache: "no-cache",
902
+ signal: AbortSignal.timeout(8e3)
903
+ }).then((res) => res.ok ? res.json() : void 0);
904
+ remoteManifests.set(remote.manifestUrl, manifestPromise);
905
+ manifestPromise.then(
906
+ (manifest2) => {
907
+ if (!manifest2) remoteManifests.delete(remote.manifestUrl);
908
+ },
909
+ () => remoteManifests.delete(remote.manifestUrl)
910
+ );
911
+ }
912
+ const manifest = await manifestPromise;
913
+ if (manifest) {
914
+ const sv = manifest.schemaVersion;
915
+ if (typeof sv === "number" && (!Number.isInteger(sv) || sv > 1)) {
916
+ emitError({
917
+ remote: name,
918
+ error: new FgError(ErrorCodes.PRELOAD_FAILED, `\u8FDC\u7A0B\u5E94\u7528 "${name}" \u7684 manifest \u534F\u8BAE\u7248\u672C\u4E3A ${String(sv)}\uFF0C\u5F53\u524D\u4EC5\u652F\u6301\u7248\u672C 1\u3002\u8BF7\u7EDF\u4E00\u5BBF\u4E3B\u548C\u8FDC\u7A0B\u7684\u63D2\u4EF6\u7248\u672C\u3002`, {
919
+ remote: name
920
+ })
921
+ });
922
+ await inject(remote.entry, "modulepreload");
923
+ return;
924
+ }
925
+ await inject(remote.entry, "modulepreload");
926
+ const exposes = manifest.exposes;
927
+ 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) : [];
928
+ const selected = module ? entries.filter(([key, item]) => normalizeModuleName(String(item?.name ?? key)) === module) : entries;
929
+ const cssLoads = [];
930
+ for (const [, item] of selected) {
931
+ if (typeof item?.file === "string") {
932
+ await inject(resolveAssetUrl(item.file), "modulepreload");
933
+ }
934
+ if (Array.isArray(item?.css)) {
935
+ for (const css of item.css) {
936
+ if (typeof css !== "string") continue;
937
+ const loading = inject(resolveAssetUrl(css), "style");
938
+ if (mode === "preload") cssLoads.push(loading);
939
+ }
940
+ }
941
+ }
942
+ await Promise.all(cssLoads);
943
+ return;
944
+ }
945
+ }
946
+ await inject(remote.entry, "modulepreload");
947
+ } catch (err) {
948
+ emitError({
949
+ remote: name,
950
+ error: new FgError(ErrorCodes.PRELOAD_FAILED, `\u8FDC\u7A0B\u5E94\u7528 "${name}" \u9884\u52A0\u8F7D\u5931\u8D25\u3002\u5E95\u5C42\u539F\u56E0\uFF1A${String(err?.message ?? err)}\u3002\u8BF7\u68C0\u67E5 manifest \u548C\u8D44\u6E90\u5730\u5740\u3002`, {
951
+ remote: name
952
+ })
953
+ });
954
+ }
955
+ }
956
+ const attachDebug = () => {
957
+ if (typeof window === "undefined") return;
958
+ window.__FULGURJS_SCOPE__ = shareScopeMap2;
959
+ window.__FULGURJS_INFO__ = {
960
+ get remotes() {
961
+ const out = {};
962
+ for (const [k, v] of remotes) {
963
+ out[k] = { ...v.debug, entry: v.entry };
964
+ }
965
+ return out;
966
+ },
967
+ errors: []
968
+ };
969
+ };
970
+ attachDebug();
971
+ return {
972
+ shareScopeMap: shareScopeMap2,
973
+ initSharing: initSharing2,
974
+ registerShare: registerShare2,
975
+ registerRemotes: registerRemotes2,
976
+ registerRemote: registerRemote2,
977
+ registerPlugins: registerPlugins2,
978
+ loadShare: loadShare2,
979
+ loadRemote: loadRemote2,
980
+ getContainer: getContainer2,
981
+ preloadRemote: preloadRemote2,
982
+ parseSpec: parseSpec2,
983
+ clearSessionState: clearSessionState2
984
+ };
985
+ }
986
+ var g = globalThis;
987
+ var runtime = g.__FULGURJS_RUNTIME__ ?? createRuntime();
988
+ g.__FULGURJS_RUNTIME__ = runtime;
989
+ try {
990
+ Object.freeze(runtime);
991
+ } catch {
992
+ }
993
+ var initSharing = runtime.initSharing;
994
+ var registerShare = runtime.registerShare;
995
+ var registerRemotes = runtime.registerRemotes;
996
+ var registerRemote = runtime.registerRemote;
997
+ var registerPlugins = runtime.registerPlugins;
998
+ var loadShare = runtime.loadShare;
999
+ var loadRemote = runtime.loadRemote;
1000
+ var getContainer = runtime.getContainer;
1001
+ var preloadRemote = runtime.preloadRemote;
1002
+ var parseSpec = runtime.parseSpec;
1003
+ var clearSessionState = runtime.clearSessionState;
1004
+ var shareScopeMap = runtime.shareScopeMap;
1005
+
1006
+ // src/context.ts
1007
+ var ContextErrorCodes = {
1008
+ /** context 必需字段缺失(三段式,修法指向宿主桥) */
1009
+ CONTEXT_MISSING_KEY: "CC-001",
1010
+ /** 运行时单例不可用(独立直开远程页,修法=经宿主联邦加载) */
1011
+ CONTEXT_NO_RUNTIME: "CC-002"
1012
+ };
1013
+ var APP_CONTEXT_STORAGE_KEY = "__FULGURJS_APP_CONFIG__";
1014
+ function requireRuntime() {
1015
+ const rt = globalThis.__FULGURJS_RUNTIME__;
1016
+ if (!rt || typeof rt.loadRemote !== "function") {
1017
+ throw new FgError(
1018
+ ContextErrorCodes.CONTEXT_NO_RUNTIME,
1019
+ "\u73B0\u8C61\uFF1A\u5F53\u524D\u9875\u9762\u627E\u4E0D\u5230 fulgurjs \u8FD0\u884C\u65F6\u5355\u4F8B\uFF08globalThis.__FULGURJS_RUNTIME__ \u672A\u5B9A\u4E49\uFF09\u3002\n \u6839\u56E0: \u5F53\u524D\u9875\u9762\u6CA1\u6709\u7ECF\u5BBF\u4E3B\u7684\u8054\u90A6\u8FD0\u884C\u65F6\u52A0\u8F7D\uFF08\u72EC\u7ACB\u76F4\u5F00\u8FDC\u7A0B\u9875\uFF0C\u6216\u5BBF\u4E3B\u6865\u665A\u4E8E\u672C\u8C03\u7528\u6267\u884C\uFF09\u3002\n \u4FEE\u6CD5: \u2460 \u4ECE\u5BBF\u4E3B\u5E94\u7528\u7684\u8054\u90A6\u8DEF\u7531\u6253\u5F00\u672C\u9875\u9762\uFF08\u5BBF\u4E3B\u6865\u4F1A\u5148\u52A0\u8F7D\u8FD0\u884C\u65F6\u5E76\u63D0\u4F9B context\uFF09\uFF1B\n \u2461 \u82E5\u4F60\u662F\u5BBF\u4E3B\u6865\u4F5C\u8005\uFF1A\u628A provideAppContext \u653E\u5728\u6865\u521D\u59CB\u5316\u5C3E\u90E8\uFF08\u65F6\u5E8F\u5951\u7EA6 bridge \u2192 \u8FDC\u7A0B setup \u2192 \u9875\u9762\u6A21\u5757\uFF09\u3002",
1020
+ { runtime: false }
1021
+ );
1022
+ }
1023
+ return rt;
1024
+ }
1025
+ function provideAppContext(config) {
1026
+ requireRuntime();
1027
+ const g2 = globalThis;
1028
+ g2[APP_CONTEXT_STORAGE_KEY] = { ...g2[APP_CONTEXT_STORAGE_KEY] ?? {}, ...config };
1029
+ }
1030
+ function clearAppContext() {
1031
+ const g2 = globalThis;
1032
+ delete g2[APP_CONTEXT_STORAGE_KEY];
1033
+ try {
1034
+ g2.__FULGURJS_RUNTIME__?.clearSessionState?.();
1035
+ } catch {
1036
+ }
1037
+ }
1038
+
1039
+ // src/bridge-errors.ts
1040
+ var BridgeErrorCodes = {
1041
+ /** 桥接契约非法(./bridge 默认导出缺 mount/unmount 或不是函数) */
1042
+ BRIDGE_INVALID_CONTRACT: "MFU-015",
1043
+ /** 桥接准备或生命周期失败(details.phase = getContext | mount | unmount) */
1044
+ BRIDGE_LIFECYCLE_FAILED: "MFU-016",
1045
+ /** 桥接会话参数与 AppContext 不一致(受控 sessionKey 与全局会话矛盾) */
1046
+ BRIDGE_SESSION_MISMATCH: "MFU-017"
1047
+ };
1048
+ function invalidBridgeContractError(spec, mod) {
1049
+ const actual = mod === null ? "null" : typeof mod;
1050
+ const missing = [];
1051
+ if (typeof mod?.mount !== "function") missing.push("mount");
1052
+ if (typeof mod?.unmount !== "function") missing.push("unmount");
1053
+ return new FgError(
1054
+ BridgeErrorCodes.BRIDGE_INVALID_CONTRACT,
1055
+ `\u73B0\u8C61\uFF1A\u8FDC\u7A0B\u6A21\u5757 "${spec}" \u7684\u9ED8\u8BA4\u5BFC\u51FA\u4E0D\u662F\u5408\u6CD5\u7684\u6865\u63A5\u5951\u7EA6\uFF08BridgeApp\uFF09\u3002
1056
+ \u6839\u56E0: \u5BFC\u51FA\u503C\u4E3A ${actual}` + (missing.length ? `\uFF0C\u7F3A\u5C11\u51FD\u6570\u7C7B\u578B\u7684 ${missing.map((m) => `"${m}"`).join(" / ")} \u5BFC\u51FA\u3002` : "\u3002") + `
1057
+ \u4FEE\u6CD5: \u8FDC\u7A0B\u4FA7\u7528 defineBridgeApp(...) \u521B\u5EFA\u5951\u7EA6\u5E76\u4F5C\u4E3A\u6A21\u5757\u9ED8\u8BA4\u5BFC\u51FA\u2014\u2014
1058
+ Vue \u5B50\u5E94\u7528: import { defineBridgeApp } from '@fulgurjs/federation/runtime'\uFF1B
1059
+ React \u5B50\u5E94\u7528: import { defineBridgeApp } from '@fulgurjs/federation/react'\u3002
1060
+ \u5951\u7EA6\u5FC5\u987B\u5305\u542B mount(el, props?) \u4E0E unmount(el) \u4E24\u4E2A\u51FD\u6570\u3002`,
1061
+ { spec, missing, actual }
1062
+ );
1063
+ }
1064
+ function bridgeLifecycleError(phase, spec, cause, extra) {
1065
+ const raw = cause instanceof Error ? cause.message : String(cause ?? "unknown");
1066
+ const fixes = {
1067
+ getContext: "\u4FEE\u6CD5: \u2460 getContext \u5FC5\u987B\u662F\u540C\u6B65\u3001\u65E0\u526F\u4F5C\u7528\u7684 getter\uFF08\u4E0D\u5F97\u8FD4\u56DE Promise/thenable\uFF09\uFF1B\n \u2461 \u8FD4\u56DE\u503C\u987B\u662F\u5305\u542B\u672C\u6B21\u4F1A\u8BDD\u5FEB\u7167\u7684\u5BF9\u8C61\uFF08\u53D7\u63A7 sessionKey \u65F6\u987B\u542B\u76F8\u540C sessionKey\uFF09\uFF1B\n \u2462 \u5F02\u6B65\u83B7\u53D6\u7528\u6237\u8D44\u6599\u8BF7\u5728\u5BBF\u4E3B\u5B8C\u6210\u540E\u518D\u8BA9\u6865\u63A5\u7EC4\u4EF6\u8FDB\u5165\u53EF\u6302\u8F7D\u72B6\u6001\u3002",
1068
+ mount: "\u4FEE\u6CD5: \u2460 \u67E5\u770B\u6839\u56E0\u4E2D\u5B50\u5E94\u7528 mount \u7684\u539F\u59CB\u9519\u8BEF\uFF08\u5DE5\u5382\u629B\u9519\u6216\u9996\u6B21\u6E32\u67D3\u5931\u8D25\uFF09\uFF1B\n \u2461 \u540C\u4E00\u5BB9\u5668\u672A\u5378\u8F7D\u524D\u91CD\u590D mount \u662F\u5951\u7EA6\u8FDD\u4F8B\u2014\u2014\u5148 unmount \u518D mount\uFF1B\n \u2462 \u4FEE\u590D\u540E\u70B9\u51FB\u300C\u91CD\u8BD5\u52A0\u8F7D\u300D\u5728\u540C\u9875\u91CD\u5EFA\u6302\u8F7D\u3002",
1069
+ unmount: "\u4FEE\u6CD5: \u2460 \u6839\u56E0\u6765\u81EA\u5B50\u5E94\u7528 unmount \u5B9E\u73B0\uFF08app.unmount/root.unmount \u629B\u9519\uFF09\uFF1B\n \u2461 \u8BE5\u5BB9\u5668\u6E05\u7406\u72B6\u6001\u4E0D\u786E\u5B9A\u2014\u2014\u4E0D\u8981\u76F4\u63A5\u5728\u540C\u4E00 el \u4E0A\u542F\u52A8\u65B0\u5B9E\u4F8B\uFF1B\n \u2462 \u5237\u65B0\u9875\u9762\u53EF\u5F7B\u5E95\u6E05\u7406\uFF0C\u6B8B\u7559\u8D44\u6E90\u8BF7\u5982\u5B9E\u6392\u67E5\uFF08\u4E8B\u4EF6\u8BA2\u9605/\u5B9A\u65F6\u5668/\u5168\u5C40\u526F\u4F5C\u7528\uFF09\u3002"
1070
+ };
1071
+ return new FgError(
1072
+ BridgeErrorCodes.BRIDGE_LIFECYCLE_FAILED,
1073
+ `\u73B0\u8C61\uFF1A\u6865\u63A5\u5E94\u7528 "${spec}" \u7684 ${phase} \u9636\u6BB5\u5931\u8D25\u3002
1074
+ \u6839\u56E0: ${raw}
1075
+ ` + fixes[phase],
1076
+ { phase, spec, cause: String(raw), ...extra },
1077
+ { cause }
1078
+ );
1079
+ }
1080
+ function bridgeSessionMismatchError(spec, controlled, actual, extra) {
1081
+ const actualText = actual === void 0 ? "undefined\uFF08\u9875\u9762\u5C1A\u65E0 AppContext\uFF09" : typeof actual === "string" ? `"${actual}"` : String(actual);
1082
+ return new FgError(
1083
+ BridgeErrorCodes.BRIDGE_SESSION_MISMATCH,
1084
+ `\u73B0\u8C61\uFF1A\u6865\u63A5\u5E94\u7528 "${spec}" \u7684\u53D7\u63A7 sessionKey\uFF08"${controlled}"\uFF09\u4E0E\u5F53\u524D\u4F1A\u8BDD\u4E0D\u4E00\u81F4\uFF08\u5F53\u524D ${actualText}\uFF09\u3002
1085
+ \u6839\u56E0: AppContext \u662F\u9875\u9762\u7EA7\u5355\u4F8B\uFF0C\u540C\u9875\u6240\u6709\u6865\u63A5\u5B9E\u4F8B\u5FC5\u987B\u4F7F\u7528\u540C\u4E00\u767B\u5F55\u4EE3\u6B21\uFF1B
1086
+ getContext \u5FEB\u7167\u7684 sessionKey\uFF08\u6216\u73B0\u6709 AppContext \u7684 sessionKey\uFF09\u4E0E\u53D7\u63A7\u503C\u4E0D\u540C\u3002
1087
+ \u4FEE\u6CD5: \u2460 \u6362\u8D26\u53F7\u65F6\u5148\u8BA9\u53D7\u63A7 prop \u7F6E null \u5E76\u7B49\u5F85\u5378\u8F7D\uFF0C\u518D clearAppContext() \u6E05\u65E7\u8D26\u53F7\u3001
1088
+ \u51C6\u5907\u65B0\u8D26\u53F7\u5B8C\u6574\u5FEB\u7167\u540E\u66F4\u65B0\u53D7\u63A7 prop\uFF1B
1089
+ \u2461 \u65E0 getContext \u65F6\uFF0C\u5148\u7531\u5BBF\u4E3B\u6865 provideAppContext({...\u65B0\u4F1A\u8BDD, sessionKey}) \u518D\u6302\u8F7D\uFF1B
1090
+ \u2462 \u540C\u9875\u4E0D\u8981\u540C\u65F6\u6302\u8F7D\u4E0D\u540C\u8D26\u53F7\u7684\u6865\u63A5\u5B9E\u4F8B\uFF08\u9875\u9762\u7EA7\u5355\u4F1A\u8BDD\u7EA6\u675F\uFF09\u3002`,
1091
+ { spec, controlled, actual: String(actual), ...extra }
1092
+ );
1093
+ }
1094
+ function invalidSessionKeyError(spec, value) {
1095
+ return new FgError(
1096
+ BridgeErrorCodes.BRIDGE_SESSION_MISMATCH,
1097
+ `\u73B0\u8C61\uFF1A\u6865\u63A5\u5E94\u7528 "${spec}" \u6536\u5230\u975E\u6CD5\u7684\u53D7\u63A7 sessionKey\uFF1A${typeof value === "string" ? '\u7A7A\u5B57\u7B26\u4E32 ""' : `${typeof value}\uFF08${String(value)}\uFF09`}\u3002
1098
+ \u6839\u56E0: sessionKey \u53EA\u63A5\u53D7 undefined\uFF08\u4E0D\u542F\u7528\u53D7\u63A7\u4F1A\u8BDD\uFF09\u3001null\uFF08\u767B\u51FA\u6001\uFF09\u6216\u975E\u7A7A\u5B57\u7B26\u4E32\uFF08\u767B\u5F55\u4EE3\u6B21 ID\uFF09\u3002
1099
+ \u4FEE\u6CD5: \u767B\u51FA\u4F20 null \u800C\u4E0D\u662F\u7A7A\u5B57\u7B26\u4E32\uFF1B\u767B\u5F55\u4EE3\u6B21\u7528\u975E\u7A7A\u5B57\u7B26\u4E32\uFF1B\u4E0D\u8981\u4F20\u6570\u5B57/\u5E03\u5C14\u7B49\u5176\u4ED6\u7C7B\u578B\u3002`,
1100
+ { spec, value: String(value) }
1101
+ );
1102
+ }
1103
+
1104
+ // src/bridge-core.ts
1105
+ function assertBridgeContract(spec, mod) {
1106
+ const contract = mod?.default ?? mod;
1107
+ if (contract === null || typeof contract !== "object" || typeof contract.mount !== "function" || typeof contract.unmount !== "function") {
1108
+ throw invalidBridgeContractError(spec, mod);
1109
+ }
1110
+ return contract;
1111
+ }
1112
+ var activeBridgeSessions = /* @__PURE__ */ new Map();
1113
+ function acquireBridgeSession(spec, sessionKey) {
1114
+ for (const [active] of activeBridgeSessions) {
1115
+ if (active !== sessionKey && (activeBridgeSessions.get(active) ?? 0) > 0) {
1116
+ throw bridgeSessionMismatchError(spec, sessionKey, active, { reason: "active-instance" });
1117
+ }
1118
+ }
1119
+ activeBridgeSessions.set(sessionKey, (activeBridgeSessions.get(sessionKey) ?? 0) + 1);
1120
+ }
1121
+ function releaseBridgeSession(sessionKey) {
1122
+ const n = activeBridgeSessions.get(sessionKey) ?? 0;
1123
+ if (n <= 1) activeBridgeSessions.delete(sessionKey);
1124
+ else activeBridgeSessions.set(sessionKey, n - 1);
1125
+ }
1126
+ function resolveBridgeContext(spec, controlled, getContext) {
1127
+ if (getContext) {
1128
+ let snapshot;
1129
+ try {
1130
+ snapshot = getContext();
1131
+ } catch (e) {
1132
+ throw bridgeLifecycleError("getContext", spec, e);
1133
+ }
1134
+ if (snapshot === null || typeof snapshot !== "object" || typeof snapshot.then === "function") {
1135
+ throw bridgeLifecycleError(
1136
+ "getContext",
1137
+ spec,
1138
+ `getContext \u8FD4\u56DE\u4E86${typeof snapshot.then === "function" ? " Promise/thenable" : "\u975E\u5BF9\u8C61\u503C\uFF08" + typeof snapshot + "\uFF09"}\uFF1B\u5B83\u5FC5\u987B\u662F\u540C\u6B65\u8FD4\u56DE\u5FEB\u7167\u5BF9\u8C61\u7684\u7EAF getter\u3002`
1139
+ );
1140
+ }
1141
+ if (controlled !== void 0 && snapshot.sessionKey !== controlled) {
1142
+ throw bridgeSessionMismatchError(spec, controlled, snapshot.sessionKey, { reason: "getter-snapshot" });
1143
+ }
1144
+ const current = currentSessionKey();
1145
+ if (snapshot.sessionKey !== void 0 && snapshot.sessionKey !== current) {
1146
+ clearAppContext();
1147
+ }
1148
+ return { provide: snapshot };
1149
+ }
1150
+ if (controlled !== void 0) {
1151
+ const current = currentSessionKey();
1152
+ if (current !== controlled) {
1153
+ throw bridgeSessionMismatchError(spec, controlled, current, { reason: "app-context" });
1154
+ }
1155
+ }
1156
+ return {};
1157
+ }
1158
+ function currentSessionKey() {
1159
+ const sk = (globalThis.__FULGURJS_APP_CONFIG__ ?? {}).sessionKey;
1160
+ return typeof sk === "string" && sk !== "" ? sk : void 0;
1161
+ }
1162
+ function assertControlledSessionKey(spec, value) {
1163
+ if (value === void 0 || value === null) return;
1164
+ if (typeof value === "string" && value !== "") return;
1165
+ throw invalidSessionKeyError(spec, value);
1166
+ }
1167
+ function assertBridgeOptions(source, opts) {
1168
+ if (opts.retries !== void 0 && (typeof opts.retries !== "number" || !Number.isInteger(opts.retries) || opts.retries < 0 || opts.retries > 10)) {
1169
+ throw new Error(
1170
+ `[fulgurjs] ${source} \u7684 retries \u914D\u7F6E\u65E0\u6548\uFF1A\u5F53\u524D\u4E3A ${String(opts.retries)}\uFF0C\u5E94\u4E3A 0 \u5230 10 \u7684\u6574\u6570\u3002\u8BF7\u4FEE\u6B63\u9009\u9879\u3002`
1171
+ );
1172
+ }
1173
+ if (opts.timeout !== void 0 && (typeof opts.timeout !== "number" || !Number.isFinite(opts.timeout) || opts.timeout <= 0)) {
1174
+ throw new Error(
1175
+ `[fulgurjs] ${source} \u7684 timeout \u914D\u7F6E\u65E0\u6548\uFF1A\u5F53\u524D\u4E3A ${String(opts.timeout)}\uFF0C\u5E94\u4E3A\u5927\u4E8E 0 \u7684\u6709\u9650\u6BEB\u79D2\u6570\u3002\u8BF7\u4FEE\u6B63\u9009\u9879\u3002`
1176
+ );
1177
+ }
1178
+ }
1179
+ function withBridgeTimeout(p, ms, spec) {
1180
+ return new Promise((resolve, reject) => {
1181
+ const timer = setTimeout(() => {
1182
+ reject(new Error(
1183
+ `[fulgurjs] \u6865\u63A5\u5E94\u7528 "${spec}" \u52A0\u8F7D\u7B49\u5F85\u8D85\u8FC7 ${ms} \u6BEB\u79D2\uFF08\u9002\u914D\u5C42 timeout\uFF09\u3002\u8BE5\u8D85\u65F6\u53EA\u7ED3\u675F\u672C\u6B21\u7B49\u5F85\uFF0C\u4E0D\u53D6\u6D88\u5DF2\u53D1\u51FA\u7684\u5171\u4EAB\u8BF7\u6C42\uFF1B\u53EF\u70B9\u51FB\u300C\u91CD\u8BD5\u52A0\u8F7D\u300D\u5EFA\u7ACB\u65B0\u7684\u5C1D\u8BD5\u3002`
1184
+ ));
1185
+ }, ms);
1186
+ p.then(
1187
+ (v) => {
1188
+ clearTimeout(timer);
1189
+ resolve(v);
1190
+ },
1191
+ (e) => {
1192
+ clearTimeout(timer);
1193
+ reject(e);
1194
+ }
1195
+ );
1196
+ });
1197
+ }
1198
+
1199
+ export {
1200
+ loadRemote,
1201
+ provideAppContext,
1202
+ bridgeLifecycleError,
1203
+ assertBridgeContract,
1204
+ acquireBridgeSession,
1205
+ releaseBridgeSession,
1206
+ resolveBridgeContext,
1207
+ assertControlledSessionKey,
1208
+ assertBridgeOptions,
1209
+ withBridgeTimeout
1210
+ };