@fulgurjs/federation 5.9.2 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.en.md +57 -416
  3. package/README.md +56 -446
  4. package/dist/bridge-app-vue.d.cts +1 -1
  5. package/dist/bridge-app-vue.d.ts +1 -1
  6. package/dist/bridge-core.cjs +1 -1
  7. package/dist/bridge-core.js +2 -2
  8. package/dist/bridge-errors.cjs +1 -1
  9. package/dist/bridge-errors.js +1 -1
  10. package/dist/bridge-host-react.cjs +1 -1
  11. package/dist/bridge-host-react.js +3 -3
  12. package/dist/bridge-host-vue.cjs +1 -1
  13. package/dist/bridge-host-vue.js +3 -3
  14. package/dist/bridge-router-react.d.ts +4 -2
  15. package/dist/bridge-router-react.js +95 -9
  16. package/dist/bridge-router-vue.d.ts +24 -5
  17. package/dist/bridge-router-vue.js +4 -5
  18. package/dist/{chunk-YKIFZIBH.js → chunk-77JZ6U6V.js} +1 -1
  19. package/dist/{chunk-Q6V5O5Y6.js → chunk-ENT3BOTX.js} +1 -1
  20. package/dist/{chunk-WRJG2YG6.js → chunk-ZAYNS7YQ.js} +1 -1
  21. package/dist/cli.js +460 -109
  22. package/dist/index.cjs +25 -38
  23. package/dist/index.js +25 -38
  24. package/dist/react.d.ts +194 -101
  25. package/dist/react.js +4 -0
  26. package/dist/runtime-entry.d.ts +9 -174
  27. package/dist/runtime-entry.js +0 -1
  28. package/dist/runtime.js +1 -1
  29. package/dist/vue-adapter.cjs +5 -1
  30. package/dist/vue-adapter.js +5 -1
  31. package/dist/vue.d.ts +576 -0
  32. package/dist/vue.js +8 -0
  33. package/docs/en/migration.md +210 -0
  34. package/docs/en/reference/api.md +534 -0
  35. package/docs/en/reference/errors.md +86 -0
  36. package/docs/{P5-vite7-8 → maintainers/P5-vite7-8}/345/205/274/345/256/271/347/237/251/351/230/265.md +7 -9
  37. package/docs/{webpack-mf- → maintainers/webpack-mf-}/345/257/271/347/205/247/344/270/216/347/274/272/345/217/243.md +10 -10
  38. package/docs/maintainers//346/262/231/347/256/261/350/276/271/347/225/214/345/256/241/350/256/241.md +43 -0
  39. package/docs/zh/migration.md +210 -0
  40. package/docs/zh/reference/api.md +533 -0
  41. package/docs/zh/reference/errors.md +86 -0
  42. package/examples/templates/README.md +1 -1
  43. package/examples/templates/react-host-vue-remote/pnpm-lock.yaml +7 -7
  44. package/examples/templates/react-host-vue-remote/pnpm-workspace.yaml +1 -1
  45. package/examples/templates/react-host-vue-remote/react-host/README.md +2 -2
  46. package/examples/templates/react-host-vue-remote/react-host/package.json +1 -1
  47. package/examples/templates/react-host-vue-remote/react-host/src/main.tsx +2 -2
  48. package/examples/templates/react-host-vue-remote/scripts/dev.config.json +11 -2
  49. package/examples/templates/react-host-vue-remote/vue-remote/package.json +1 -1
  50. package/examples/templates/react-host-vue-remote/vue-remote/src/bridge.ts +1 -1
  51. package/examples/templates/react-react/host/package.json +1 -1
  52. package/examples/templates/react-react/pnpm-lock.yaml +7 -7
  53. package/examples/templates/react-react/pnpm-workspace.yaml +1 -1
  54. package/examples/templates/react-react/remote/package.json +1 -1
  55. package/examples/templates/react-react/scripts/dev.config.json +11 -2
  56. package/examples/templates/showcase/README.md +6 -6
  57. package/examples/templates/showcase/pnpm-lock.yaml +11 -11
  58. package/examples/templates/showcase/pnpm-workspace.yaml +1 -1
  59. package/examples/templates/showcase/react-host/package.json +1 -1
  60. package/examples/templates/showcase/react-host/src/pages/BridgeVuePage.tsx +1 -1
  61. package/examples/templates/showcase/react-host/src/routing.ts +1 -1
  62. package/examples/templates/showcase/react-remote/package.json +1 -1
  63. package/examples/templates/showcase/react-remote/src/bridge.tsx +1 -1
  64. package/examples/templates/showcase/scripts/dev.config.json +22 -4
  65. package/examples/templates/showcase/vue-host/package.json +1 -1
  66. package/examples/templates/showcase/vue-host/src/pages/BridgeReactPage.vue +1 -1
  67. package/examples/templates/showcase/vue-host/src/routing.ts +3 -13
  68. package/examples/templates/showcase/vue-remote/package.json +1 -1
  69. package/examples/templates/showcase/vue-remote/src/bridge.ts +2 -2
  70. package/examples/templates/vue-host-react-remote/pnpm-lock.yaml +7 -7
  71. package/examples/templates/vue-host-react-remote/pnpm-workspace.yaml +1 -1
  72. package/examples/templates/vue-host-react-remote/react-remote/package.json +1 -1
  73. package/examples/templates/vue-host-react-remote/scripts/dev.config.json +11 -2
  74. package/examples/templates/vue-host-react-remote/vue-host/README.md +2 -2
  75. package/examples/templates/vue-host-react-remote/vue-host/package.json +1 -1
  76. package/examples/templates/vue-host-react-remote/vue-host/src/App.vue +2 -2
  77. package/examples/templates/vue-vue/host/package.json +1 -1
  78. package/examples/templates/vue-vue/host/src/main.ts +28 -21
  79. package/examples/templates/vue-vue/host/src/pages/HomePage.vue +1 -1
  80. package/examples/templates/vue-vue/pnpm-lock.yaml +7 -7
  81. package/examples/templates/vue-vue/pnpm-workspace.yaml +1 -1
  82. package/examples/templates/vue-vue/remote/package.json +1 -1
  83. package/examples/templates/vue-vue/scripts/dev.config.json +11 -2
  84. package/package.json +24 -37
  85. package/dist/bridge-core-D37VanBl.d.ts +0 -99
  86. package/dist/bridge-host-react-CGymCzD2.d.ts +0 -35
  87. package/dist/bridge-host-vue-B3GHaQAC.d.ts +0 -32
  88. package/dist/bridge-react.d.ts +0 -14
  89. package/dist/bridge-react.js +0 -3
  90. package/dist/bridge-vue.d.ts +0 -8
  91. package/dist/bridge-vue.js +0 -3
  92. package/dist/bridge.d.ts +0 -18
  93. package/dist/bridge.js +0 -5
  94. package/dist/chunk-GLASM5EX.js +0 -1730
  95. package/dist/chunk-V6EASSCR.js +0 -249
  96. package/dist/chunk-X34EJB4H.js +0 -207
  97. package/docs/API.en.md +0 -339
  98. package/docs/API.md +0 -914
  99. package/docs//346/262/231/347/256/261/350/276/271/347/225/214/345/256/241/350/256/241.md +0 -45
  100. package/docs//350/277/201/347/247/273/346/214/207/345/215/227.md +0 -179
