@fulgurjs/federation 6.12.7 → 6.12.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.en.md +3 -1
- package/README.md +3 -1
- package/dist/bridge-app-react.js +4 -4
- package/dist/bridge-app-vue.js +5 -5
- package/dist/bridge-core.js +4 -4
- package/dist/bridge-errors.js +2 -2
- package/dist/bridge-host-react.js +5 -5
- package/dist/bridge-host-vue.js +5 -5
- package/dist/bridge-session-auto.js +2 -2
- package/dist/chunk-4YTQSOGQ.js +50 -0
- package/dist/{chunk-GVWC7J7Q.js → chunk-5ODWDP5I.js} +1 -1
- package/dist/{chunk-VF64X44Z.js → chunk-MUATDGMR.js} +14 -3
- package/dist/{chunk-TSYLDY6H.js → chunk-PPHNZBZX.js} +1 -1
- package/dist/{chunk-6FMTBDIM.js → chunk-UTL2F4KF.js} +1 -1
- package/dist/{chunk-2MWIIRQZ.js → chunk-YXGV4ZK6.js} +2 -2
- package/dist/cli.js +13 -2
- package/dist/context.js +2 -2
- package/dist/index.cjs +40 -2
- package/dist/index.js +40 -2
- package/dist/react-adapter.cjs +47 -3
- package/dist/react-adapter.js +2 -1
- package/dist/runtime.js +15 -15
- package/dist/vue-adapter.cjs +83 -7
- package/dist/vue-adapter.js +41 -6
- package/docs/en/reference/api.md +3 -3
- package/docs/en/reference/errors.md +3 -0
- package/docs/en/reference/types.md +1 -1
- package/docs/zh/reference/api.md +4 -4
- package/docs/zh/reference/errors.md +3 -0
- package/docs/zh/reference/types.md +1 -1
- package/examples/templates/README.md +3 -3
- package/examples/templates/react-host-vue-remote/README.md +1 -1
- package/examples/templates/react-host-vue-remote/vue-remote/README.md +2 -2
- package/examples/templates/react-react/README.md +2 -2
- package/examples/templates/showcase/README.md +1 -1
- package/examples/templates/vue-host-react-remote/README.md +1 -1
- package/examples/templates/vue-host-react-remote/react-remote/README.md +2 -2
- package/examples/templates/vue-host-react-remote/vue-host/README.md +1 -1
- package/examples/templates/vue-vue/README.md +2 -2
- package/package.json +2 -2
- package/dist/chunk-OF2JCXTZ.js +0 -15
package/dist/vue-adapter.cjs
CHANGED
|
@@ -26,6 +26,42 @@ __export(vue_adapter_exports, {
|
|
|
26
26
|
module.exports = __toCommonJS(vue_adapter_exports);
|
|
27
27
|
var import_vue = require("vue");
|
|
28
28
|
|
|
29
|
+
// src/runtime/errors.ts
|
|
30
|
+
var ErrorCodes = {
|
|
31
|
+
/** 远程容器加载失败(网络/超时/重试耗尽/熔断) */
|
|
32
|
+
REMOTE_LOAD_FAILED: "MFU-001",
|
|
33
|
+
/** remoteEntry 自报名与 remotes 配置的名称不匹配 */
|
|
34
|
+
REMOTE_NAME_MISMATCH: "MFU-002",
|
|
35
|
+
/** strictVersion 开启且共享版本不满足 */
|
|
36
|
+
SHARE_STRICT_VERSION: "MFU-003",
|
|
37
|
+
/** 共享模块缺失且无本地 fallback(import: false) */
|
|
38
|
+
SHARE_NOT_AVAILABLE: "MFU-004",
|
|
39
|
+
/** 同一容器用不同 share scope 重复 init */
|
|
40
|
+
CONTAINER_REINIT_CONFLICT: "MFU-005",
|
|
41
|
+
/** 请求的模块未被该远程 exposes */
|
|
42
|
+
MODULE_NOT_EXPOSED: "MFU-006",
|
|
43
|
+
/** 预加载失败 */
|
|
44
|
+
PRELOAD_FAILED: "MFU-007",
|
|
45
|
+
/** 未知远程 */
|
|
46
|
+
REMOTE_UNKNOWN: "MFU-008",
|
|
47
|
+
/** 加载到的模块没有任何导出(常见:exposes 指向了不导出内容的文件) */
|
|
48
|
+
EMPTY_EXPORTS: "MFU-009",
|
|
49
|
+
/** singleton 共享协商版本与消费方 requiredVersion 不一致(使用作用域版本,仅告警) */
|
|
50
|
+
SINGLETON_SKEW: "MFU-010",
|
|
51
|
+
/** setup 生命周期入口导出形态非法(默认导出不是函数,或具名 onSession 不是函数) */
|
|
52
|
+
SETUP_INVALID_EXPORT: "MFU-011",
|
|
53
|
+
/** setup/onSession 执行抛错(该次 loadRemote 拒绝;仅清失败阶段缓存,允许重试) */
|
|
54
|
+
SETUP_RUN_FAILED: "MFU-012",
|
|
55
|
+
/** 远程声明了 onSession 但宿主 AppContext 未提供 sessionKey(登录代次) */
|
|
56
|
+
SETUP_SESSION_KEY_MISSING: "MFU-013",
|
|
57
|
+
/** setup/onSession 执行的同步段内递归 loadRemote 同一远程 */
|
|
58
|
+
SETUP_RECURSION: "MFU-014",
|
|
59
|
+
/** 远程模块在组件消费边界导出的不是可渲染组件(适配器实测导出形态后报出) */
|
|
60
|
+
COMPONENT_EXPORT_INVALID: "MFU-018",
|
|
61
|
+
/** Vue 组件直渲染的渲染器与共享作用域实际收敛的 Vue 实例不一致(身份证据已证实) */
|
|
62
|
+
VUE_RUNTIME_IDENTITY_MISMATCH: "MFU-019"
|
|
63
|
+
};
|
|
64
|
+
|
|
29
65
|
// src/pages.ts
|
|
30
66
|
var normSpec = (s) => s.replace(/^[./]+/, "").replace(/\/+$/, "");
|
|
31
67
|
var hasParam = (route) => /(^|\/):[\w]+/.test(route);
|
|
@@ -179,9 +215,17 @@ function shouldResetSessionCache(sessionKey, cached) {
|
|
|
179
215
|
return sessionKey !== void 0 && sessionKey !== cached;
|
|
180
216
|
}
|
|
181
217
|
function noRenderableExportError(spec, inner) {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
218
|
+
const shape = inner === null ? "null" : typeof inner === "object" ? `\u666E\u901A\u5BF9\u8C61\uFF08\u952E\uFF1A${Object.keys(inner).slice(0, 5).join("\u3001") || "\u7A7A"}\uFF09` : typeof inner;
|
|
219
|
+
return Object.assign(
|
|
220
|
+
new Error(
|
|
221
|
+
`\u73B0\u8C61\uFF1A\u8FDC\u7A0B\u6A21\u5757 "${spec}" \u5DF2\u52A0\u8F7D\u6210\u529F\uFF0C\u4F46\u5B83\u5BFC\u51FA\u7684\u5185\u5BB9\u4E0D\u662F\u53EF\u6E32\u67D3\u7EC4\u4EF6\uFF08\u5B9E\u9645\u5F62\u6001\uFF1A${shape}\uFF09\u3002
|
|
222
|
+
\u6839\u56E0\uFF1A\u8BE5 exposes \u6307\u5411\u7684\u6587\u4EF6\u5BFC\u51FA\u7684\u662F\u666E\u901A\u6A21\u5757/\u5E38\u91CF\uFF0C\u6216 default \u5BFC\u51FA\u88AB\u5199\u6210\u4E86\u4E0D\u53EF\u6E32\u67D3\u7684\u503C\uFF1B\u6A21\u5757\u52A0\u8F7D\u6210\u529F\u4E0D\u7B49\u4E8E\u7EC4\u4EF6\u53EF\u7528\u3002
|
|
223
|
+
\u4FEE\u6CD5\uFF1A\u6838\u5BF9\u8FDC\u7A0B exposes["${spec.slice(spec.indexOf("/") + 1)}"] \u6307\u5411\u7684\u6587\u4EF6\u2014\u2014\u7EC4\u4EF6\u573A\u666F\u5E94 export default \u4E00\u4E2A\u7EC4\u4EF6\uFF08Vue\uFF1AdefineComponent/<script setup> \u7EC4\u4EF6\u6216\u51FD\u6570\u5F0F\u7EC4\u4EF6\uFF1BReact\uFF1A\u51FD\u6570\u7EC4\u4EF6\u6216 memo/forwardRef\uFF09\uFF1B\u82E5\u672C\u610F\u662F\u6D88\u8D39\u666E\u901A\u6A21\u5757/\u51FD\u6570\uFF0C\u8BF7\u6539\u7528 loadRemote \u800C\u4E0D\u662F remoteComponent\u3002`
|
|
224
|
+
),
|
|
225
|
+
{
|
|
226
|
+
code: ErrorCodes.COMPONENT_EXPORT_INVALID,
|
|
227
|
+
details: { remote: spec.slice(0, spec.indexOf("/")) || spec, module: spec.slice(spec.indexOf("/") + 1) || void 0, exportShape: typeof inner }
|
|
228
|
+
}
|
|
185
229
|
);
|
|
186
230
|
}
|
|
187
231
|
function createHostPagesCore(options) {
|
|
@@ -277,6 +321,29 @@ function resolvePageProviderEntries(list) {
|
|
|
277
321
|
}
|
|
278
322
|
|
|
279
323
|
// src/vue-adapter.ts
|
|
324
|
+
function isVueRenderableComponent(inner) {
|
|
325
|
+
if (!inner) return false;
|
|
326
|
+
if (typeof inner === "function") return true;
|
|
327
|
+
if (typeof inner !== "object") return false;
|
|
328
|
+
const c = inner;
|
|
329
|
+
return typeof c.render === "function" || typeof c.setup === "function" || typeof c.template === "string";
|
|
330
|
+
}
|
|
331
|
+
function vueRuntimeIdentityError(spec) {
|
|
332
|
+
const sharedVue = globalThis.__FULGURJS_RUNTIME__?.getLoadedShare?.("vue");
|
|
333
|
+
if (!sharedVue || typeof sharedVue.ref !== "function" || typeof sharedVue.version !== "string") return void 0;
|
|
334
|
+
if (sharedVue.ref === import_vue.ref) return void 0;
|
|
335
|
+
return Object.assign(
|
|
336
|
+
new Error(
|
|
337
|
+
`\u73B0\u8C61\uFF1A\u6D88\u8D39\u65B9\u76F4\u6E32\u67D3\u8FDC\u7A0B\u7EC4\u4EF6 "${spec}" \u65F6\uFF0C\u68C0\u6D4B\u5230\u6E32\u67D3\u5668\u4F7F\u7528\u7684 Vue \u4E0E\u5171\u4EAB\u4F5C\u7528\u57DF\u5B9E\u9645\u6536\u655B\u7684 Vue \u4E0D\u662F\u540C\u4E00\u5B9E\u4F8B\uFF08\u6E32\u67D3\u94FE ${import_vue.version}\uFF0C\u5171\u4EAB\u5B9E\u4F8B ${sharedVue.version}\uFF1B\u7248\u672C\u53F7\u76F8\u540C\u4E5F\u4E0D\u4EE3\u8868\u5B9E\u4F8B\u76F8\u540C\uFF09\u3002
|
|
338
|
+
\u6839\u56E0\uFF08\u5DF2\u8BC1\u5B9E\uFF09\uFF1A\u53CC\u65B9\u81F3\u5C11\u4E00\u65B9\u58F0\u660E\u4E86 shared.vue\uFF0C\u4F46\u6D88\u8D39\u65B9\u81EA\u8EAB\u7684 vue \u5BFC\u5165\u6CA1\u6709\u6536\u655B\u5230\u540C\u4E00\u5B9E\u4F8B\u2014\u2014\u5E38\u89C1\u4E8E\u6D88\u8D39\u65B9\u672A\u58F0\u660E shared.vue\u3001dev \u9884\u6784\u5EFA/optimizeDeps \u7ED5\u8FC7\u5171\u4EAB\u95E8\u9762\u3001\u6216 shareScope \u4E0D\u4E00\u81F4\u3002\u5B9E\u4F8B\u5206\u88C2\u65F6 inject/provide/Pinia/getCurrentInstance \u8DE8\u5B9E\u4F8B\u65AD\u94FE\uFF0C\u9875\u9762\u5E38\u8868\u73B0\u4E3A\u5916\u58F3\u5728\u3001\u5185\u90E8\u7A7A\u767D\u4E14\u65E0 pageerror\u3002
|
|
339
|
+
\u4FEE\u6CD5\uFF1A\u2460 \u6D88\u8D39\u65B9\u4E0E\u8FDC\u7A0B\u90FD\u5728 federation({ shared }) \u58F0\u660E vue\uFF08singleton \u5EFA\u8BAE\u5F00\u542F\uFF09\u5E76\u4F7F\u7528\u540C\u4E00 shareScope\uFF1B\u2461 dev \u4E0B\u786E\u8BA4\u63D2\u4EF6\u7248\u672C\u4E00\u81F4\u3001\u672A\u7528 optimizeDeps \u628A vue \u9884\u6784\u5EFA\u6210\u7B2C\u4E8C\u4EFD\u526F\u672C\uFF1B\u2462 \u9700\u8981\u72EC\u7ACB Vue \u8FD0\u884C\u65F6\u7684\u5B8C\u6574\u5B50\u5E94\u7528\u4E0D\u8981\u7528 remoteComponent \u76F4\u6E32\u67D3\uFF0C\u6539\u7528\u5E94\u7528\u6865\u63A5\uFF08defineBridgeApp/createVueBridgeApp\uFF09\u3002\u6838\u5B9E\u624B\u6BB5\uFF1Awindow.__FULGURJS_SCOPE__.default.vue \u67E5\u770B\u5B9E\u9645\u6536\u655B\u6761\u76EE\u4E0E\u6765\u6E90\u3002`
|
|
340
|
+
),
|
|
341
|
+
{
|
|
342
|
+
code: ErrorCodes.VUE_RUNTIME_IDENTITY_MISMATCH,
|
|
343
|
+
details: { remote: spec.slice(0, spec.indexOf("/")) || spec, module: spec.slice(spec.indexOf("/") + 1) || void 0, rendererVueVersion: import_vue.version, sharedVueVersion: sharedVue.version }
|
|
344
|
+
}
|
|
345
|
+
);
|
|
346
|
+
}
|
|
280
347
|
var ERROR_STYLE = {
|
|
281
348
|
padding: "16px",
|
|
282
349
|
border: "1px solid #fde2e2",
|
|
@@ -341,7 +408,8 @@ var RemoteErrorPlaceholder = /* @__PURE__ */ (0, import_vue.defineComponent)({
|
|
|
341
408
|
return (0, import_vue.h)("div", { style: ERROR_STYLE, "data-fulgurjs-error": code }, [
|
|
342
409
|
(0, import_vue.h)("p", { style: "margin:0 0 4px;font-weight:600" }, `\u8FDC\u7A0B\u7EC4\u4EF6\u52A0\u8F7D\u5931\u8D25\uFF08\u9519\u8BEF\u7801 ${code}\uFF09`),
|
|
343
410
|
(0, import_vue.h)("p", { style: "margin:0 0 8px;word-break:break-all" }, message),
|
|
344
|
-
(0, import_vue.h)("p", { style: "margin:0 0 8px" }, "\u4FEE\u6CD5\uFF1A\u2460 \u6838\u5BF9 spec \u7684\u300C\u8FDC\u7A0B\u540D/expose \u540D\u300D\u4E0E\u8FDC\u7A0B\u5E94\u7528 exposes \u662F\u5426\u4E00\u81F4\uFF08MFU-006/008\uFF09\uFF1B\u2461 \u6838\u5BF9 remotes \u5730\u5740\u7AEF\u53E3\u4E0E\u8FDC\u7A0B\u670D\u52A1\u53EF\u8FBE\u6027\u3001remoteEntry \u662F\u5426\u53EF\u8BBF\u95EE\uFF08MFU-001\uFF09\uFF1B\u2462 \u67E5\u770B window \u7684 fulgurjs:error \u4E8B\u4EF6\u4E0E console \u540C\u6E90\u9519\u8BEF\u5B9A\u4F4D\u6839\u56E0\u3002"),
|
|
411
|
+
(0, import_vue.h)("p", { style: "margin:0 0 8px" }, "\u4FEE\u6CD5\uFF1A\u2460 \u6838\u5BF9 spec \u7684\u300C\u8FDC\u7A0B\u540D/expose \u540D\u300D\u4E0E\u8FDC\u7A0B\u5E94\u7528 exposes \u662F\u5426\u4E00\u81F4\uFF08MFU-006/008/018\uFF09\uFF1B\u2461 \u6838\u5BF9 remotes \u5730\u5740\u7AEF\u53E3\u4E0E\u8FDC\u7A0B\u670D\u52A1\u53EF\u8FBE\u6027\u3001remoteEntry \u662F\u5426\u53EF\u8BBF\u95EE\uFF08MFU-001\uFF09\uFF1B\u2462 \u67E5\u770B window \u7684 fulgurjs:error \u4E8B\u4EF6\u4E0E console \u540C\u6E90\u9519\u8BEF\u5B9A\u4F4D\u6839\u56E0\u3002"),
|
|
412
|
+
(0, import_vue.h)("p", { style: "margin:0 0 8px" }, "\u7F51\u7EDC\u53D6\u8BC1\uFF1ANetwork \u9762\u677F\u770B\u5931\u8D25\u7684\u5177\u4F53 chunk\u2014\u2014\u6709\u7EA2\u8272\u8BF7\u6C42\uFF08404/502\uFF09= \u8D44\u6E90\u7F3A\u5931\u6216\u4EE3\u7406\u9519\u8BEF\uFF1B\u96F6\u65B0\u8BF7\u6C42\u4E14\u76F4\u63A5\u62D2\u7EDD = \u6D4F\u89C8\u5668\u5DF2\u7F13\u5B58\u4E0A\u6B21 import \u5931\u8D25\uFF08\u540C URL \u4E0D\u518D\u53D1\u8BF7\u6C42\uFF09\uFF0C\u7528\u300C\u5237\u65B0\u9875\u9762\u91CD\u8BD5\u300D\u7A7F\u900F\uFF1B\u5916\u58F3\u6B63\u5E38\u800C\u5185\u90E8\u7A7A\u767D\u4E14\u65E0\u62A5\u9519 = \u4F18\u5148\u6838\u5BF9 shared.vue \u5B9E\u4F8B\u6536\u655B\uFF08MFU-019\uFF09\u3002"),
|
|
345
413
|
(0, import_vue.h)("p", { style: "margin:0 0 10px" }, "\u300C\u91CD\u8BD5\u52A0\u8F7D\u300D\u5728\u5F53\u524D\u9875\u9762\u91CD\u5EFA\u52A0\u8F7D\uFF1B\u82E5\u6D4F\u89C8\u5668\u5DF2\u7F13\u5B58\u5931\u8D25\u7684\u6A21\u5757\uFF08\u5982\u8FDC\u7A0B\u9759\u6001\u5B50\u4F9D\u8D56\u66FE\u52A0\u8F7D\u5931\u8D25\uFF09\uFF0C\u8BF7\u7528\u300C\u5237\u65B0\u9875\u9762\u91CD\u8BD5\u300D\u2014\u2014\u5B83\u4F1A\u6574\u9875\u5237\u65B0\uFF08\u4FDD\u7559\u5F53\u524D\u5730\u5740\uFF09\uFF0C\u672A\u4FDD\u5B58\u7684\u9875\u9762\u72B6\u6001\u4F1A\u4E22\u5931\u3002"),
|
|
346
414
|
(0, import_vue.h)("div", { style: "margin:0" }, buttons)
|
|
347
415
|
]);
|
|
@@ -364,7 +432,7 @@ function createRecoverableErrorPlaceholder(loader, spec, userErrorComponent) {
|
|
|
364
432
|
try {
|
|
365
433
|
const mod = await loader();
|
|
366
434
|
const inner = mod?.default ?? mod;
|
|
367
|
-
if (!inner) throw noRenderableExportError(spec, inner);
|
|
435
|
+
if (!isVueRenderableComponent(inner)) throw noRenderableExportError(spec, inner);
|
|
368
436
|
recovered.value = inner;
|
|
369
437
|
} catch (e) {
|
|
370
438
|
retryError.value = e;
|
|
@@ -476,7 +544,13 @@ function createRemoteComponent(loadRemote) {
|
|
|
476
544
|
return loadRemote(spec, {
|
|
477
545
|
retries: opts.retries,
|
|
478
546
|
consumerApp: consumerRegistry(app, captured)
|
|
479
|
-
}).then(async (m) =>
|
|
547
|
+
}).then(async (m) => {
|
|
548
|
+
const inner = m?.default ?? m;
|
|
549
|
+
if (!isVueRenderableComponent(inner)) throw noRenderableExportError(spec, inner);
|
|
550
|
+
const identity = vueRuntimeIdentityError(spec);
|
|
551
|
+
if (identity) throw identity;
|
|
552
|
+
return applyPageProviders(inner, captured);
|
|
553
|
+
});
|
|
480
554
|
};
|
|
481
555
|
let initialApp;
|
|
482
556
|
const loader = async () => {
|
|
@@ -515,9 +589,11 @@ function createHostPages(options, load) {
|
|
|
515
589
|
const captured = {};
|
|
516
590
|
const mod = await load(spec, { consumerApp: consumerRegistry(app, captured) });
|
|
517
591
|
let inner = mod?.default ?? mod;
|
|
518
|
-
if (!inner) {
|
|
592
|
+
if (!isVueRenderableComponent(inner)) {
|
|
519
593
|
throw noRenderableExportError(spec, inner);
|
|
520
594
|
}
|
|
595
|
+
const identity = vueRuntimeIdentityError(spec);
|
|
596
|
+
if (identity) throw identity;
|
|
521
597
|
if (keepAlivePage && inner && typeof inner === "object") {
|
|
522
598
|
inner = Object.defineProperties({ ...inner }, { name: { value: name, configurable: true } });
|
|
523
599
|
}
|
package/dist/vue-adapter.js
CHANGED
|
@@ -6,11 +6,37 @@ import {
|
|
|
6
6
|
readSessionKey,
|
|
7
7
|
resolvePageProviderEntries,
|
|
8
8
|
shouldResetSessionCache
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-MUATDGMR.js";
|
|
10
10
|
import "./chunk-TSXZ7TWA.js";
|
|
11
|
+
import {
|
|
12
|
+
ErrorCodes
|
|
13
|
+
} from "./chunk-4YTQSOGQ.js";
|
|
11
14
|
|
|
12
15
|
// src/vue-adapter.ts
|
|
13
|
-
import { defineAsyncComponent, defineComponent, getCurrentInstance, h, ref, shallowRef, onActivated, onUnmounted } from "vue";
|
|
16
|
+
import { defineAsyncComponent, defineComponent, getCurrentInstance, h, ref, shallowRef, onActivated, onUnmounted, version as vueRuntimeVersion } from "vue";
|
|
17
|
+
function isVueRenderableComponent(inner) {
|
|
18
|
+
if (!inner) return false;
|
|
19
|
+
if (typeof inner === "function") return true;
|
|
20
|
+
if (typeof inner !== "object") return false;
|
|
21
|
+
const c = inner;
|
|
22
|
+
return typeof c.render === "function" || typeof c.setup === "function" || typeof c.template === "string";
|
|
23
|
+
}
|
|
24
|
+
function vueRuntimeIdentityError(spec) {
|
|
25
|
+
const sharedVue = globalThis.__FULGURJS_RUNTIME__?.getLoadedShare?.("vue");
|
|
26
|
+
if (!sharedVue || typeof sharedVue.ref !== "function" || typeof sharedVue.version !== "string") return void 0;
|
|
27
|
+
if (sharedVue.ref === ref) return void 0;
|
|
28
|
+
return Object.assign(
|
|
29
|
+
new Error(
|
|
30
|
+
`\u73B0\u8C61\uFF1A\u6D88\u8D39\u65B9\u76F4\u6E32\u67D3\u8FDC\u7A0B\u7EC4\u4EF6 "${spec}" \u65F6\uFF0C\u68C0\u6D4B\u5230\u6E32\u67D3\u5668\u4F7F\u7528\u7684 Vue \u4E0E\u5171\u4EAB\u4F5C\u7528\u57DF\u5B9E\u9645\u6536\u655B\u7684 Vue \u4E0D\u662F\u540C\u4E00\u5B9E\u4F8B\uFF08\u6E32\u67D3\u94FE ${vueRuntimeVersion}\uFF0C\u5171\u4EAB\u5B9E\u4F8B ${sharedVue.version}\uFF1B\u7248\u672C\u53F7\u76F8\u540C\u4E5F\u4E0D\u4EE3\u8868\u5B9E\u4F8B\u76F8\u540C\uFF09\u3002
|
|
31
|
+
\u6839\u56E0\uFF08\u5DF2\u8BC1\u5B9E\uFF09\uFF1A\u53CC\u65B9\u81F3\u5C11\u4E00\u65B9\u58F0\u660E\u4E86 shared.vue\uFF0C\u4F46\u6D88\u8D39\u65B9\u81EA\u8EAB\u7684 vue \u5BFC\u5165\u6CA1\u6709\u6536\u655B\u5230\u540C\u4E00\u5B9E\u4F8B\u2014\u2014\u5E38\u89C1\u4E8E\u6D88\u8D39\u65B9\u672A\u58F0\u660E shared.vue\u3001dev \u9884\u6784\u5EFA/optimizeDeps \u7ED5\u8FC7\u5171\u4EAB\u95E8\u9762\u3001\u6216 shareScope \u4E0D\u4E00\u81F4\u3002\u5B9E\u4F8B\u5206\u88C2\u65F6 inject/provide/Pinia/getCurrentInstance \u8DE8\u5B9E\u4F8B\u65AD\u94FE\uFF0C\u9875\u9762\u5E38\u8868\u73B0\u4E3A\u5916\u58F3\u5728\u3001\u5185\u90E8\u7A7A\u767D\u4E14\u65E0 pageerror\u3002
|
|
32
|
+
\u4FEE\u6CD5\uFF1A\u2460 \u6D88\u8D39\u65B9\u4E0E\u8FDC\u7A0B\u90FD\u5728 federation({ shared }) \u58F0\u660E vue\uFF08singleton \u5EFA\u8BAE\u5F00\u542F\uFF09\u5E76\u4F7F\u7528\u540C\u4E00 shareScope\uFF1B\u2461 dev \u4E0B\u786E\u8BA4\u63D2\u4EF6\u7248\u672C\u4E00\u81F4\u3001\u672A\u7528 optimizeDeps \u628A vue \u9884\u6784\u5EFA\u6210\u7B2C\u4E8C\u4EFD\u526F\u672C\uFF1B\u2462 \u9700\u8981\u72EC\u7ACB Vue \u8FD0\u884C\u65F6\u7684\u5B8C\u6574\u5B50\u5E94\u7528\u4E0D\u8981\u7528 remoteComponent \u76F4\u6E32\u67D3\uFF0C\u6539\u7528\u5E94\u7528\u6865\u63A5\uFF08defineBridgeApp/createVueBridgeApp\uFF09\u3002\u6838\u5B9E\u624B\u6BB5\uFF1Awindow.__FULGURJS_SCOPE__.default.vue \u67E5\u770B\u5B9E\u9645\u6536\u655B\u6761\u76EE\u4E0E\u6765\u6E90\u3002`
|
|
33
|
+
),
|
|
34
|
+
{
|
|
35
|
+
code: ErrorCodes.VUE_RUNTIME_IDENTITY_MISMATCH,
|
|
36
|
+
details: { remote: spec.slice(0, spec.indexOf("/")) || spec, module: spec.slice(spec.indexOf("/") + 1) || void 0, rendererVueVersion: vueRuntimeVersion, sharedVueVersion: sharedVue.version }
|
|
37
|
+
}
|
|
38
|
+
);
|
|
39
|
+
}
|
|
14
40
|
var ERROR_STYLE = {
|
|
15
41
|
padding: "16px",
|
|
16
42
|
border: "1px solid #fde2e2",
|
|
@@ -75,7 +101,8 @@ var RemoteErrorPlaceholder = /* @__PURE__ */ defineComponent({
|
|
|
75
101
|
return h("div", { style: ERROR_STYLE, "data-fulgurjs-error": code }, [
|
|
76
102
|
h("p", { style: "margin:0 0 4px;font-weight:600" }, `\u8FDC\u7A0B\u7EC4\u4EF6\u52A0\u8F7D\u5931\u8D25\uFF08\u9519\u8BEF\u7801 ${code}\uFF09`),
|
|
77
103
|
h("p", { style: "margin:0 0 8px;word-break:break-all" }, message),
|
|
78
|
-
h("p", { style: "margin:0 0 8px" }, "\u4FEE\u6CD5\uFF1A\u2460 \u6838\u5BF9 spec \u7684\u300C\u8FDC\u7A0B\u540D/expose \u540D\u300D\u4E0E\u8FDC\u7A0B\u5E94\u7528 exposes \u662F\u5426\u4E00\u81F4\uFF08MFU-006/008\uFF09\uFF1B\u2461 \u6838\u5BF9 remotes \u5730\u5740\u7AEF\u53E3\u4E0E\u8FDC\u7A0B\u670D\u52A1\u53EF\u8FBE\u6027\u3001remoteEntry \u662F\u5426\u53EF\u8BBF\u95EE\uFF08MFU-001\uFF09\uFF1B\u2462 \u67E5\u770B window \u7684 fulgurjs:error \u4E8B\u4EF6\u4E0E console \u540C\u6E90\u9519\u8BEF\u5B9A\u4F4D\u6839\u56E0\u3002"),
|
|
104
|
+
h("p", { style: "margin:0 0 8px" }, "\u4FEE\u6CD5\uFF1A\u2460 \u6838\u5BF9 spec \u7684\u300C\u8FDC\u7A0B\u540D/expose \u540D\u300D\u4E0E\u8FDC\u7A0B\u5E94\u7528 exposes \u662F\u5426\u4E00\u81F4\uFF08MFU-006/008/018\uFF09\uFF1B\u2461 \u6838\u5BF9 remotes \u5730\u5740\u7AEF\u53E3\u4E0E\u8FDC\u7A0B\u670D\u52A1\u53EF\u8FBE\u6027\u3001remoteEntry \u662F\u5426\u53EF\u8BBF\u95EE\uFF08MFU-001\uFF09\uFF1B\u2462 \u67E5\u770B window \u7684 fulgurjs:error \u4E8B\u4EF6\u4E0E console \u540C\u6E90\u9519\u8BEF\u5B9A\u4F4D\u6839\u56E0\u3002"),
|
|
105
|
+
h("p", { style: "margin:0 0 8px" }, "\u7F51\u7EDC\u53D6\u8BC1\uFF1ANetwork \u9762\u677F\u770B\u5931\u8D25\u7684\u5177\u4F53 chunk\u2014\u2014\u6709\u7EA2\u8272\u8BF7\u6C42\uFF08404/502\uFF09= \u8D44\u6E90\u7F3A\u5931\u6216\u4EE3\u7406\u9519\u8BEF\uFF1B\u96F6\u65B0\u8BF7\u6C42\u4E14\u76F4\u63A5\u62D2\u7EDD = \u6D4F\u89C8\u5668\u5DF2\u7F13\u5B58\u4E0A\u6B21 import \u5931\u8D25\uFF08\u540C URL \u4E0D\u518D\u53D1\u8BF7\u6C42\uFF09\uFF0C\u7528\u300C\u5237\u65B0\u9875\u9762\u91CD\u8BD5\u300D\u7A7F\u900F\uFF1B\u5916\u58F3\u6B63\u5E38\u800C\u5185\u90E8\u7A7A\u767D\u4E14\u65E0\u62A5\u9519 = \u4F18\u5148\u6838\u5BF9 shared.vue \u5B9E\u4F8B\u6536\u655B\uFF08MFU-019\uFF09\u3002"),
|
|
79
106
|
h("p", { style: "margin:0 0 10px" }, "\u300C\u91CD\u8BD5\u52A0\u8F7D\u300D\u5728\u5F53\u524D\u9875\u9762\u91CD\u5EFA\u52A0\u8F7D\uFF1B\u82E5\u6D4F\u89C8\u5668\u5DF2\u7F13\u5B58\u5931\u8D25\u7684\u6A21\u5757\uFF08\u5982\u8FDC\u7A0B\u9759\u6001\u5B50\u4F9D\u8D56\u66FE\u52A0\u8F7D\u5931\u8D25\uFF09\uFF0C\u8BF7\u7528\u300C\u5237\u65B0\u9875\u9762\u91CD\u8BD5\u300D\u2014\u2014\u5B83\u4F1A\u6574\u9875\u5237\u65B0\uFF08\u4FDD\u7559\u5F53\u524D\u5730\u5740\uFF09\uFF0C\u672A\u4FDD\u5B58\u7684\u9875\u9762\u72B6\u6001\u4F1A\u4E22\u5931\u3002"),
|
|
80
107
|
h("div", { style: "margin:0" }, buttons)
|
|
81
108
|
]);
|
|
@@ -98,7 +125,7 @@ function createRecoverableErrorPlaceholder(loader, spec, userErrorComponent) {
|
|
|
98
125
|
try {
|
|
99
126
|
const mod = await loader();
|
|
100
127
|
const inner = mod?.default ?? mod;
|
|
101
|
-
if (!inner) throw noRenderableExportError(spec, inner);
|
|
128
|
+
if (!isVueRenderableComponent(inner)) throw noRenderableExportError(spec, inner);
|
|
102
129
|
recovered.value = inner;
|
|
103
130
|
} catch (e) {
|
|
104
131
|
retryError.value = e;
|
|
@@ -210,7 +237,13 @@ function createRemoteComponent(loadRemote) {
|
|
|
210
237
|
return loadRemote(spec, {
|
|
211
238
|
retries: opts.retries,
|
|
212
239
|
consumerApp: consumerRegistry(app, captured)
|
|
213
|
-
}).then(async (m) =>
|
|
240
|
+
}).then(async (m) => {
|
|
241
|
+
const inner = m?.default ?? m;
|
|
242
|
+
if (!isVueRenderableComponent(inner)) throw noRenderableExportError(spec, inner);
|
|
243
|
+
const identity = vueRuntimeIdentityError(spec);
|
|
244
|
+
if (identity) throw identity;
|
|
245
|
+
return applyPageProviders(inner, captured);
|
|
246
|
+
});
|
|
214
247
|
};
|
|
215
248
|
let initialApp;
|
|
216
249
|
const loader = async () => {
|
|
@@ -249,9 +282,11 @@ function createHostPages(options, load) {
|
|
|
249
282
|
const captured = {};
|
|
250
283
|
const mod = await load(spec, { consumerApp: consumerRegistry(app, captured) });
|
|
251
284
|
let inner = mod?.default ?? mod;
|
|
252
|
-
if (!inner) {
|
|
285
|
+
if (!isVueRenderableComponent(inner)) {
|
|
253
286
|
throw noRenderableExportError(spec, inner);
|
|
254
287
|
}
|
|
288
|
+
const identity = vueRuntimeIdentityError(spec);
|
|
289
|
+
if (identity) throw identity;
|
|
255
290
|
if (keepAlivePage && inner && typeof inner === "object") {
|
|
256
291
|
inner = Object.defineProperties({ ...inner }, { name: { value: name, configurable: true } });
|
|
257
292
|
}
|
package/docs/en/reference/api.md
CHANGED
|
@@ -339,7 +339,7 @@ export const pageProviders: RemoteSetupModule['pageProviders'] = [
|
|
|
339
339
|
|
|
340
340
|
`RemoteSetupContext`: `{ appContext: Readonly<AppContext>, sessionKey?: string, signal: AbortSignal }`. `appContext` is the snapshot taken from the page-level AppContext **at call time** (never a saved stale reference); `signal` is invalidated when the login generation changes or on logout cleanup.
|
|
341
341
|
|
|
342
|
-
A module with only pageProviders/globalComponents/onSession can omit the default function. Empty modules and non-function defaults remain invalid.
|
|
342
|
+
A module with only pageProviders/globalComponents/onSession can omit the default function. With no initialization, missing registration or ancestor dependency, omit setup entirely. Reference one existing Provider directly; no wrapper file is required for ordinary components. Empty modules and non-function defaults remain invalid.
|
|
343
343
|
|
|
344
344
|
**Keep execution scopes separate**: default setup runs once per remote container; onSession once per login generation; globalComponents installation targets each actual consumer app, and pageProviders applies during component rendering. defineApplication/defineBridgeApp factories run on every mount. Create independent app/router/store instances in factories, not one-time setup. A direct static/dynamic remote import uses the plugin loading chain, but does not render the export as a component or wrap manually rendered components in Providers.
|
|
345
345
|
|
|
@@ -552,7 +552,7 @@ Remote types are on by default (`dts: true`). The chain is **provider generates
|
|
|
552
552
|
|
|
553
553
|
`loadRemote`, `remoteComponent`, `createVueBridgeApp` and `createReactBridgeApp` share one registry (`FgRemoteTypes`, populated by the synced output) with plain imports:
|
|
554
554
|
|
|
555
|
-
- **Synced entry literals** get real module/component/bridge types — parameters, return values, props and `appProps` are all checked precisely; `remoteComponent` only accepts exposes whose default export is a component, and the bridge factories
|
|
555
|
+
- **Synced entry literals** get real module/component/bridge types — parameters, return values, props and `appProps` are all checked precisely; `remoteComponent` only accepts exposes whose default export is a component, and the bridge factories accept typed bridge-contract default exports, including defineBridgeApp and defineApplication (a plain module masquerading as one is a compile error);
|
|
556
556
|
- **Misspelled entry literals** fail at the **call site** once the registry is non-empty (no wide string overload can absorb them);
|
|
557
557
|
- **Dynamic string variables** (entry names assembled at runtime) always pass with an `unknown` result — an honest boundary: loading still works, the type system just does not know the remote's real shape; narrow with an explicit generic when needed;
|
|
558
558
|
- **No types synced** (old remote / `dts: false`): all literals pass with `unknown` results; pages run normally. Precise checking requires upgrading the remote with `dts: true`.
|
|
@@ -623,7 +623,7 @@ The [public type reference](types.md) lists every exported type and its fields b
|
|
|
623
623
|
## One application for standalone and bridge startup: `defineApplication`
|
|
624
624
|
|
|
625
625
|
|
|
626
|
-
Import from `@fulgurjs/federation/vue` or `@fulgurjs/federation/react`; no additional subpath. See [existing application assembly](../guide/existing-apps.md) for complete examples.
|
|
626
|
+
Import from `@fulgurjs/federation/vue` or `@fulgurjs/federation/react`; no additional subpath. See [existing application assembly](../guide/existing-apps.md) for complete examples. Existing standalone startup can stay with a defineBridgeApp adapter reusing initialization. Main relocation is not required; defineApplication is optional unified startup.
|
|
627
627
|
|
|
628
628
|
| Item | Contract |
|
|
629
629
|
|---|---|
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
| `BLD-002` | Build reports a build target below es2022 | `build.target` is below es2022 — top-level await (TLA) needs it | `build.target: 'es2022'` or newer |
|
|
52
52
|
| `BLD-003` | Manual check: required exposed-component props are missing (no automatic warning) | The consumer did not provide a required prop; provider-side builds cannot know whether a host supplies values | Give required props defaults, or guarantee they are always passed at the usage site |
|
|
53
53
|
| `BLD-006` | Build reports that the negotiation facade chunk isolation cannot be injected automatically under an array-shaped output | `build.rollupOptions.output` is an array, so the plugin cannot add the facade chunk isolation branch automatically | Manually complete the isolation rules the plugin suggests in every output branch |
|
|
54
|
+
| `BLD-007` | Build reports "a static dependency cycle between shared provider chunks" (the cycle's chunks are listed) | The local-copy closures of multiple shared keys interleave at chunking time; a common transitive dep is claimed by the first provider chunk and the other statically depends back, forming a cycle. The bundle then fails with undefined/TDZ under some evaluation orders, with no pageerror-level clue | Turn one shared key on the cycle into a plain consumer-local dependency (remove it from both sides' `shared`; install and register it in the consumer — template tags resolve via the consumer's global registry, semantics unchanged); or reduce the number of shared keys so closures no longer interleave. Rebuild afterwards |
|
|
54
55
|
|
|
55
56
|
#### MFU runtime
|
|
56
57
|
|
|
@@ -70,6 +71,8 @@
|
|
|
70
71
|
| `MFU-012` | That `loadRemote` rejects, reporting setup/onSession threw (cause carries the original exception) | The initialization code itself threw | Fix the error inside setup/onSession and retry directly — only the failed stage's cache is cleared (after a setup failure the retry starts at setup; an onSession failure only reruns the session segment); already-successful stages are not repeated |
|
|
71
72
|
| `MFU-013` | Runtime reports the remote declares onSession but the host AppContext lacks sessionKey | The host did not provide a non-sensitive login generation ID (never use a token as one) | In the host login flow call `provideAppContext({ sessionKey })` — generate a new value on every successful login/re-login; token refreshes keep it |
|
|
72
73
|
| `MFU-014` | Runtime reports a recursive loadRemote of the same remote inside setup/onSession's synchronous segment | Loading a module of the same remote inside the initialization's synchronous segment — the call waits on itself, deadlocking | Do not load same-remote modules inside initialization; put cross-remote loads in the async segment |
|
|
74
|
+
| `MFU-018` | remoteComponent / host-pages component consumption reports "the export is not a renderable component" (with the actual export shape) | The exposes target exports a plain module/constant/non-renderable object — a successful module load does not mean a usable component | Point exposes at an `export default` component (Vue: defineComponent / `<script setup>` / functional; React: function component or memo/forwardRef); to consume a plain module use `loadRemote` instead |
|
|
75
|
+
| `MFU-019` | Vue component direct rendering reports "the renderer and the Vue instance converged in the share scope are not the same instance" (with renderer/shared instance versions) | Proven instance split: at least one side declared shared.vue, but the consumer's vue import did not converge onto the same instance (shared.vue not declared, dev prebundling bypassing the facade, or mismatched shareScope). With split instances inject/Pinia/getCurrentInstance break across instances — typically "shell renders, inner blank, no pageerror" | Both sides declare vue in `shared` (singleton recommended) with the same shareScope; in dev verify vue was not prebundled into a second copy; full sub apps that need an independent Vue runtime must use app bridging instead of remoteComponent direct rendering. Verify via `window.__FULGURJS_SCOPE__.default.vue` |
|
|
73
76
|
| `MFU-015` | Runtime reports an illegal bridge contract (the `./bridge` default export lacks mount/unmount or they are not functions) | The module exposed as `./bridge` was not built with `defineBridgeApp` | Build the child app entry's default export with `defineBridgeApp(...)` (see [sub app bridge](../guide/app-bridge.md)) |
|
|
74
77
|
| `MFU-016` | Runtime reports bridge preparation or lifecycle failure (`details.phase` distinguishes getContext/mount/unmount; the root cause carries the child app's original error) | getContext returned a Promise/non-object, mount threw before the first commit, or unmount cleanup threw | Troubleshoot the child app code by phase: getContext must return a synchronous snapshot object; on mount failure clean up the app/root before throwing; fix unmount's cleanup logic — a container whose unmount threw is persistently locked out; recover with a full page refresh |
|
|
75
78
|
| `MFU-017` | Runtime reports bridge session parameters inconsistent with AppContext | The controlled `sessionKey` contradicts the global session (a getContext snapshot mismatch), an illegal value (empty string/number), or a page-level single-session conflict (generations mismatch across instances) | Unify the session across the page's controlled bridge instances; sessionKey takes only non-empty string/null/omitted; on generation switch follow "first null to unmount, clearAppContext, then write the new generation" |
|
|
@@ -271,7 +271,7 @@ The **augmentable interface** shared by all three entries (`/runtime`, `/vue`, `
|
|
|
271
271
|
| `FgStaticEntry<S>` | Static check on string-API parameters (empty-registry/dynamic pass; registered literals narrow to themselves; unregistered literals get an error type carrying the fix text, failing at the call site) |
|
|
272
272
|
| `FgRemoteModule<S>` | The module type for an entry: registered → module namespace; unsynced/dynamic → `unknown` |
|
|
273
273
|
|
|
274
|
-
Framework-entry check types (re-exported by `/vue` and `/react`): `FgComponentEntry<S>` (remoteComponent accepts only exposes whose default export is a component), `FgBridgeEntry<S>` (bridge factories accept
|
|
274
|
+
Framework-entry check types (re-exported by `/vue` and `/react`): `FgComponentEntry<S>` (remoteComponent accepts only exposes whose default export is a component), `FgBridgeEntry<S>` (bridge factories accept a default export implementing the typed bridge contract, including defineBridgeApp and defineApplication), `FgBridgeAppProps<S>` (bridge props extracted from the provider's declaration), `FgRemoteVueComponent<S>` / `FgReactRemoteProps<S>` (real remote component/props types), `FgVueBridgeWrapper<S>` (the Vue bridge wrapper component). Semantics and limits: [API reference · string-API entry checks](api.md#entry-checks-for-the-string-apis-type-registry).
|
|
275
275
|
|
|
276
276
|
## RemoteSchemaEntry
|
|
277
277
|
|
package/docs/zh/reference/api.md
CHANGED
|
@@ -335,7 +335,7 @@ export const pageProviders: RemoteSetupModule['pageProviders'] = [
|
|
|
335
335
|
|
|
336
336
|
`RemoteSetupContext`:`{ appContext: Readonly<AppContext>, sessionKey?: string, signal: AbortSignal }`。`appContext` 是**调用时**从页面级 AppContext 取得的快照(不保存永不更新的旧引用);`signal` 在登录代次变化或退出清理时失效。
|
|
337
337
|
|
|
338
|
-
没有应用级副作用时可只导出 pageProviders/globalComponents/onSession
|
|
338
|
+
没有应用级副作用时可只导出 pageProviders/globalComponents/onSession,省略默认函数;模块至少有一项有效声明,不为通过校验写空函数。没有初始化、缺失注册或祖先依赖时,整个 setup 配置和文件都可省略。单个已有 Provider 直接引用,不要求新建包装组件;普通组件直接 expose 即可。
|
|
339
339
|
|
|
340
340
|
**不要混淆四种执行粒度**:默认 setup 是每个远程容器一次;onSession 是每个登录代次一次;globalComponents 安装按实际消费方 app、pageProviders 包裹按组件渲染生效;defineApplication/defineBridgeApp 工厂则每次挂载都调用。需要独立 app/router/store 的装配写在工厂中,不能放进仅执行一次的 setup。直接静态或动态 import 一个远程业务模块会走插件加载链,但不会自动把模块导出渲染成组件,也不会替手动渲染包装 Provider。
|
|
341
341
|
|
|
@@ -548,7 +548,7 @@ export const bridgeSession = createBridgeSession()
|
|
|
548
548
|
|
|
549
549
|
`loadRemote`、`remoteComponent`、`createVueBridgeApp`、`createReactBridgeApp` 与普通 import 共用同一份注册表类型(`FgRemoteTypes`,由同步产物登记):
|
|
550
550
|
|
|
551
|
-
- **已同步的入口字面量**:获得真实模块/组件/桥接类型——参数、返回值、props、`appProps` 全部精确检查;`remoteComponent`
|
|
551
|
+
- **已同步的入口字面量**:获得真实模块/组件/桥接类型——参数、返回值、props、`appProps` 全部精确检查;`remoteComponent` 只接受默认导出为组件的暴露项,桥接工厂接受带类型的桥接契约默认导出,包括 defineBridgeApp 与 defineApplication 的返回值(普通模块冒充会编译报错);
|
|
552
552
|
- **拼错的入口字面量**:注册表非空时在**调用点编译报错**(不再被宽泛 string 重载兜底通过);
|
|
553
553
|
- **动态字符串变量**(业务拼接的入口名):永远放行,结果类型为 `unknown`——这是诚实边界:运行时仍可加载,但类型系统不知道远程真实形状;需要时可显式泛型 `loadRemote<T>(spec)` 或先用类型化变量收窄;
|
|
554
554
|
- **未同步任何类型**(远程旧版本/`dts: false`):所有字面量放行、结果 `unknown`,页面正常运行;严格类型检查需要升级远程并保持 `dts: true`。
|
|
@@ -619,13 +619,13 @@ void preloadRemote('remote-a/business-form', { mode: 'prefetch' }).catch(reportP
|
|
|
619
619
|
## 同一应用独立启动与桥接:`defineApplication`
|
|
620
620
|
|
|
621
621
|
|
|
622
|
-
Vue 从 `@fulgurjs/federation/vue` 导入,React 从 `@fulgurjs/federation/react` 导入;不新增子路径。完整例子见[已有应用统一装配](../guide/existing-apps.md)
|
|
622
|
+
Vue 从 `@fulgurjs/federation/vue` 导入,React 从 `@fulgurjs/federation/react` 导入;不新增子路径。完整例子见[已有应用统一装配](../guide/existing-apps.md)。已有独立入口可以保留,用 `defineBridgeApp` 另提供桥接并复用原初始化;不要求迁移 main。`defineApplication` 是主动选择统一启动时的可选 API。
|
|
623
623
|
|
|
624
624
|
```ts
|
|
625
625
|
// Vue;React 使用同名入口,工厂返回 ReactElement 或 { element, dispose? }
|
|
626
626
|
import { defineApplication } from '@fulgurjs/federation/vue'
|
|
627
627
|
// application.ts 默认导出这个返回值;exposes 指向 application.ts。
|
|
628
|
-
//
|
|
628
|
+
// 仅选择 defineApplication 统一启动时,独立入口调用 application.start('#app', props)。
|
|
629
629
|
```
|
|
630
630
|
|
|
631
631
|
| 项目 | 合同 |
|
|
@@ -50,6 +50,7 @@
|
|
|
50
50
|
| `BLD-002` | 构建报构建目标低于 es2022 | `build.target` 低于 es2022——顶层 await(TLA)需要 | `build.target: 'es2022'` 或更新 |
|
|
51
51
|
| `BLD-003` | 人工检查项:expose 组件缺少必填 props(不自动发射告警) | 组件需要参数,但消费处未传入;插件无法在提供方构建时知道宿主是否传值 | 必填 props 给默认值,或在使用处保证始终传入 |
|
|
52
52
|
| `BLD-006` | 构建报 output 数组形态下无法自动注入协商门面 chunk 隔离 | `build.rollupOptions.output` 是数组,插件无法自动加协商门面 chunk 隔离分支 | 手工在 output 各分支补齐插件提示的隔离规则 |
|
|
53
|
+
| `BLD-007` | 构建报「共享 provider chunk 之间存在静态依赖环」(列出环上 chunk) | 多个 shared 键的本地副本闭包在分块期交错,公共传递依赖被首占归组,provider chunk 互为静态依赖;产物按某种求值序加载即 undefined/TDZ 且无 pageerror 线索 | 把环上其中一个 shared 键改为消费方本地依赖(双方 `shared` 移除该键,消费方直接安装并注册——模板标签按消费方全局注册解析,语义不变);或减少共享键使闭包不再交错。改完重新构建 |
|
|
53
54
|
|
|
54
55
|
### MFU 运行时
|
|
55
56
|
|
|
@@ -69,6 +70,8 @@
|
|
|
69
70
|
| `MFU-012` | 该次 `loadRemote` 拒绝,报 setup/onSession 执行抛错(cause 含原始异常) | 初始化代码自身抛错 | 修复 setup/onSession 内部错误后直接重试——只清失败阶段缓存(setup 失败重试从 setup 开始;onSession 失败只重跑会话段),已成功阶段不重复 |
|
|
70
71
|
| `MFU-013` | 运行时报远程声明 onSession 但宿主 AppContext 缺 sessionKey | 宿主未提供非敏感登录代次 ID(禁止用 token 充当) | 宿主登录流程 `provideAppContext({ sessionKey })`——每次成功登录/重登生成新值,token 刷新沿用 |
|
|
71
72
|
| `MFU-014` | 运行时报 setup/onSession 同步段内递归 loadRemote 同一远程 | 初始化同步段内加载同远程模块——该调用会等待自身形成死锁 | 初始化内不要加载同远程模块;跨远程加载放异步段 |
|
|
73
|
+
| `MFU-018` | remoteComponent/宿主页面组件消费时报「导出的不是可渲染组件」(附实际导出形态) | 该 exposes 指向的文件导出了普通模块/常量/不可渲染对象——模块加载成功不等于组件可用 | exposes 指向 `export default` 组件(Vue:defineComponent/`<script setup>`/函数式;React:函数组件或 memo/forwardRef);消费普通模块改用 `loadRemote` |
|
|
74
|
+
| `MFU-019` | Vue 组件直渲染时报「渲染器与共享作用域收敛的 Vue 实例不一致」(附渲染链/共享实例双版本) | 已证实的实例分裂:至少一方声明 shared.vue,但消费方 vue 导入未收敛到同一实例(未声明 shared.vue、dev 预构建绕过门面、shareScope 不一致)。实例分裂时 inject/Pinia/getCurrentInstance 跨实例断链,常见「外壳在、内部空白且无 pageerror」 | 两侧 `shared` 均声明 vue(建议 singleton)且 shareScope 一致;dev 核对未把 vue 预构建成第二份;需独立 Vue 运行时的完整子应用改用应用桥接,不要 remoteComponent 直渲染。核实:`window.__FULGURJS_SCOPE__.default.vue` |
|
|
72
75
|
| `MFU-015` | 运行时报桥接契约非法(`./bridge` 默认导出缺 mount/unmount 或非函数) | expose `./bridge` 的模块没有用 `defineBridgeApp` 构造契约对象 | 子应用入口改用 `defineBridgeApp(...)` 默认导出(见[子应用桥接](../guide/app-bridge.md)) |
|
|
73
76
|
| `MFU-016` | 运行时报桥接准备或生命周期失败(`details.phase` 区分 getContext/mount/unmount;根因含子应用原始错误) | getContext 返回 Promise/非对象、mount 首次提交前抛错、或 unmount 清理抛错 | 按 phase 排查子应用代码:getContext 返回同步快照对象;mount 失败先清理 app/root 再抛;unmount 修复清理逻辑——unmount 抛错的容器被持久封锁,整页刷新恢复 |
|
|
74
77
|
| `MFU-017` | 运行时报桥接会话参数与 AppContext 不一致 | 受控 `sessionKey` 与全局会话矛盾(getContext 快照不一致)、非法值(空串/数字)、或页面级单会话冲突(多实例代次不一致) | 同页受控桥接实例统一会话;sessionKey 只用 非空字符串/null/省略 三态;换代按「先 null 卸载、clearAppContext、再写新代次」顺序 |
|
|
@@ -282,7 +282,7 @@ setup/onSession 收到的上下文.
|
|
|
282
282
|
| `FgStaticEntry<S>` | 字符串 API 参数面的静态检查(空注册表/动态放行;已登记收敛为字面量;未登记得到带修法文案的错误类型,调用点报错) |
|
|
283
283
|
| `FgRemoteModule<S>` | 入口对应的模块类型:已登记 → 模块命名空间;未同步/动态 → `unknown` |
|
|
284
284
|
|
|
285
|
-
框架入口的扩展检查类型(`/vue`、`/react` 再导出):`FgComponentEntry<S>`(remoteComponent 只接受默认导出为组件的暴露项)、`FgBridgeEntry<S
|
|
285
|
+
框架入口的扩展检查类型(`/vue`、`/react` 再导出):`FgComponentEntry<S>`(remoteComponent 只接受默认导出为组件的暴露项)、`FgBridgeEntry<S>`(桥接工厂接受带类型的桥接契约默认导出,包括 defineBridgeApp 与 defineApplication 的返回值)、`FgBridgeAppProps<S>`(从提供方声明提取的桥接 props)、`FgRemoteVueComponent<S>` / `FgReactRemoteProps<S>`(远程组件真实类型/props)、`FgVueBridgeWrapper<S>`(Vue 桥接包装组件)。语义与限制见 [API 参考 · 字符串 API 的入口检查](api.md#字符串-api-的入口检查类型注册表)。
|
|
286
286
|
|
|
287
287
|
## RemoteSchema
|
|
288
288
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# fulgurjs-federation 模板(examples/templates/)
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> 本模板面向新建工程,选择 `defineApplication/start` 统一管理独立与嵌入启动。已有工程优先保留原入口,按[首次接入指南](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/zh/guide/first-integration.md)复用业务初始化,不照搬本模板的 main。插件版本以 package.json 和锁文件为准,按下面命令安装 registry 正式包。
|
|
4
4
|
|
|
5
|
-
>
|
|
5
|
+
> 五个新建模板选择 defineApplication 装配;已有工程默认采用保留 main 的桥接路径。两者都是当前有效用法,适用场景不同。
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
五个可独立复制、`pnpm install` 后直接运行的联邦接入模板。全部使用 **npm registry 正式包** `@fulgurjs/federation`(精确版本 + pnpm 锁文件,钉定版本见各模板 package.json;统一入口 /vue、/react、/runtime——模板代码即统一入口用法),不依赖本仓库源码、workspace 或父目录。
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
## 接入代码目录
|
|
17
17
|
|
|
18
|
-
五模板统一采用 `src/fulgurjs/host/`、`exposes/`、`types/`:宿主装配及会话放 `host/`,远程装配和对外组件/模块放 `exposes/`,同步声明放 `types/`。根部仅按需保留 `setup.ts
|
|
18
|
+
五模板统一采用 `src/fulgurjs/host/`、`exposes/`、`types/`:宿主装配及会话放 `host/`,远程装配和对外组件/模块放 `exposes/`,同步声明放 `types/`。根部仅按需保留 `setup.ts`,不用的目录不创建。这些新建模板选择让 `src/main.ts(x)` 启动对应 `application`;这只是模板采用的统一启动方案,已有工程保留原入口并复用初始化即可。原业务 App、页面仍在原位置。完整说明与文件格式见[已有应用接入指南](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/zh/guide/existing-apps.md#接入文件集中放在哪里)。
|
|
19
19
|
|
|
20
20
|
## 获取模板的两种方式
|
|
21
21
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# react-host-vue-remote:完整子应用桥接模板
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> 本模板面向新建工程,选择 `defineApplication/start` 统一管理独立与嵌入启动。已有工程优先保留原入口,按[首次接入指南](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/zh/guide/first-integration.md)复用业务初始化,不照搬本模板的 main。插件版本以 package.json 和锁文件为准,按下面命令安装 registry 正式包。
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
`react-host/` 是宿主,`vue-remote/` 是子应用。复制整个模板目录,在根目录运行:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# vue-remote — Vue 子应用
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
本目录属于新建工程的统一启动模板,安装和启动见[组合说明](../README.md)。已有工程接入时保留自己的业务入口,不直接照搬本模板的 main。
|
|
4
4
|
|
|
5
5
|
## 按顺序读源码
|
|
6
6
|
|
|
@@ -22,4 +22,4 @@ pnpm build
|
|
|
22
22
|
pnpm prod
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
按组合根的 package.json 与锁文件安装正式包;在组合根执行冻结安装。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# React 联邦示例:宿主 + 远程(可直接复制运行)
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> 本模板面向新建工程,选择 `defineApplication/start` 统一管理独立与嵌入启动。已有工程优先保留原入口,按[首次接入指南](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/zh/guide/first-integration.md)复用业务初始化,不照搬本模板的 main。插件版本以 package.json 和锁文件为准,按下面命令安装 registry 正式包。
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
本目录是一对完整、可独立安装运行的 React 19 模块联邦演示:
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
| [`remote/`](./remote/) | **React 远程**(被消费方) | 5203 | 暴露可点击按钮组件、普通 TS 工具模块、联邦首页与参数详情页 |
|
|
11
11
|
| [`host/`](./host/) | **React 宿主**(消费方) | 5204 | 消费 react-remote 的组件/模块/页面,提供导航与懒加载 |
|
|
12
12
|
|
|
13
|
-
- 包管理器为 **pnpm**;复制整个模板目录,包含 workspace
|
|
13
|
+
- 包管理器为 **pnpm**;复制整个模板目录,包含 workspace 和锁文件。插件精确版本以 package.json 和锁文件为准。
|
|
14
14
|
|
|
15
15
|
## 最快开始(从干净拷贝)
|
|
16
16
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 双向桥接 URL 同步(examples/templates/showcase)
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> 本模板面向新建工程,选择 `defineApplication/start` 统一管理独立与嵌入启动。已有工程优先保留原入口,按[首次接入指南](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/zh/guide/first-integration.md)复用业务初始化,不照搬本模板的 main。插件版本以 package.json 和锁文件为准,按下面命令安装 registry 正式包。
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
`@fulgurjs/federation` 桥接 URL 同步的双向旗舰演示(API 见[文档中心](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/README.md)):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# vue-host-react-remote:完整子应用桥接模板
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> 本模板面向新建工程,选择 `defineApplication/start` 统一管理独立与嵌入启动。已有工程优先保留原入口,按[首次接入指南](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/zh/guide/first-integration.md)复用业务初始化,不照搬本模板的 main。插件版本以 package.json 和锁文件为准,按下面命令安装 registry 正式包。
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
`vue-host/` 是宿主,`react-remote/` 是子应用。复制整个模板目录,在根目录运行:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# react-remote — React 子应用
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
本目录属于新建工程的统一启动模板,安装和启动见[组合说明](../README.md)。已有工程接入时保留自己的业务入口,不直接照搬本模板的 main。
|
|
4
4
|
|
|
5
5
|
## 按顺序读源码
|
|
6
6
|
|
|
@@ -22,4 +22,4 @@ pnpm build
|
|
|
22
22
|
pnpm prod
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
按组合根的 package.json 与锁文件安装正式包;在组合根执行冻结安装。
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Vue 联邦示例:宿主 + 远程(可直接复制运行)
|
|
2
2
|
|
|
3
|
-
>
|
|
3
|
+
> 本模板面向新建工程,选择 `defineApplication/start` 统一管理独立与嵌入启动。已有工程优先保留原入口,按[首次接入指南](https://github.com/chenmingye/fulgurjs-federation/blob/master/docs/zh/guide/first-integration.md)复用业务初始化,不照搬本模板的 main。插件版本以 package.json 和锁文件为准,按下面命令安装 registry 正式包。
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
本目录是一对完整、可独立安装运行的 Vue 3 模块联邦演示:
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
| [`remote/`](./remote/) | **Vue 远程**(被消费方) | 5213 | 暴露可点击按钮组件、普通 TS 工具模块、联邦首页与参数详情页 |
|
|
11
11
|
| [`host/`](./host/) | **Vue 宿主**(消费方) | 5214 | 消费 vue-remote 的组件/模块/页面,提供导航与懒加载 |
|
|
12
12
|
|
|
13
|
-
- 包管理器为 **pnpm**;复制整个模板目录,包含 workspace
|
|
13
|
+
- 包管理器为 **pnpm**;复制整个模板目录,包含 workspace 和锁文件。插件精确版本以 package.json 和锁文件为准。
|
|
14
14
|
|
|
15
15
|
## 最快开始(从干净拷贝)
|
|
16
16
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fulgurjs/federation",
|
|
3
|
-
"version": "6.12.
|
|
3
|
+
"version": "6.12.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Vite Module Federation for Vue and React: remote modules, shared dependencies and app bridges.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -209,4 +209,4 @@
|
|
|
209
209
|
"optional": true
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
|
-
}
|
|
212
|
+
}
|
package/dist/chunk-OF2JCXTZ.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
// src/runtime/errors.ts
|
|
2
|
-
var FgError = class extends Error {
|
|
3
|
-
code;
|
|
4
|
-
details;
|
|
5
|
-
constructor(code, message, details, options) {
|
|
6
|
-
super(`[fulgurjs:${code}] ${message}`, options);
|
|
7
|
-
this.name = "FgError";
|
|
8
|
-
this.code = code;
|
|
9
|
-
this.details = details;
|
|
10
|
-
}
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
export {
|
|
14
|
-
FgError
|
|
15
|
-
};
|