@@ -1,1730 +0,0 @@
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 resolvedShareSnapshots = /* @__PURE__ */ new Map();
256
- const shareRequestKey = (name, opts) => JSON.stringify([
257
- opts.shareScope || "default",
258
- opts.shareKey || name,
259
- opts.requiredVersion ?? null,
260
- !!opts.singleton,
261
- !!opts.strictVersion
262
- ]);
263
- const loadedModules = /* @__PURE__ */ new Map();
264
- const remoteManifests = /* @__PURE__ */ new Map();
265
- const stylesheetLoads = /* @__PURE__ */ new WeakMap();
266
- const containerInitScopes = /* @__PURE__ */ new WeakMap();
267
- const hooks = {};
268
- const applyPlugins = () => {
269
- for (const p of plugins) {
270
- try {
271
- p.init?.(hooks);
272
- } catch (err) {
273
- 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);
274
- }
275
- }
276
- };
277
- const lifecycleStates = /* @__PURE__ */ new Map();
278
- let lifecycleSyncRemote;
279
- const lifecycleOf = (name) => {
280
- let st = lifecycleStates.get(name);
281
- if (!st) lifecycleStates.set(name, st = {});
282
- return st;
283
- };
284
- const currentAppContext = () => globalThis[APP_CONTEXT_GLOBAL_KEY] ?? {};
285
- async function invokeLifecycleFn(remoteName, fn, ctx, phase) {
286
- const wrap = (err) => {
287
- if (err instanceof FgError) return err;
288
- return new FgError(
289
- ErrorCodes.SETUP_RUN_FAILED,
290
- `\u73B0\u8C61\uFF1A\u8FDC\u7A0B\u5E94\u7528 "${remoteName}" \u7684 ${phase} \u521D\u59CB\u5316\u5931\u8D25\u3002
291
- \u539F\u56E0\uFF1A${String(err?.message ?? err)}
292
- \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`,
293
- { remote: remoteName, phase },
294
- { cause: err }
295
- );
296
- };
297
- lifecycleSyncRemote = remoteName;
298
- let result;
299
- try {
300
- result = fn(ctx);
301
- } catch (err) {
302
- throw wrap(err);
303
- } finally {
304
- lifecycleSyncRemote = void 0;
305
- }
306
- try {
307
- await result;
308
- } catch (err) {
309
- throw wrap(err);
310
- }
311
- }
312
- async function runSetupPhase(remote, container, setupKey, st) {
313
- remote.debug.setup = "pending";
314
- try {
315
- if (remote.manifestUrl) {
316
- try {
317
- await preloadRemote2(`${remote.name}/${setupKey}`);
318
- } catch {
319
- }
320
- }
321
- const mod = await container.get(setupKey);
322
- const setupFn = mod?.default;
323
- if (typeof setupFn !== "function") {
324
- throw new FgError(
325
- ErrorCodes.SETUP_INVALID_EXPORT,
326
- `\u73B0\u8C61\uFF1A\u8FDC\u7A0B\u5E94\u7528 "${remote.name}" \u7684 setup \u6A21\u5757 "${setupKey}" \u65E0\u6CD5\u521D\u59CB\u5316\u3002
327
- \u539F\u56E0\uFF1A\u9ED8\u8BA4\u5BFC\u51FA\u7C7B\u578B\u4E3A ${mod == null ? String(mod) : typeof setupFn}\uFF0C\u5E94\u4E3A\u51FD\u6570\u3002
328
- \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`,
329
- { remote: remote.name, module: setupKey }
330
- );
331
- }
332
- if (mod.onSession !== void 0 && typeof mod.onSession !== "function") {
333
- throw new FgError(
334
- ErrorCodes.SETUP_INVALID_EXPORT,
335
- `\u73B0\u8C61\uFF1A\u8FDC\u7A0B\u5E94\u7528 "${remote.name}" \u7684 setup \u6A21\u5757 "${setupKey}" \u65E0\u6CD5\u521D\u59CB\u5316\u3002
336
- \u539F\u56E0\uFF1AonSession \u5BFC\u51FA\u7C7B\u578B\u4E3A ${typeof mod.onSession}\uFF0C\u5E94\u4E3A\u51FD\u6570\u3002
337
- \u4FEE\u6CD5\uFF1A\u5C06 onSession \u6539\u4E3A\u5177\u540D\u5BFC\u51FA\u7684\u51FD\u6570\uFF0C\u6216\u5220\u9664\u8BE5\u5BFC\u51FA\u3002`,
338
- { remote: remote.name, module: setupKey }
339
- );
340
- }
341
- st.setupExports = mod;
342
- await invokeLifecycleFn(
343
- remote.name,
344
- setupFn,
345
- { appContext: currentAppContext(), signal: (st.controller ??= new AbortController()).signal },
346
- "setup"
347
- );
348
- remote.debug.setup = "ready";
349
- } catch (err) {
350
- remote.debug.setup = "failed";
351
- st.setupExports = void 0;
352
- throw err;
353
- }
354
- }
355
- async function ensureSessionPhase(remote, st, onSession) {
356
- const appContext = currentAppContext();
357
- const sessionKey = appContext?.sessionKey;
358
- if (typeof sessionKey !== "string" || sessionKey === "") {
359
- throw new FgError(
360
- ErrorCodes.SETUP_SESSION_KEY_MISSING,
361
- `\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
362
- \u539F\u56E0\uFF1A\u5F53\u524D\u503C\u4E3A ${sessionKey === void 0 ? "\u7F3A\u5931" : JSON.stringify(sessionKey)}\u3002
363
- \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`,
364
- { remote: remote.name }
365
- );
366
- }
367
- if (st.sessionPromise && st.sessionKey === sessionKey) return st.sessionPromise;
368
- st.controller?.abort();
369
- const controller = new AbortController();
370
- st.controller = controller;
371
- st.sessionKey = sessionKey;
372
- const prev = st.sessionPromise ? st.sessionPromise.catch(() => {
373
- }) : Promise.resolve();
374
- const run = (async () => {
375
- await prev;
376
- if (controller.signal.aborted) return;
377
- await invokeLifecycleFn(remote.name, onSession, { appContext, sessionKey, signal: controller.signal }, "onSession");
378
- })();
379
- st.sessionPromise = run;
380
- run.catch(() => {
381
- if (st.sessionKey === sessionKey) {
382
- st.sessionKey = void 0;
383
- st.sessionPromise = void 0;
384
- }
385
- });
386
- return run;
387
- }
388
- async function ensureLifecycle(remote, container) {
389
- const setupKey = container?.[SETUP_META_KEY];
390
- if (typeof setupKey !== "string" || setupKey === "") return;
391
- const st = lifecycleOf(remote.name);
392
- if (!st.setupPromise) {
393
- st.setupPromise = runSetupPhase(remote, container, setupKey, st).catch((err) => {
394
- st.setupPromise = void 0;
395
- throw err;
396
- });
397
- }
398
- await st.setupPromise;
399
- const onSession = st.setupExports?.onSession;
400
- if (typeof onSession === "function") {
401
- await ensureSessionPhase(remote, st, onSession);
402
- }
403
- }
404
- function clearSessionState2() {
405
- for (const st of lifecycleStates.values()) {
406
- try {
407
- st.controller?.abort();
408
- } catch {
409
- }
410
- st.sessionKey = void 0;
411
- st.sessionPromise = void 0;
412
- }
413
- }
414
- const getScope = (name) => shareScopeMap2[name] ??= /* @__PURE__ */ Object.create(null);
415
- function emitError(info) {
416
- try {
417
- hooks.onRemoteError?.(info);
418
- } catch {
419
- }
420
- if (typeof window !== "undefined") {
421
- window.dispatchEvent(new CustomEvent("fulgurjs:error", { detail: info }));
422
- }
423
- }
424
- function initSharing2(scopeName = "default") {
425
- getScope(scopeName);
426
- return shareScopeMap2;
427
- }
428
- function registerShare2(scopeName, name, version, get, opts = {}) {
429
- const scope = getScope(scopeName || "default");
430
- const own = Object.hasOwn(scope, name) ? scope[name] : void 0;
431
- let byName = own && typeof own === "object" ? own : void 0;
432
- if (!byName) {
433
- byName = /* @__PURE__ */ Object.create(null);
434
- scope[name] = byName;
435
- }
436
- if (Object.hasOwn(byName, version) && byName[version]) {
437
- return;
438
- }
439
- byName[version] = { version, get, from: opts.from ?? "unknown", eager: !!opts.eager, loaded: !!opts.loaded };
440
- }
441
- function assertRemoteParams(r) {
442
- const check = (v, name, int10) => {
443
- if (v === void 0) return;
444
- const ok = typeof v === "number" && (int10 ? Number.isInteger(v) && v >= 0 && v <= 10 : Number.isFinite(v) && v > 0);
445
- if (!ok) {
446
- throw new Error(
447
- `[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`
448
- );
449
- }
450
- };
451
- check(r.timeout, "timeout");
452
- check(r.retries, "retries", true);
453
- if (r.breaker) {
454
- check(r.breaker.threshold, "breaker.threshold");
455
- check(r.breaker.resetMs, "breaker.resetMs");
456
- }
457
- }
458
- function registerRemotes2(list) {
459
- for (const r of list) {
460
- assertRemoteParams(r);
461
- const prev = remotes.get(r.name);
462
- remotes.set(r.name, {
463
- timeout: DEFAULT_TIMEOUT,
464
- retries: DEFAULT_RETRIES,
465
- ...prev,
466
- ...r,
467
- // 熔断参数随最新配置刷新;计数状态(fails/openUntil)跨注册保留——半开窗口不被重置
468
- breakerThreshold: r.breaker?.threshold ?? prev?.breakerThreshold ?? BREAKER_THRESHOLD,
469
- breakerResetMs: r.breaker?.resetMs ?? prev?.breakerResetMs ?? BREAKER_RESET_MS,
470
- state: prev?.state ?? "idle",
471
- breakerState: prev?.breakerState ?? { fails: 0, openUntil: 0 },
472
- entryInflight: prev?.entryInflight ?? /* @__PURE__ */ new Map(),
473
- entryFailCounts: prev?.entryFailCounts ?? /* @__PURE__ */ new Map(),
474
- debug: prev?.debug ?? { entry: r.entry, status: "idle", setup: "none" }
475
- });
476
- }
477
- }
478
- function registerRemote2(remote) {
479
- registerRemotes2([remote]);
480
- }
481
- function registerPlugins2(list) {
482
- plugins.push(...list);
483
- resolvedShareSnapshots.clear();
484
- applyPlugins();
485
- }
486
- function selectShareEntry(name, opts, readyOnly = false) {
487
- const scopeName = opts.shareScope || "default";
488
- const shareKey = opts.shareKey || name;
489
- const scope = getScope(scopeName);
490
- const byName = scope[shareKey] || {};
491
- const versions = Object.keys(byName).filter((v) => !readyOnly || opts.singleton || byName[v].value !== void 0);
492
- if (readyOnly && versions.length === 0) return void 0;
493
- const req = opts.requiredVersion;
494
- const satisfying = versions.filter(
495
- (v) => req === void 0 || req === false || satisfies(v, req)
496
- );
497
- let pick;
498
- if (opts.singleton && versions.length > 0) {
499
- const loadedVersions = versions.filter((v) => byName[v].loaded);
500
- pick = [...loadedVersions.length ? loadedVersions : versions].sort(compareVersions).pop();
501
- if (!satisfying.includes(pick)) {
502
- const entry0 = byName[pick];
503
- 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
504
- \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
505
- \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
506
- \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`;
507
- if (opts.strictVersion) {
508
- const err = new FgError(ErrorCodes.SHARE_STRICT_VERSION, msg, {
509
- shareKey,
510
- requiredVersion: req
511
- });
512
- emitError({ remote: entry0.from, error: err });
513
- throw err;
514
- }
515
- const warningKey = `${scopeName}|${shareKey}|${req ?? "*"}|${pick}|${versions.join(",")}`;
516
- if (!reportedSingletonSkews.has(warningKey)) {
517
- reportedSingletonSkews.add(warningKey);
518
- console.warn(`[fulgurjs:MFU-010] ${msg}`);
519
- }
520
- }
521
- } else {
522
- pick = [...satisfying].sort(compareVersions).pop();
523
- if (!pick) {
524
- if (opts.strictVersion) {
525
- const err = new FgError(
526
- ErrorCodes.SHARE_STRICT_VERSION,
527
- `\u73B0\u8C61\uFF1A\u5171\u4EAB\u4F5C\u7528\u57DF "${scopeName}" \u4E2D\u627E\u4E0D\u5230\u7B26\u5408\u8981\u6C42\u7684 "${shareKey}"\u3002
528
- \u539F\u56E0\uFF1A\u8981\u6C42\u7248\u672C ${req}\uFF0C\u5F53\u524D\u53EF\u7528\u7248\u672C\u4E3A ${versions.join("\u3001") || "\u65E0"}\u3002
529
- \u4FEE\u6CD5\uFF1A\u7EDF\u4E00\u5BBF\u4E3B\u4E0E\u8FDC\u7A0B\u7684\u4F9D\u8D56\u7248\u672C\uFF0C\u6216\u4FEE\u6B63 shared.requiredVersion\u3002`
530
- );
531
- emitError({ remote: shareKey, error: err });
532
- throw err;
533
- }
534
- return void 0;
535
- }
536
- }
537
- return byName[pick];
538
- }
539
- function registerFallbackInstance(name, opts, instance) {
540
- const localVersion = opts.localVersion;
541
- if (!localVersion || instance === void 0) return;
542
- const scopeName = opts.shareScope || "default";
543
- const key = opts.shareKey || name;
544
- const scope = getScope(scopeName);
545
- const byName = Object.hasOwn(scope, key) ? scope[key] : void 0;
546
- if (byName && Object.keys(byName).length > 0) {
547
- try {
548
- const pick = selectShareEntry(name, opts);
549
- if (pick && pick.value === void 0) {
550
- if (pick.version === localVersion) {
551
- pick.value = instance;
552
- pick.loaded = true;
553
- } else if (opts.singleton && !pick.pendingGet) {
554
- registerShare2(scopeName, key, localVersion, () => Promise.resolve(instance), {
555
- from: "fulgurjs:local-fallback",
556
- loaded: true
557
- });
558
- const local = scope[key][localVersion];
559
- local.value = instance;
560
- local.loaded = true;
561
- }
562
- }
563
- } catch {
564
- }
565
- return;
566
- }
567
- registerShare2(scopeName, key, localVersion, () => Promise.resolve(instance), {
568
- from: "fulgurjs:local-fallback",
569
- loaded: true
570
- });
571
- const fresh = scope[key]?.[localVersion];
572
- if (fresh) fresh.value = instance;
573
- }
574
- async function loadShare2(name, opts = {}) {
575
- const scopeName = opts.shareScope || "default";
576
- const shareKey = opts.shareKey || name;
577
- const byName = getScope(scopeName)[shareKey] || {};
578
- const requestKey = shareRequestKey(name, opts);
579
- const snapshot = hooks.resolveShare ? resolvedShareSnapshots.get(requestKey) : void 0;
580
- let entry = snapshot || selectShareEntry(name, opts);
581
- if (!entry) {
582
- if (opts.fallback) {
583
- const instance2 = await opts.fallback();
584
- registerFallbackInstance(name, opts, instance2);
585
- return instance2;
586
- }
587
- const err = new FgError(
588
- ErrorCodes.SHARE_NOT_AVAILABLE,
589
- `\u73B0\u8C61\uFF1A\u65E0\u6CD5\u52A0\u8F7D\u5171\u4EAB\u4F9D\u8D56 "${shareKey}"\u3002
590
- \u539F\u56E0\uFF1A\u4F5C\u7528\u57DF "${scopeName}" \u4E2D\u6CA1\u6709\u7B26\u5408 ${opts.requiredVersion ?? "\u4EFB\u610F\u7248\u672C"} \u8981\u6C42\u7684\u7248\u672C\uFF0C\u4E14\u672A\u914D\u7F6E\u672C\u5730\u526F\u672C\u3002
591
- \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`,
592
- { shareKey, requiredVersion: opts.requiredVersion }
593
- );
594
- emitError({ remote: shareKey, error: err });
595
- throw err;
596
- }
597
- if (hooks.resolveShare && !snapshot) {
598
- const picked = await hooks.resolveShare({
599
- shareKey,
600
- shareScope: scopeName,
601
- requiredVersion: opts.requiredVersion,
602
- picked: entry,
603
- available: Object.values(byName)
604
- });
605
- if (picked) entry = picked;
606
- resolvedShareSnapshots.set(requestKey, entry);
607
- }
608
- if (entry.value !== void 0) {
609
- resolvedShareSnapshots.set(shareRequestKey(name, opts), entry);
610
- return entry.value;
611
- }
612
- entry.loaded = true;
613
- if (!entry.pendingGet) {
614
- try {
615
- entry.pendingGet = entry.get();
616
- } catch (e) {
617
- entry.pendingGet = Promise.reject(e);
618
- }
619
- }
620
- const pending = entry.pendingGet;
621
- let instance;
622
- try {
623
- instance = await pending;
624
- } catch (e) {
625
- if (entry.value !== void 0) {
626
- resolvedShareSnapshots.set(shareRequestKey(name, opts), entry);
627
- return entry.value;
628
- }
629
- entry.loaded = false;
630
- if (resolvedShareSnapshots.get(requestKey) === entry) resolvedShareSnapshots.delete(requestKey);
631
- throw e;
632
- } finally {
633
- if (entry.pendingGet === pending) entry.pendingGet = void 0;
634
- }
635
- if (entry.value === void 0) entry.value = instance;
636
- resolvedShareSnapshots.set(shareRequestKey(name, opts), entry);
637
- return entry.value;
638
- }
639
- function getLoadedShare2(name, opts = {}) {
640
- if (hooks.resolveShare) return resolvedShareSnapshots.get(shareRequestKey(name, opts))?.value;
641
- return selectShareEntry(name, opts, true)?.value;
642
- }
643
- function pinLoadedShare2(name, opts, localVersion, instance) {
644
- registerFallbackInstance(name, { ...opts, localVersion }, instance);
645
- }
646
- function loadShareSync2(name, opts) {
647
- const scopeName = opts.shareScope || "default";
648
- const shareKey = opts.shareKey || name;
649
- if (hooks.resolveShare) {
650
- const snapKey = shareRequestKey(name, opts);
651
- const snap = resolvedShareSnapshots.get(snapKey);
652
- if (snap && snap.value !== void 0) return { kind: "ready", value: snap.value };
653
- const pickedRaw = snap || hooks.resolveShare({
654
- shareKey,
655
- shareScope: scopeName,
656
- requiredVersion: opts.requiredVersion,
657
- picked: selectShareEntry(name, opts),
658
- available: Object.values(getScope(scopeName)[shareKey] || {})
659
- });
660
- if (pickedRaw && typeof pickedRaw.then === "function") {
661
- void Promise.resolve(pickedRaw).catch(() => {
662
- });
663
- throw new FgError(
664
- ErrorCodes.SHARE_NOT_AVAILABLE,
665
- `\u73B0\u8C61\uFF1AresolveShare \u5BF9\u5171\u4EAB "${shareKey}" \u8FD4\u56DE\u4E86 Promise\uFF0C\u4F46\u8BE5\u5171\u4EAB\u6B63\u88AB\u540C\u6B65\u9759\u6001\u5BFC\u5165\u6D88\u8D39\uFF08Vite 8 \u540C\u6B65\u534F\u5546\u95E8\u9762\uFF09\u3002
666
- \u539F\u56E0\uFF1A\u6A21\u5757\u6C42\u503C\u671F\u65E0\u6CD5\u7B49\u5F85\u5F02\u6B65\u51B3\u7B56\uFF1B\u9759\u6001\u5BFC\u5165\u4E0D\u80FD\u9759\u9ED8\u5FFD\u7565 hook \u51B3\u7B56\u3002
667
- \u4FEE\u6CD5\uFF1AVite 8 HTML \u5165\u53E3\u4F1A\u5148\u5B8C\u6210\u5F02\u6B65\u534F\u5546\uFF1B\u5176\u4ED6\u5165\u53E3\u8BF7\u5148\u52A8\u6001 loadShare \u5EFA\u7ACB\u5FEB\u7167\uFF0C\u518D\u5BFC\u5165\u6D88\u8D39\u8005\uFF0C\u6216\u8BA9\u8BE5\u952E\u540C\u6B65\u8FD4\u56DE picked\u3002`,
668
- { shareKey, requiredVersion: opts.requiredVersion, syncUnsupported: true }
669
- );
670
- }
671
- const picked = pickedRaw;
672
- if (picked) {
673
- if (picked.value !== void 0) {
674
- resolvedShareSnapshots.set(shareRequestKey(name, opts), picked);
675
- return { kind: "ready", value: picked.value };
676
- }
677
- if (picked.version !== void 0 && picked.version === opts.localVersion) return { kind: "local" };
678
- throw new FgError(
679
- ErrorCodes.SHARE_NOT_AVAILABLE,
680
- `\u73B0\u8C61\uFF1AresolveShare \u4E3A "${shareKey}" \u9009\u62E9\u4E86\u672A\u5C31\u7EEA\u7684 ${picked.version ?? "\u6761\u76EE"}\u3002
681
- \u539F\u56E0\uFF1A\u540C\u6B65\u6D88\u8D39\u4E0D\u80FD\u7B49\u5F85\u52A0\u8F7D\u3002
682
- \u4FEE\u6CD5\uFF1A\u5148\u52A8\u6001 loadShare \u518D\u5BFC\u5165\u6D88\u8D39\u8005\uFF0C\u6216\u9009\u62E9\u5DF2\u5C31\u7EEA\u5B9E\u4F8B/\u672C\u5730\u7248\u672C ${opts.localVersion ?? "\u672A\u58F0\u660E"}\u3002`,
683
- { shareKey, requiredVersion: opts.requiredVersion, syncUnsupported: true }
684
- );
685
- }
686
- }
687
- const entry = selectShareEntry(name, opts);
688
- if (!entry) return { kind: "local" };
689
- if (entry.value !== void 0) return { kind: "ready", value: entry.value };
690
- if (entry.version !== void 0 && entry.version === opts.localVersion) return { kind: "local" };
691
- if (opts.singleton || opts.strictVersion && opts.requiredVersion && (!opts.localVersion || !satisfies(opts.localVersion, opts.requiredVersion))) {
692
- if (opts.strictVersion || entry.pendingGet) {
693
- throw new FgError(
694
- ErrorCodes.SHARE_NOT_AVAILABLE,
695
- `\u73B0\u8C61\uFF1A\u5171\u4EAB "${shareKey}" \u9009\u4E2D\u4E86 ${entry.from} \u63D0\u4F9B\u7684 ${entry.version}\uFF08\u5C1A\u672A\u52A0\u8F7D\uFF09\uFF0C\u540C\u6B65\u9759\u6001\u5BFC\u5165\u65E0\u6CD5\u7B49\u5F85\u5176\u5B9E\u4F8B\u5C31\u7EEA\u3002
696
- \u539F\u56E0\uFF1A\u4E0D\u80FD\u6539\u7528\u4E0D\u517C\u5BB9\u7684\u672C\u5730\u526F\u672C\u6216\u5206\u88C2\u5355\u4F8B\u3002
697
- \u4FEE\u6CD5\uFF1A\u8BA9\u8BE5\u5171\u4EAB\u7684\u9996\u6B21\u6D88\u8D39\u5148\u7ECF\u4E00\u6B21\u52A8\u6001 loadShare \u5B8C\u6210\u52A0\u8F7D\uFF0C\u6216\u5C06\u672C\u5E94\u7528\u7248\u672C\u4E0E\u4F5C\u7528\u57DF\u63D0\u4F9B\u7248\u672C\u5BF9\u9F50\u3002`,
698
- { shareKey, requiredVersion: opts.requiredVersion, syncUnsupported: true }
699
- );
700
- }
701
- const warnKey = `${scopeName}|${shareKey}|${entry.version}|${opts.localVersion}`;
702
- if (!reportedSingletonSkews.has(warnKey)) {
703
- reportedSingletonSkews.add(warnKey);
704
- console.warn(
705
- `[fulgurjs:MFU-010] \u5171\u4EAB\u5355\u4F8B "${shareKey}"\uFF1A\u4F5C\u7528\u57DF\u4E2D ${entry.from} \u63D0\u4F9B\u7684 ${entry.version} \u5C1A\u672A\u52A0\u8F7D\uFF0C\u9996\u4E2A\u6D88\u8D39\u8005\u4EE5\u672C\u5730\u7248\u672C ${opts.localVersion ?? ""} \u63A5\u7BA1\uFF08\u540E\u7EED\u6D88\u8D39\u8005\u6309\u5DF2\u52A0\u8F7D\u4F18\u5148\u6536\u655B\u5230\u8BE5\u5B9E\u4F8B\uFF09\u3002`
706
- );
707
- }
708
- return { kind: "local" };
709
- }
710
- console.warn(
711
- `[fulgurjs] \u5171\u4EAB "${shareKey}"\uFF1A\u975E\u5355\u4F8B\u540C\u6B65\u6D88\u8D39\u4F7F\u7528\u672C\u5730 ${opts.localVersion ?? ""}\uFF0C\u8FDC\u7A0B ${entry.version} \u5C1A\u672A\u5C31\u7EEA\u3002`
712
- );
713
- return { kind: "local" };
714
- }
715
- async function prepareShares2(requests) {
716
- if (!hooks.resolveShare) return;
717
- for (const { name, opts } of requests) {
718
- const key = shareRequestKey(name, opts);
719
- if (resolvedShareSnapshots.has(key)) continue;
720
- const entry = selectShareEntry(name, opts);
721
- const picked = await hooks.resolveShare({
722
- shareKey: opts.shareKey || name,
723
- shareScope: opts.shareScope || "default",
724
- requiredVersion: opts.requiredVersion,
725
- picked: entry,
726
- available: Object.values(getScope(opts.shareScope || "default")[opts.shareKey || name] || {})
727
- }) || entry;
728
- if (picked) resolvedShareSnapshots.set(key, picked);
729
- }
730
- for (const { name, opts } of requests) await loadShare2(name, opts);
731
- }
732
- function sanitizeUrl(u) {
733
- try {
734
- const x = new URL(u, typeof location < "u" ? location.href : "http://f.invalid");
735
- return x.protocol + "//" + x.host + x.pathname;
736
- } catch {
737
- return String(u).replace(/[?#].*$/, "");
738
- }
739
- }
740
- function withTimeout(p, ms, label) {
741
- return new Promise((resolve, reject) => {
742
- const t = setTimeout(() => reject(new Error(`\u7B49\u5F85\u8D85\u8FC7 ${ms} \u6BEB\u79D2\uFF1A${label}`)), ms);
743
- p.then(
744
- (v) => {
745
- clearTimeout(t);
746
- resolve(v);
747
- },
748
- (e) => {
749
- clearTimeout(t);
750
- reject(e);
751
- }
752
- );
753
- });
754
- }
755
- const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
756
- const nameMismatchWarned = /* @__PURE__ */ new Set();
757
- function validateContainerInterface(container, remote) {
758
- if (!container || typeof container.get !== "function") {
759
- throw new Error(
760
- `\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`
761
- );
762
- }
763
- if (container.name && remote.name && container.name !== remote.name) {
764
- 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`;
765
- if (remote.promise) {
766
- const key = `${remote.name}\u2194${container.name}`;
767
- if (!nameMismatchWarned.has(key)) {
768
- nameMismatchWarned.add(key);
769
- console.warn(`[fulgurjs] ${msg} \u5F53\u524D\u662F\u52A8\u6001 Promise \u8FDC\u7A0B\uFF0C\u7EE7\u7EED\u52A0\u8F7D\u3002`);
770
- }
771
- } else {
772
- throw Object.assign(new Error(msg), { code: ErrorCodes.REMOTE_NAME_MISMATCH });
773
- }
774
- }
775
- }
776
- async function importEntry(url, remote) {
777
- const fails = remote.entryFailCounts.get(url) ?? 0;
778
- const attemptUrl = fails > 0 ? `${url}${url.includes("?") ? "&" : "?"}fulgurjs_retry=${fails}` : url;
779
- const cached = remote.entryInflight.get(url);
780
- const inflight = cached ?? import(
781
- /* @vite-ignore */
782
- attemptUrl
783
- );
784
- if (!cached) {
785
- remote.entryInflight.set(url, inflight);
786
- inflight.catch(() => {
787
- remote.entryInflight.delete(url);
788
- remote.entryFailCounts.set(url, (remote.entryFailCounts.get(url) ?? 0) + 1);
789
- });
790
- }
791
- return withTimeout(inflight, remote.timeout ?? DEFAULT_TIMEOUT, `\u52A0\u8F7D\u8FDC\u7A0B\u5165\u53E3 ${sanitizeUrl(url)}`);
792
- }
793
- async function acquireContainer(remote, overrides) {
794
- const scopeKey = remote.shareScope || "default";
795
- if (remote.container) {
796
- const prevScope = containerInitScopes.get(remote.container);
797
- if (prevScope && prevScope !== scopeKey) {
798
- throw new FgError(
799
- ErrorCodes.CONTAINER_REINIT_CONFLICT,
800
- `\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`
801
- );
802
- }
803
- return remote.container;
804
- }
805
- if (remote.containerPromise) return remote.containerPromise;
806
- const b = remote.breakerState;
807
- if (b.openUntil > Date.now()) {
808
- const err = new FgError(
809
- ErrorCodes.REMOTE_LOAD_FAILED,
810
- `\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`,
811
- { remote: remote.name }
812
- );
813
- emitError({ remote: remote.name, error: err });
814
- throw err;
815
- }
816
- remote.state = "loading";
817
- remote.debug.status = "loading";
818
- const containerPromise = (async () => {
819
- const loaders = [];
820
- if (remote.promise) {
821
- loaders.push(async () => {
822
- const resolved = await withTimeout(
823
- remote.promise(),
824
- remote.timeout ?? DEFAULT_TIMEOUT,
825
- `\u89E3\u6790\u52A8\u6001\u8FDC\u7A0B\u5E94\u7528 "${remote.name}"`
826
- );
827
- const container2 = typeof resolved === "string" ? await importEntry(resolved, remote) : resolved;
828
- validateContainerInterface(container2, remote);
829
- return container2;
830
- });
831
- } else {
832
- for (const url of [remote.entry, ...remote.fallback ?? []]) {
833
- loaders.push(async () => {
834
- const container2 = await importEntry(url, remote);
835
- validateContainerInterface(container2, remote);
836
- return container2;
837
- });
838
- }
839
- }
840
- let container;
841
- let lastErr;
842
- let succeeded = false;
843
- const maxRetries = overrides?.retries ?? remote.retries ?? DEFAULT_RETRIES;
844
- for (const loader of loaders) {
845
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
846
- const start = performance.now();
847
- try {
848
- container = await loader();
849
- remote.lastLoadMs = Math.round(performance.now() - start);
850
- remote.debug.lastLoadMs = remote.lastLoadMs;
851
- succeeded = true;
852
- break;
853
- } catch (err) {
854
- lastErr = err;
855
- if (err?.code) throw err;
856
- if (attempt < maxRetries) {
857
- await sleep(Math.min(BACKOFF_MAX_MS, 200 * 2 ** attempt) * (1 - BACKOFF_JITTER * (1 - 2 * Math.random())));
858
- }
859
- }
860
- }
861
- if (succeeded) break;
862
- }
863
- if (!succeeded || !container) {
864
- const shown = sanitizeUrl(remote.entry);
865
- const hints = [];
866
- if (/^https?:\/\//.test(remote.entry) && typeof location !== "undefined") {
867
- 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`);
868
- hints.push(`2. \u6838\u5BF9 federation({ remotes }) \u4E2D\u7684\u5730\u5740\uFF1B\u5F00\u53D1\u548C\u751F\u4EA7\u5730\u5740\u53EF\u4EE5\u4E0D\u540C\u3002`);
869
- hints.push(`3. \u82E5\u8DE8\u57DF\uFF0C\u68C0\u67E5\u8FDC\u7A0B\u5F00\u53D1\u670D\u52A1\u7684 server.cors \u914D\u7F6E\u3002`);
870
- } else {
871
- 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`);
872
- 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`);
873
- }
874
- throw new FgError(
875
- ErrorCodes.REMOTE_LOAD_FAILED,
876
- `\u73B0\u8C61\uFF1A\u65E0\u6CD5\u4ECE ${shown} \u52A0\u8F7D\u8FDC\u7A0B\u5E94\u7528 "${remote.name}"\u3002
877
- \u539F\u56E0\uFF1A${String(lastErr?.message ?? lastErr)}
878
- \u4FEE\u6CD5\uFF1A
879
- ${hints.join("\n")}`,
880
- { remote: remote.name }
881
- );
882
- }
883
- validateContainerInterface(container, remote);
884
- const prevScope = containerInitScopes.get(container);
885
- if (prevScope && prevScope !== scopeKey) {
886
- throw new FgError(
887
- ErrorCodes.CONTAINER_REINIT_CONFLICT,
888
- `\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`
889
- );
890
- }
891
- if (typeof container.init === "function") {
892
- await container.init(shareScopeMap2);
893
- }
894
- containerInitScopes.set(container, scopeKey);
895
- remote.container = container;
896
- remote.state = "loaded";
897
- remote.debug.status = "loaded";
898
- b.fails = 0;
899
- return container;
900
- })();
901
- remote.containerPromise = containerPromise;
902
- try {
903
- return await containerPromise;
904
- } catch (err) {
905
- remote.containerPromise = void 0;
906
- remote.state = "failed";
907
- remote.debug.status = "failed";
908
- remote.debug.error = String(err?.message ?? err);
909
- b.fails += 1;
910
- if (b.fails >= remote.breakerThreshold) {
911
- b.openUntil = Date.now() + remote.breakerResetMs;
912
- b.fails = 0;
913
- }
914
- if (err instanceof FgError) {
915
- emitError({ remote: remote.name, error: err });
916
- throw err;
917
- }
918
- if (err?.code) {
919
- emitError({ remote: remote.name, error: err });
920
- throw err;
921
- }
922
- 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`, {
923
- remote: remote.name
924
- });
925
- emitError({ remote: remote.name, error: wrapped });
926
- throw wrapped;
927
- }
928
- }
929
- function parseSpec2(spec) {
930
- const idx = spec.indexOf("/");
931
- if (idx === -1) return { remote: spec, module: "" };
932
- return { remote: spec.slice(0, idx), module: normalizeModuleName(spec.slice(idx + 1)) };
933
- }
934
- function normalizeModuleName(m) {
935
- if (!m) return "";
936
- return m.startsWith(".") ? m : `./${m}`;
937
- }
938
- async function loadRemote2(spec, opts) {
939
- const { remote: name, module } = parseSpec2(spec);
940
- if (module && lifecycleSyncRemote === name) {
941
- throw new FgError(
942
- ErrorCodes.SETUP_RECURSION,
943
- `\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
944
- \u539F\u56E0\uFF1A\u521D\u59CB\u5316\u5C1A\u672A\u5B8C\u6210\uFF0C\u4E0D\u80FD\u7B49\u5F85\u81EA\u8EAB\u7684\u521D\u59CB\u5316\u7ED3\u679C\u3002
945
- \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`,
946
- { remote: name }
947
- );
948
- }
949
- try {
950
- hooks.beforeLoadRemote?.({ remote: name, module });
951
- } catch (hookErr) {
952
- 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);
953
- }
954
- const remote = remotes.get(name);
955
- if (!remote) {
956
- throw new FgError(
957
- ErrorCodes.REMOTE_UNKNOWN,
958
- `\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`,
959
- { remote: name }
960
- );
961
- }
962
- let container;
963
- try {
964
- const stylesReady = module && remote.manifestUrl ? preloadRemote2(spec) : Promise.resolve();
965
- [container] = await Promise.all([acquireContainer(remote, { retries: opts?.retries }), stylesReady]);
966
- } catch (err) {
967
- if (opts?.fallbackModule) {
968
- 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);
969
- emitError({ remote: name, error: err });
970
- return await opts.fallbackModule();
971
- }
972
- throw err;
973
- }
974
- if (!module) return container;
975
- await ensureLifecycle(remote, container);
976
- const cacheKey = `${name}@${remote.shareScope || "default"}#${module}`;
977
- if (!loadedModules.has(cacheKey)) {
978
- loadedModules.set(
979
- cacheKey,
980
- container.get(module).catch((err) => {
981
- loadedModules.delete(cacheKey);
982
- if (err instanceof FgError || err?.code) throw err;
983
- throw new FgError(
984
- ErrorCodes.REMOTE_LOAD_FAILED,
985
- `\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`,
986
- { remote: name, module }
987
- );
988
- })
989
- );
990
- }
991
- let ns;
992
- try {
993
- ns = await loadedModules.get(cacheKey);
994
- } catch (err) {
995
- if (opts?.fallbackModule) {
996
- 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);
997
- emitError({ remote: name, error: err });
998
- return await opts.fallbackModule();
999
- }
1000
- throw err;
1001
- }
1002
- try {
1003
- hooks.afterLoadRemote?.({ remote: name, module, module_ns: ns });
1004
- } catch (hookErr) {
1005
- 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);
1006
- }
1007
- if (ns && typeof ns === "object" && Object.keys(ns).length === 0) {
1008
- const err = new FgError(
1009
- ErrorCodes.EMPTY_EXPORTS,
1010
- `\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`,
1011
- { remote: name, module }
1012
- );
1013
- emitError({ remote: name, error: err });
1014
- console.error(err.message);
1015
- }
1016
- return ns;
1017
- }
1018
- function getContainer2(name) {
1019
- const remote = remotes.get(name);
1020
- if (!remote) {
1021
- 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 });
1022
- }
1023
- return acquireContainer(remote);
1024
- }
1025
- async function preloadRemote2(spec, opts = {}) {
1026
- const { remote: name, module } = parseSpec2(spec);
1027
- const remote = remotes.get(name);
1028
- if (!remote) {
1029
- 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 });
1030
- }
1031
- const mode = opts.mode ?? "preload";
1032
- const waitForStylesheet = (link, href) => {
1033
- if (link.sheet) return Promise.resolve();
1034
- const pending = stylesheetLoads.get(link);
1035
- if (pending) return pending;
1036
- const loaded = new Promise((resolve) => {
1037
- const finish = () => {
1038
- link.removeEventListener("load", finish);
1039
- link.removeEventListener("error", failed);
1040
- resolve();
1041
- };
1042
- const failed = () => {
1043
- emitError({
1044
- remote: name,
1045
- 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`, {
1046
- remote: name
1047
- })
1048
- });
1049
- finish();
1050
- };
1051
- link.addEventListener("load", finish, { once: true });
1052
- link.addEventListener("error", failed, { once: true });
1053
- if (link.sheet) finish();
1054
- });
1055
- stylesheetLoads.set(link, loaded);
1056
- return loaded;
1057
- };
1058
- const inject = (href, as) => {
1059
- if (typeof document === "undefined") return Promise.resolve();
1060
- const existing = document.querySelector(`link[href="${href}"]`);
1061
- if (existing) {
1062
- return as === "style" && mode === "preload" ? stylesheetLoads.get(existing) ?? Promise.resolve() : Promise.resolve();
1063
- }
1064
- const link = document.createElement("link");
1065
- link.rel = as === "style" ? "stylesheet" : "modulepreload";
1066
- link.href = href;
1067
- if (mode === "prefetch") {
1068
- ;
1069
- link.fetchPriority = "low";
1070
- }
1071
- const loaded = as === "style" ? waitForStylesheet(link, href) : Promise.resolve();
1072
- document.head.appendChild(link);
1073
- return as === "style" && mode === "preload" ? loaded : Promise.resolve();
1074
- };
1075
- const resolveAssetUrl = (asset) => {
1076
- const pageUrl = typeof document !== "undefined" ? document.baseURI : void 0;
1077
- const base = new URL(remote.entry, pageUrl || "http://fulgurjs.invalid/");
1078
- if (!base.pathname.endsWith("/") && !base.pathname.split("/").pop().includes(".")) {
1079
- base.pathname += "/";
1080
- }
1081
- return new URL(asset, base).href;
1082
- };
1083
- try {
1084
- if (remote.manifestUrl) {
1085
- let manifestPromise = remoteManifests.get(remote.manifestUrl);
1086
- if (!manifestPromise) {
1087
- manifestPromise = fetch(remote.manifestUrl, {
1088
- cache: "no-cache",
1089
- signal: AbortSignal.timeout(8e3)
1090
- }).then((res) => res.ok ? res.json() : void 0);
1091
- remoteManifests.set(remote.manifestUrl, manifestPromise);
1092
- manifestPromise.then(
1093
- (manifest2) => {
1094
- if (!manifest2) remoteManifests.delete(remote.manifestUrl);
1095
- },
1096
- () => remoteManifests.delete(remote.manifestUrl)
1097
- );
1098
- }
1099
- const manifest = await manifestPromise;
1100
- if (manifest) {
1101
- const sv = manifest.schemaVersion;
1102
- if (typeof sv === "number" && (!Number.isInteger(sv) || sv > 1)) {
1103
- emitError({
1104
- remote: name,
1105
- 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`, {
1106
- remote: name
1107
- })
1108
- });
1109
- await inject(remote.entry, "modulepreload");
1110
- return;
1111
- }
1112
- await inject(remote.entry, "modulepreload");
1113
- const exposes = manifest.exposes;
1114
- 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) : [];
1115
- const selected = module ? entries.filter(([key, item]) => normalizeModuleName(String(item?.name ?? key)) === module) : entries;
1116
- const cssLoads = [];
1117
- for (const [, item] of selected) {
1118
- if (typeof item?.file === "string") {
1119
- await inject(resolveAssetUrl(item.file), "modulepreload");
1120
- }
1121
- if (Array.isArray(item?.css)) {
1122
- for (const css of item.css) {
1123
- if (typeof css !== "string") continue;
1124
- const loading = inject(resolveAssetUrl(css), "style");
1125
- if (mode === "preload") cssLoads.push(loading);
1126
- }
1127
- }
1128
- }
1129
- await Promise.all(cssLoads);
1130
- return;
1131
- }
1132
- }
1133
- await inject(remote.entry, "modulepreload");
1134
- } catch (err) {
1135
- emitError({
1136
- remote: name,
1137
- 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`, {
1138
- remote: name
1139
- })
1140
- });
1141
- }
1142
- }
1143
- const attachDebug = () => {
1144
- if (typeof window === "undefined") return;
1145
- window.__FULGURJS_SCOPE__ = shareScopeMap2;
1146
- window.__FULGURJS_INFO__ = {
1147
- get remotes() {
1148
- const out = {};
1149
- for (const [k, v] of remotes) {
1150
- out[k] = { ...v.debug, entry: v.entry };
1151
- }
1152
- return out;
1153
- },
1154
- errors: []
1155
- };
1156
- };
1157
- attachDebug();
1158
- return {
1159
- shareScopeMap: shareScopeMap2,
1160
- initSharing: initSharing2,
1161
- registerShare: registerShare2,
1162
- registerRemotes: registerRemotes2,
1163
- registerRemote: registerRemote2,
1164
- registerPlugins: registerPlugins2,
1165
- loadShare: loadShare2,
1166
- loadShareSync: loadShareSync2,
1167
- prepareShares: prepareShares2,
1168
- getLoadedShare: getLoadedShare2,
1169
- pinLoadedShare: pinLoadedShare2,
1170
- loadRemote: loadRemote2,
1171
- getContainer: getContainer2,
1172
- preloadRemote: preloadRemote2,
1173
- parseSpec: parseSpec2,
1174
- clearSessionState: clearSessionState2
1175
- };
1176
- }
1177
- var g = globalThis;
1178
- var runtime = g.__FULGURJS_RUNTIME__ ?? createRuntime();
1179
- g.__FULGURJS_RUNTIME__ = runtime;
1180
- try {
1181
- Object.freeze(runtime);
1182
- } catch {
1183
- }
1184
- var initSharing = runtime.initSharing;
1185
- var registerShare = runtime.registerShare;
1186
- var registerRemotes = runtime.registerRemotes;
1187
- var registerRemote = runtime.registerRemote;
1188
- var registerPlugins = runtime.registerPlugins;
1189
- var loadShare = runtime.loadShare;
1190
- var getLoadedShare = runtime.getLoadedShare;
1191
- var prepareShares = runtime.prepareShares || (async (requests) => {
1192
- for (const { name, opts } of requests) await runtime.loadShare(name, opts);
1193
- });
1194
- var loadShareSync = runtime.loadShareSync;
1195
- var pinLoadedShare = runtime.pinLoadedShare;
1196
- var loadRemote = runtime.loadRemote;
1197
- var getContainer = runtime.getContainer;
1198
- var preloadRemote = runtime.preloadRemote;
1199
- var parseSpec = runtime.parseSpec;
1200
- var clearSessionState = runtime.clearSessionState;
1201
- var shareScopeMap = runtime.shareScopeMap;
1202
-
1203
- // src/context.ts
1204
- var ContextErrorCodes = {
1205
- /** context 必需字段缺失(三段式,修法指向宿主桥) */
1206
- CONTEXT_MISSING_KEY: "CC-001",
1207
- /** 运行时单例不可用(独立直开远程页,修法=经宿主联邦加载) */
1208
- CONTEXT_NO_RUNTIME: "CC-002"
1209
- };
1210
- var APP_CONTEXT_STORAGE_KEY = "__FULGURJS_APP_CONFIG__";
1211
- function requireRuntime() {
1212
- const rt = globalThis.__FULGURJS_RUNTIME__;
1213
- if (!rt || typeof rt.loadRemote !== "function") {
1214
- throw new FgError(
1215
- ContextErrorCodes.CONTEXT_NO_RUNTIME,
1216
- "\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",
1217
- { runtime: false }
1218
- );
1219
- }
1220
- return rt;
1221
- }
1222
- function provideAppContext(config) {
1223
- requireRuntime();
1224
- const g2 = globalThis;
1225
- g2[APP_CONTEXT_STORAGE_KEY] = { ...g2[APP_CONTEXT_STORAGE_KEY] ?? {}, ...config };
1226
- }
1227
- function clearAppContext() {
1228
- const g2 = globalThis;
1229
- delete g2[APP_CONTEXT_STORAGE_KEY];
1230
- try {
1231
- g2.__FULGURJS_RUNTIME__?.clearSessionState?.();
1232
- } catch {
1233
- }
1234
- }
1235
-
1236
- // src/bridge-errors.ts
1237
- var BridgeErrorCodes = {
1238
- /** 桥接契约非法(./bridge 默认导出缺 mount/unmount 或不是函数) */
1239
- BRIDGE_INVALID_CONTRACT: "MFU-015",
1240
- /** 桥接准备或生命周期失败(details.phase = getContext | mount | unmount) */
1241
- BRIDGE_LIFECYCLE_FAILED: "MFU-016",
1242
- /** 桥接会话参数与 AppContext 不一致(受控 sessionKey 与全局会话矛盾) */
1243
- BRIDGE_SESSION_MISMATCH: "MFU-017",
1244
- /** 路由同步配置/前缀冲突(basePath 非法、同页前缀冲突或重叠) */
1245
- ROUTING_CONFIG_INVALID: "MFU-030",
1246
- /** 路由协议缺失/通道失效(子应用未声明 routing 协议,或已销毁通道被再次使用) */
1247
- ROUTING_PROTOCOL_MISSING: "MFU-031",
1248
- /** 非法导航(目标越界前缀、非法 go 参数、失效实例的请求) */
1249
- ROUTING_NAVIGATION_INVALID: "MFU-032",
1250
- /** 路由准备/同步失败(重定向循环、子应用路由准备失败且已阻止错误页面显示) */
1251
- ROUTING_SYNC_FAILED: "MFU-033"
1252
- };
1253
- function invalidBridgeContractError(spec, mod) {
1254
- const actual = mod === null ? "null" : typeof mod;
1255
- const missing = [];
1256
- if (typeof mod?.mount !== "function") missing.push("mount");
1257
- if (typeof mod?.unmount !== "function") missing.push("unmount");
1258
- return new FgError(
1259
- BridgeErrorCodes.BRIDGE_INVALID_CONTRACT,
1260
- `\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
1261
- \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") + `
1262
- \u4FEE\u6CD5: \u8FDC\u7A0B\u4FA7\u7528 defineBridgeApp(...) \u521B\u5EFA\u5951\u7EA6\u5E76\u4F5C\u4E3A\u6A21\u5757\u9ED8\u8BA4\u5BFC\u51FA\u2014\u2014
1263
- Vue \u5B50\u5E94\u7528: import { defineBridgeApp } from '@fulgurjs/federation/runtime'\uFF1B
1264
- React \u5B50\u5E94\u7528: import { defineBridgeApp } from '@fulgurjs/federation/react'\u3002
1265
- \u5951\u7EA6\u5FC5\u987B\u5305\u542B mount(el, props?) \u4E0E unmount(el) \u4E24\u4E2A\u51FD\u6570\u3002`,
1266
- { spec, missing, actual }
1267
- );
1268
- }
1269
- function bridgeLifecycleError(phase, spec, cause, extra) {
1270
- const raw = cause instanceof Error ? cause.message : String(cause ?? "unknown");
1271
- const fixes = {
1272
- 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",
1273
- 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",
1274
- 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\uFF0C\u63D2\u4EF6\u5DF2\u6301\u4E45\u5C01\u9501\u8BE5\u5BB9\u5668\u2014\u2014\u540C\u9875\u300C\u91CD\u8BD5\u52A0\u8F7D\u300D\u4E0E\n \u6362\u4F1A\u8BDD\u90FD\u4E0D\u4F1A\u5728\u6B64\u5BB9\u5668\u91CD\u65B0\u6302\u8F7D\uFF1B\n \u2462 \u53EA\u80FD\u6574\u9875\u5237\u65B0\u6062\u590D\uFF08\u300C\u5237\u65B0\u9875\u9762\u91CD\u8BD5\u300D\uFF09\uFF1B\u6B8B\u7559\u8D44\u6E90\uFF08\u4E8B\u4EF6\u8BA2\u9605/\u5B9A\u65F6\u5668/\u5168\u5C40\u526F\u4F5C\u7528\uFF09\u8BF7\u5982\u5B9E\u6392\u67E5\u3002"
1275
- };
1276
- return new FgError(
1277
- BridgeErrorCodes.BRIDGE_LIFECYCLE_FAILED,
1278
- `\u73B0\u8C61\uFF1A\u6865\u63A5\u5E94\u7528 "${spec}" \u7684 ${phase} \u9636\u6BB5\u5931\u8D25\u3002
1279
- \u6839\u56E0: ${raw}
1280
- ` + fixes[phase],
1281
- { phase, spec, cause: String(raw), ...extra },
1282
- { cause }
1283
- );
1284
- }
1285
- function bridgeHostError(phase, spec, cause) {
1286
- if (cause instanceof FgError) return cause;
1287
- return bridgeLifecycleError(phase, spec, cause instanceof Error ? cause : String(cause));
1288
- }
1289
- function bridgeSessionMismatchError(spec, controlled, actual, extra) {
1290
- const actualText = actual === void 0 ? "undefined\uFF08\u9875\u9762\u5C1A\u65E0 AppContext\uFF09" : typeof actual === "string" ? `"${actual}"` : String(actual);
1291
- return new FgError(
1292
- BridgeErrorCodes.BRIDGE_SESSION_MISMATCH,
1293
- `\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
1294
- \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
1295
- getContext \u5FEB\u7167\u7684 sessionKey\uFF08\u6216\u73B0\u6709 AppContext \u7684 sessionKey\uFF09\u4E0E\u53D7\u63A7\u503C\u4E0D\u540C\u3002
1296
- \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
1297
- \u51C6\u5907\u65B0\u8D26\u53F7\u5B8C\u6574\u5FEB\u7167\u540E\u66F4\u65B0\u53D7\u63A7 prop\uFF1B
1298
- \u2461 \u65E0 getContext \u65F6\uFF0C\u5148\u7531\u5BBF\u4E3B\u6865 provideAppContext({...\u65B0\u4F1A\u8BDD, sessionKey}) \u518D\u6302\u8F7D\uFF1B
1299
- \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`,
1300
- { spec, controlled, actual: String(actual), ...extra }
1301
- );
1302
- }
1303
- function invalidSessionKeyError(spec, value) {
1304
- return new FgError(
1305
- BridgeErrorCodes.BRIDGE_SESSION_MISMATCH,
1306
- `\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
1307
- \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
1308
- \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`,
1309
- { spec, value: String(value) }
1310
- );
1311
- }
1312
- function routingConfigError(spec, reason) {
1313
- return new FgError(
1314
- BridgeErrorCodes.ROUTING_CONFIG_INVALID,
1315
- `\u73B0\u8C61\uFF1A\u6865\u63A5\u5E94\u7528 "${spec}" \u7684 URL \u540C\u6B65\u914D\u7F6E\u65E0\u6CD5\u542F\u7528\u3002
1316
- \u539F\u56E0\uFF1A${reason}
1317
- \u4FEE\u6CD5\uFF1A\u4FEE\u6B63 routing.basePath \u4E3A\u5BBF\u4E3B\u8DEF\u7531\u89C6\u89D2\u7684\u9759\u6001\u7EDD\u5BF9\u8DEF\u5F84\uFF08\u5982 "/approval"\uFF09\uFF0C\u5E76\u4FDD\u8BC1\u540C\u9875\u5404\u540C\u6B65\u5B9E\u4F8B\u524D\u7F00\u4E92\u4E0D\u91CD\u53E0\uFF1B\u5BBF\u4E3B\u8DEF\u7531\u9700\u58F0\u660E\u5BF9\u5E94\u7684\u540E\u7F00\u5339\u914D\uFF08Vue "/approval/:pathMatch(.*)*"\u3001React "/approval/*"\uFF09\u3002`,
1318
- { spec }
1319
- );
1320
- }
1321
- function routingProtocolError(spec, reason) {
1322
- return new FgError(
1323
- BridgeErrorCodes.ROUTING_PROTOCOL_MISSING,
1324
- `\u73B0\u8C61\uFF1A\u6865\u63A5\u5E94\u7528 "${spec}" \u7684 URL \u540C\u6B65\u901A\u9053\u4E0D\u53EF\u7528\u3002
1325
- \u539F\u56E0\uFF1A${reason}
1326
- \u4FEE\u6CD5\uFF1A\u5B50\u5E94\u7528\u4EE5 defineBridgeApp(\u5DE5\u5382, { routing: true }) \u58F0\u660E\u8DEF\u7531\u534F\u8BAE\u5E76\u5728\u5DE5\u5382\u7B2C\u4E8C\u53C2\u6570\u63A5\u6536 { signal, routing } \u63A5\u7EBF\u53D7\u63A7\u8DEF\u7531\uFF1B\u901A\u9053\u968F\u6302\u8F7D\u751F\u547D\u5468\u671F\u521B\u5EFA\uFF0C\u9500\u6BC1\u540E\u4E0D\u5F97\u590D\u7528\uFF08\u91CD\u6302\u4F1A\u5F97\u5230\u65B0\u901A\u9053\uFF09\u3002`,
1327
- { spec }
1328
- );
1329
- }
1330
- function routingNavigationError(spec, reason) {
1331
- return new FgError(
1332
- BridgeErrorCodes.ROUTING_NAVIGATION_INVALID,
1333
- `\u73B0\u8C61\uFF1A\u6865\u63A5\u5E94\u7528 "${spec}" \u7684\u4E00\u6B21\u5BFC\u822A\u8BF7\u6C42\u88AB\u62D2\u7EDD\u3002
1334
- \u539F\u56E0\uFF1A${reason}
1335
- \u4FEE\u6CD5\uFF1A\u5B50\u5E94\u7528\u5BFC\u822A\u76EE\u6807\u5FC5\u987B\u4F4D\u4E8E\u81EA\u8EAB basePath \u524D\u7F00\u5185\uFF08\u8DE8\u524D\u7F00\u8BF7\u7528\u5BBF\u4E3B\u83DC\u5355/\u5BBF\u4E3B\u80FD\u529B\uFF09\uFF1Bgo \u53C2\u6570\u4E3A\u6709\u9650\u6574\u6570\u4E14 |delta| \u2264 50\u3002`,
1336
- { spec }
1337
- );
1338
- }
1339
- function routingSyncError(spec, reason, chain, cause) {
1340
- return new FgError(
1341
- BridgeErrorCodes.ROUTING_SYNC_FAILED,
1342
- `\u73B0\u8C61\uFF1A\u6865\u63A5\u5E94\u7528 "${spec}" \u7684 URL \u540C\u6B65\u5931\u8D25\u3002
1343
- \u539F\u56E0\uFF1A${reason}
1344
- \u4FEE\u6CD5\uFF1A\u68C0\u67E5\u8DEF\u7531\u5B88\u536B\u3001\u52A0\u8F7D\u5668\u4E0E\u5BFC\u822A\u5F02\u5E38\uFF1B\u91CD\u5B9A\u5411\u94FE\u4E0D\u5E94\u6210\u73AF\u3002\u76EE\u6807\u94FE\uFF1A${chain.join(" \u2192 ")}\u3002`,
1345
- { spec, chain: [...chain] },
1346
- { cause }
1347
- );
1348
- }
1349
-
1350
- // src/bridge-core.ts
1351
- function assertBridgeContract(spec, mod) {
1352
- const contract = mod?.default ?? mod;
1353
- if (contract === null || typeof contract !== "object" || typeof contract.mount !== "function" || typeof contract.unmount !== "function") {
1354
- throw invalidBridgeContractError(spec, mod);
1355
- }
1356
- return contract;
1357
- }
1358
- var activeBridgeSessions = /* @__PURE__ */ new Map();
1359
- function acquireBridgeSession(spec, sessionKey) {
1360
- for (const [active] of activeBridgeSessions) {
1361
- if (active !== sessionKey && (activeBridgeSessions.get(active) ?? 0) > 0) {
1362
- throw bridgeSessionMismatchError(spec, sessionKey, active, { reason: "active-instance" });
1363
- }
1364
- }
1365
- activeBridgeSessions.set(sessionKey, (activeBridgeSessions.get(sessionKey) ?? 0) + 1);
1366
- }
1367
- function releaseBridgeSession(sessionKey) {
1368
- const n = activeBridgeSessions.get(sessionKey) ?? 0;
1369
- if (n <= 1) activeBridgeSessions.delete(sessionKey);
1370
- else activeBridgeSessions.set(sessionKey, n - 1);
1371
- }
1372
- function resolveBridgeContext(spec, controlled, getContext) {
1373
- if (getContext) {
1374
- let snapshot;
1375
- try {
1376
- snapshot = getContext();
1377
- } catch (e) {
1378
- throw bridgeLifecycleError("getContext", spec, e);
1379
- }
1380
- if (snapshot === null || typeof snapshot !== "object" || typeof snapshot.then === "function") {
1381
- throw bridgeLifecycleError("getContext", spec, describeContextValue(snapshot));
1382
- }
1383
- if (controlled !== void 0 && snapshot.sessionKey !== controlled) {
1384
- throw bridgeSessionMismatchError(spec, controlled, snapshot.sessionKey, { reason: "getter-snapshot" });
1385
- }
1386
- const current = currentSessionKey();
1387
- if (snapshot.sessionKey !== void 0 && snapshot.sessionKey !== current) {
1388
- clearAppContext();
1389
- }
1390
- return { provide: snapshot };
1391
- }
1392
- if (controlled !== void 0) {
1393
- const current = currentSessionKey();
1394
- if (current !== controlled) {
1395
- throw bridgeSessionMismatchError(spec, controlled, current, { reason: "app-context" });
1396
- }
1397
- }
1398
- return {};
1399
- }
1400
- function currentSessionKey() {
1401
- const sk = (globalThis.__FULGURJS_APP_CONFIG__ ?? {}).sessionKey;
1402
- return typeof sk === "string" && sk !== "" ? sk : void 0;
1403
- }
1404
- function describeContextValue(snapshot) {
1405
- if (snapshot === null) {
1406
- return "getContext \u8FD4\u56DE\u4E86 null\u3002\u5B83\u5FC5\u987B\u662F\u540C\u6B65\u8FD4\u56DE\u5FEB\u7167\u5BF9\u8C61\u7684\u7EAF getter\uFF1A\u8FD4\u56DE\u503C\u5E94\u4E3A\u5305\u542B\u672C\u6B21\u4F1A\u8BDD\u5FEB\u7167\u7684\u5BF9\u8C61\uFF08\u53D7\u63A7 sessionKey \u65F6\u987B\u542B\u76F8\u540C sessionKey\uFF09\uFF0C\u4E0D\u80FD\u8FD4\u56DE null\u3002";
1407
- }
1408
- if (snapshot === void 0) {
1409
- return "getContext \u8FD4\u56DE\u4E86 undefined\u3002\u5B83\u5FC5\u987B\u662F\u540C\u6B65\u8FD4\u56DE\u5FEB\u7167\u5BF9\u8C61\u7684\u7EAF getter\uFF1A\u8FD4\u56DE\u503C\u5E94\u4E3A\u5305\u542B\u672C\u6B21\u4F1A\u8BDD\u5FEB\u7167\u7684\u5BF9\u8C61\uFF0C\u8BF7\u68C0\u67E5 getter \u662F\u5426\u6F0F\u5199 return \u6216\u8FD4\u56DE\u4E86\u672A\u521D\u59CB\u5316\u7684\u53D8\u91CF\u3002";
1410
- }
1411
- if (typeof snapshot.then === "function") {
1412
- return "getContext \u8FD4\u56DE\u4E86 Promise/thenable\u3002\u5B83\u5FC5\u987B\u662F\u540C\u6B65 getter\uFF0C\u4E0D\u5F97\u8FD4\u56DE Promise\uFF1A\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";
1413
- }
1414
- return `getContext \u8FD4\u56DE\u4E86\u975E\u5BF9\u8C61\u503C\uFF08${typeof snapshot}\uFF1A"${String(snapshot)}"\uFF09\u3002\u5B83\u5FC5\u987B\u662F\u540C\u6B65\u8FD4\u56DE\u5FEB\u7167\u5BF9\u8C61\u7684\u7EAF getter\uFF0C\u8FD4\u56DE\u503C\u5E94\u4E3A\u5BF9\u8C61\u800C\u4E0D\u662F\u539F\u59CB\u503C\u3002`;
1415
- }
1416
- function assertControlledSessionKey(spec, value) {
1417
- if (value === void 0 || value === null) return;
1418
- if (typeof value === "string" && value !== "") return;
1419
- throw invalidSessionKeyError(spec, value);
1420
- }
1421
- function assertBridgeOptions(source, opts) {
1422
- if (opts.retries !== void 0 && (typeof opts.retries !== "number" || !Number.isInteger(opts.retries) || opts.retries < 0 || opts.retries > 10)) {
1423
- throw new Error(
1424
- `[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`
1425
- );
1426
- }
1427
- if (opts.timeout !== void 0 && (typeof opts.timeout !== "number" || !Number.isFinite(opts.timeout) || opts.timeout <= 0)) {
1428
- throw new Error(
1429
- `[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`
1430
- );
1431
- }
1432
- }
1433
- function withBridgeTimeout(p, ms, spec) {
1434
- return new Promise((resolve, reject) => {
1435
- const timer = setTimeout(() => {
1436
- reject(new Error(
1437
- `[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`
1438
- ));
1439
- }, ms);
1440
- p.then(
1441
- (v) => {
1442
- clearTimeout(timer);
1443
- resolve(v);
1444
- },
1445
- (e) => {
1446
- clearTimeout(timer);
1447
- reject(e);
1448
- }
1449
- );
1450
- });
1451
- }
1452
-
1453
- // src/bridge-router-core.ts
1454
- var MAX_REDIRECT_PER_REQUEST = 5;
1455
- var MAX_GO_DELTA = 50;
1456
- function normalizeBasePath(basePath, spec) {
1457
- const bad = (reason) => routingConfigError(spec, reason);
1458
- if (typeof basePath !== "string" || basePath === "") throw bad("basePath \u4E0D\u80FD\u4E3A\u7A7A\u3002");
1459
- if (/^\/+$/u.test(basePath)) throw bad('basePath \u4E0D\u80FD\u662F\u6839 "/"\u2014\u2014\u6839\u8DEF\u5F84\u5C5E\u4E8E\u5BBF\u4E3B\u81EA\u8EAB\u9875\u9762\uFF0C\u8BF7\u4E3A\u540C\u6B65\u5B9E\u4F8B\u5206\u914D\u72EC\u7ACB\u524D\u7F00\u3002');
1460
- if (!basePath.startsWith("/")) throw bad(`basePath \u5FC5\u987B\u4EE5 "/" \u5F00\u5934\uFF1A\u5F53\u524D\u4E3A "${basePath}"\u3002`);
1461
- if (/[?#]/.test(basePath) || basePath.includes("://")) throw bad(`basePath \u4E0D\u80FD\u5305\u542B query/hash/\u534F\u8BAE\uFF1A\u5F53\u524D\u4E3A "${basePath}"\u3002`);
1462
- if (basePath.includes("*") || basePath.includes(":")) throw bad(`basePath \u4E0D\u63A5\u53D7\u901A\u914D\u7B26\u6216\u53C2\u6570\u6A21\u677F\uFF1A\u5F53\u524D\u4E3A "${basePath}"\u3002\u8BF7\u4F7F\u7528\u9759\u6001\u7EDD\u5BF9\u8DEF\u5F84\u3002`);
1463
- if (/\\|\/\/|%(?:2e|2f|5c)/i.test(basePath) || basePath.split("/").some((part) => part === "." || part === "..")) throw bad("basePath \u4E0D\u80FD\u5305\u542B\u7A7A\u8DEF\u5F84\u6BB5\u3001\u8F6C\u4E49\u5206\u9694\u7B26\u6216\u76F8\u5BF9\u8DEF\u5F84\u6BB5\u3002");
1464
- return basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
1465
- }
1466
- function matchesBasePath(hostPathname, basePath) {
1467
- return hostPathname === basePath || hostPathname.startsWith(basePath + "/");
1468
- }
1469
- function overlapsBasePath(a, b) {
1470
- return a === b || a.startsWith(b + "/") || b.startsWith(a + "/");
1471
- }
1472
- function toChildLocation(host, basePath) {
1473
- const rest = host.pathname === basePath ? "/" : host.pathname.slice(basePath.length);
1474
- return { pathname: rest || "/", search: host.search, hash: host.hash };
1475
- }
1476
- function toHostLocation(child, basePath, spec) {
1477
- const childPath = child.pathname === "/" ? "" : child.pathname;
1478
- if (childPath !== "" && !childPath.startsWith("/")) {
1479
- throw routingNavigationError(spec, `\u5BFC\u822A\u76EE\u6807 pathname \u5FC5\u987B\u4EE5 "/" \u5F00\u5934\uFF1A\u5F53\u524D\u4E3A "${child.pathname}"\u3002`);
1480
- }
1481
- return { pathname: basePath + childPath, search: child.search, hash: child.hash };
1482
- }
1483
- function sameLocation(a, b) {
1484
- return a.pathname === b.pathname && a.search === b.search && a.hash === b.hash;
1485
- }
1486
- function assertChildTargetInScope(target, basePath, spec) {
1487
- if (!target || typeof target.pathname !== "string" || typeof target.search !== "string" || typeof target.hash !== "string" || target.search !== "" && !target.search.startsWith("?") || target.hash !== "" && !target.hash.startsWith("#")) throw routingNavigationError(spec, "\u5BFC\u822A\u76EE\u6807\u5FC5\u987B\u63D0\u4F9B pathname\u3001search\u3001hash \u4E09\u6BB5\uFF0Csearch/hash \u5E94\u4E3A\u7A7A\u6216\u4EE5 ?/# \u5F00\u5934\u3002");
1488
- if (/[\x00-\x20\\?#]|%(?:2e|2f|5c)/i.test(target.pathname) || target.pathname.includes("//") || target.pathname.split("/").some((part) => part === "." || part === "..")) {
1489
- throw routingNavigationError(spec, `\u5BFC\u822A\u76EE\u6807\u8BD5\u56FE\u9003\u9038\u81EA\u8EAB\u524D\u7F00\uFF1Apathname "${target.pathname}"\u3002\u8DE8\u524D\u7F00\u5BFC\u822A\u8BF7\u901A\u8FC7\u5BBF\u4E3B\u83DC\u5355\u7B49\u5BBF\u4E3B\u80FD\u529B\u5B8C\u6210\u3002`);
1490
- }
1491
- const host = toHostLocation(target, basePath, spec);
1492
- if (!matchesBasePath(host.pathname, basePath)) {
1493
- throw routingNavigationError(spec, `\u5BFC\u822A\u76EE\u6807 ${host.pathname} \u4E0D\u5728\u524D\u7F00 ${basePath} \u4E0B\u3002`);
1494
- }
1495
- }
1496
- var activeRoutingPrefixes = /* @__PURE__ */ new Map();
1497
- function acquireRoutingPrefix(basePath, spec) {
1498
- for (const [active] of activeRoutingPrefixes) {
1499
- if ((activeRoutingPrefixes.get(active) ?? 0) > 0 && overlapsBasePath(active, basePath)) {
1500
- throw routingConfigError(spec, `\u540C\u9875\u5DF2\u6709\u5360\u7528\u524D\u7F00 "${active}" \u7684\u540C\u6B65\u5B9E\u4F8B\uFF0C\u65B0\u5B9E\u4F8B "${spec}" \u7684 basePath "${basePath}" \u4E0E\u5176\u91CD\u53E0\u3002`);
1501
- }
1502
- }
1503
- activeRoutingPrefixes.set(basePath, (activeRoutingPrefixes.get(basePath) ?? 0) + 1);
1504
- let released = false;
1505
- return () => {
1506
- if (released) return;
1507
- released = true;
1508
- activeRoutingPrefixes.set(basePath, Math.max(0, (activeRoutingPrefixes.get(basePath) ?? 1) - 1));
1509
- };
1510
- }
1511
- function assertBridgeRoutingProtocol(spec, contract) {
1512
- const r = contract?.routing;
1513
- if (!r || r.protocol !== 1) {
1514
- throw routingProtocolError(
1515
- spec,
1516
- `\u8FDC\u7A0B\u5951\u7EA6\u672A\u58F0\u660E\u8DEF\u7531\u534F\u8BAE\uFF08\u671F\u671B routing: { protocol: 1 }\uFF0C\u5B9E\u9645 ${r === void 0 ? "\u7F3A\u5931" : `protocol=${String(r?.protocol)}`}\uFF09\u3002\u5F00\u542F URL \u540C\u6B65\u4E0D\u80FD\u9759\u9ED8\u9000\u56DE memory \u6A21\u5F0F\u3002`
1517
- );
1518
- }
1519
- }
1520
- var RoutingChannel = class {
1521
- id;
1522
- basePath;
1523
- /** 远程 spec(诊断错误定位真实远程用;随通道暴露给子应用路由接线) */
1524
- spec;
1525
- host;
1526
- listeners = /* @__PURE__ */ new Set();
1527
- current;
1528
- lastConfirmed;
1529
- seq = 0;
1530
- inflight = null;
1531
- queue;
1532
- generation = 0;
1533
- disposed = false;
1534
- active = true;
1535
- releasePrefix;
1536
- unsubHost;
1537
- /** 连续内部 replace 计数(有界重定向检测) */
1538
- redirectChain = [];
1539
- redirectTimer;
1540
- /** 诊断观测钩子(测试断言/诊断用) */
1541
- onEvent;
1542
- constructor(id, basePath, host, spec) {
1543
- this.id = id;
1544
- this.spec = spec;
1545
- this.basePath = normalizeBasePath(basePath, spec);
1546
- this.host = host;
1547
- const initial = host.getLocation();
1548
- this.current = matchesBasePath(initial.pathname, this.basePath) ? toChildLocation(initial, this.basePath) : { pathname: "/", search: "", hash: "" };
1549
- this.lastConfirmed = { ...this.current };
1550
- this.releasePrefix = acquireRoutingPrefix(this.basePath, spec);
1551
- this.unsubHost = host.subscribe((loc) => this.handleHostLocation(loc));
1552
- }
1553
- emit(loc) {
1554
- this.current = { ...loc };
1555
- for (const l of [...this.listeners]) {
1556
- try {
1557
- l(this.current);
1558
- } catch (e) {
1559
- console.error("[fulgurjs] \u5B50\u5E94\u7528\u8DEF\u7531\u8BA2\u9605\u8005\u5F02\u5E38\uFF08\u5DF2\u9694\u79BB\uFF0C\u4E0D\u5F71\u54CD\u5BBF\u4E3B\uFF09\uFF1A", e);
1560
- }
1561
- }
1562
- }
1563
- handleHostLocation(hostLoc) {
1564
- if (this.disposed) return;
1565
- if (!matchesBasePath(hostLoc.pathname, this.basePath)) {
1566
- this.cancelPending();
1567
- this.onEvent?.({ type: "prefix-miss", pathname: hostLoc.pathname });
1568
- return;
1569
- }
1570
- if (!this.active) {
1571
- this.onEvent?.({ type: "paused-host-loc" });
1572
- return;
1573
- }
1574
- const child = toChildLocation(hostLoc, this.basePath);
1575
- if (this.inflight && sameLocation(child, this.inflight.target)) {
1576
- const { seq } = this.inflight;
1577
- this.lastConfirmed = child;
1578
- this.emit(child);
1579
- this.onEvent?.({ type: "request-confirmed", seq });
1580
- return;
1581
- }
1582
- if (this.inflight) {
1583
- const seq = this.inflight.seq;
1584
- this.lastConfirmed = child;
1585
- this.cancelPending();
1586
- this.emit(child);
1587
- this.onEvent?.({ type: "request-superseded", seq });
1588
- return;
1589
- }
1590
- if (!sameLocation(child, this.lastConfirmed)) this.generation++;
1591
- this.lastConfirmed = child;
1592
- this.emit(child);
1593
- this.onEvent?.({ type: "host-broadcast" });
1594
- }
1595
- getLocation() {
1596
- return { ...this.current };
1597
- }
1598
- subscribe(listener) {
1599
- if (this.disposed) {
1600
- throw routingProtocolError(this.spec, `\u901A\u9053 ${this.id} \u5DF2\u9500\u6BC1\uFF0C\u4E0D\u80FD\u518D\u8BA2\u9605\uFF08\u65E7\u5B9E\u4F8B\u7684\u8FDF\u5230\u8BA2\u9605\u5FC5\u987B\u5931\u6548\uFF1B\u91CD\u6302\u4F1A\u5F97\u5230\u65B0\u901A\u9053\uFF09\u3002`);
1601
- }
1602
- this.listeners.add(listener);
1603
- return () => {
1604
- this.listeners.delete(listener);
1605
- };
1606
- }
1607
- cancelPending() {
1608
- this.generation++;
1609
- const pending = this.inflight;
1610
- this.inflight = null;
1611
- if (pending) {
1612
- pending.abort.abort();
1613
- pending.resolve({ status: "cancelled", location: { ...this.lastConfirmed } });
1614
- }
1615
- }
1616
- navigate(target, action) {
1617
- const generation = this.generation;
1618
- const snapshot = { ...target };
1619
- const run = () => generation !== this.generation ? Promise.resolve({ status: "cancelled", location: { ...this.lastConfirmed } }) : this.performNavigation(snapshot, action);
1620
- const task = this.queue ? this.queue.then(run) : run();
1621
- const tail = task.then(() => {
1622
- }, () => {
1623
- });
1624
- this.queue = tail;
1625
- void tail.then(() => {
1626
- if (this.queue === tail) this.queue = void 0;
1627
- });
1628
- return task;
1629
- }
1630
- async performNavigation(target, action) {
1631
- if (this.disposed || !this.active || !matchesBasePath(this.host.getLocation().pathname, this.basePath)) {
1632
- this.onEvent?.({ type: this.disposed ? "rejected-disposed" : "rejected-inactive" });
1633
- return { status: "cancelled", location: { ...this.lastConfirmed } };
1634
- }
1635
- assertChildTargetInScope(target, this.basePath, this.spec);
1636
- if (sameLocation(target, this.lastConfirmed)) {
1637
- this.onEvent?.({ type: "noop" });
1638
- return { status: "committed", location: { ...this.lastConfirmed } };
1639
- }
1640
- if (action === "replace") {
1641
- this.redirectChain.push(target.pathname + target.search + target.hash);
1642
- } else {
1643
- this.redirectChain = [];
1644
- }
1645
- if (this.redirectTimer) clearTimeout(this.redirectTimer);
1646
- this.redirectTimer = setTimeout(() => {
1647
- this.redirectChain = [];
1648
- }, 1e3);
1649
- if (this.redirectChain.length > MAX_REDIRECT_PER_REQUEST) {
1650
- const chain = [...this.redirectChain];
1651
- this.redirectChain = [];
1652
- throw routingSyncError(this.spec, "\u540C\u4E00\u901A\u9053\u7684\u8FDE\u7EED replace \u91CD\u5B9A\u5411\u8D85\u8FC7\u4E0A\u9650\uFF08\u7591\u4F3C\u5B50\u5E94\u7528\u8DEF\u7531\u4E92\u76F8\u6210\u73AF\uFF09\u3002", chain);
1653
- }
1654
- const seq = ++this.seq;
1655
- this.onEvent?.({ type: "request-start", seq, target: { ...target }, action });
1656
- return new Promise((resolve, reject) => {
1657
- const abort = new AbortController();
1658
- this.inflight = { seq, target: { ...target }, resolve, abort };
1659
- Promise.resolve().then(() => abort.signal.aborted ? { status: "cancelled", location: this.host.getLocation() } : this.host.navigate(toHostLocation(target, this.basePath, this.spec), action, { signal: abort.signal })).then((result) => {
1660
- if (!this.inflight || this.inflight.seq !== seq) return;
1661
- if (this.disposed || !this.active) {
1662
- this.inflight = null;
1663
- resolve({ status: "cancelled", location: { ...this.lastConfirmed } });
1664
- return;
1665
- }
1666
- this.inflight = null;
1667
- if (result.status === "committed" && matchesBasePath(result.location.pathname, this.basePath)) {
1668
- const child = toChildLocation(result.location, this.basePath);
1669
- this.lastConfirmed = child;
1670
- this.emit(child);
1671
- } else {
1672
- this.emit({ ...this.lastConfirmed });
1673
- }
1674
- this.onEvent?.({ type: "host-verdict", seq, status: result.status });
1675
- resolve(
1676
- result.status === "committed" && matchesBasePath(result.location.pathname, this.basePath) ? { status: "committed", location: { ...this.lastConfirmed } } : { status: "cancelled", location: { ...this.lastConfirmed } }
1677
- );
1678
- }).catch((cause) => {
1679
- if (this.inflight?.seq === seq) {
1680
- this.inflight = null;
1681
- this.onEvent?.({ type: "host-error", seq });
1682
- this.emit({ ...this.lastConfirmed });
1683
- reject(routingSyncError(this.spec, `\u5BBF\u4E3B\u5BFC\u822A\u6267\u884C\u5931\u8D25\uFF1A${cause instanceof Error ? cause.message : String(cause)}`, [], cause));
1684
- }
1685
- });
1686
- });
1687
- }
1688
- /** 委托宿主历史(有限整数);不创建第二条独立历史 */
1689
- go(delta) {
1690
- if (this.disposed || !this.active || !matchesBasePath(this.host.getLocation().pathname, this.basePath)) return;
1691
- if (typeof delta !== "number" || !Number.isInteger(delta) || Math.abs(delta) > MAX_GO_DELTA) {
1692
- throw routingNavigationError(this.spec, `go \u53C2\u6570\u5FC5\u987B\u662F\u6709\u9650\u6574\u6570\uFF08|delta| \u2264 ${MAX_GO_DELTA}\uFF09\uFF1A\u5F53\u524D\u4E3A ${String(delta)}\u3002`);
1693
- }
1694
- this.host.go(delta);
1695
- }
1696
- /** KeepAlive deactivate:暂停广播与写入;通道与订阅保留(activate 后 resync) */
1697
- setActive(active) {
1698
- if (this.disposed) return;
1699
- if (!active) this.cancelPending();
1700
- this.active = active;
1701
- this.onEvent?.({ type: active ? "resumed" : "paused" });
1702
- if (active) this.handleHostLocation(this.host.getLocation());
1703
- }
1704
- /** 挂载生命周期结束:清理订阅/登记/在飞请求;此后一切写入失效 */
1705
- dispose() {
1706
- if (this.disposed) return;
1707
- this.disposed = true;
1708
- if (this.redirectTimer) clearTimeout(this.redirectTimer);
1709
- this.unsubHost();
1710
- this.releasePrefix();
1711
- this.cancelPending();
1712
- this.listeners.clear();
1713
- this.onEvent?.({ type: "disposed" });
1714
- }
1715
- };
1716
-
1717
- export {
1718
- loadRemote,
1719
- provideAppContext,
1720
- bridgeHostError,
1721
- assertBridgeContract,
1722
- acquireBridgeSession,
1723
- releaseBridgeSession,
1724
- resolveBridgeContext,
1725
- assertControlledSessionKey,
1726
- assertBridgeOptions,
1727
- withBridgeTimeout,
1728
- assertBridgeRoutingProtocol,
1729
- RoutingChannel
1730
- };