@fulgurjs/federation 5.2.5 → 5.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/DESIGN.md +7 -0
- package/README.en.md +86 -4
- package/README.md +125 -3
- package/dist/bridge-app-react.cjs +185 -0
- package/dist/bridge-app-react.d.cts +37 -0
- package/dist/bridge-app-react.d.ts +37 -0
- package/dist/bridge-app-react.js +115 -0
- package/dist/bridge-app-vue.cjs +111 -0
- package/dist/bridge-app-vue.d.cts +34 -0
- package/dist/bridge-app-vue.d.ts +34 -0
- package/dist/bridge-app-vue.js +51 -0
- package/dist/bridge-core-6uLJiMVa.d.ts +42 -0
- package/dist/bridge-core.cjs +232 -0
- package/dist/bridge-core.d.cts +51 -0
- package/dist/bridge-core.d.ts +51 -0
- package/dist/bridge-core.js +21 -0
- package/dist/bridge-errors.cjs +114 -0
- package/dist/bridge-errors.d.cts +36 -0
- package/dist/bridge-errors.d.ts +36 -0
- package/dist/bridge-errors.js +15 -0
- package/dist/bridge-host-react-C55RmGll.d.ts +35 -0
- package/dist/bridge-host-react.cjs +400 -0
- package/dist/bridge-host-react.d.cts +45 -0
- package/dist/bridge-host-react.d.ts +45 -0
- package/dist/bridge-host-react.js +183 -0
- package/dist/bridge-host-vue-BAxDKqGH.d.ts +32 -0
- package/dist/bridge-host-vue.cjs +437 -0
- package/dist/bridge-host-vue.d.cts +39 -0
- package/dist/bridge-host-vue.d.ts +39 -0
- package/dist/bridge-host-vue.js +214 -0
- package/dist/bridge-react.d.ts +12 -0
- package/dist/bridge-react.js +3 -0
- package/dist/bridge-vue.d.ts +8 -0
- package/dist/bridge-vue.js +3 -0
- package/dist/bridge.d.ts +16 -0
- package/dist/bridge.js +5 -0
- package/dist/chunk-DH2QMM4Y.js +76 -0
- package/dist/chunk-LE2G23E7.js +122 -0
- package/dist/chunk-MMXKQN2H.js +210 -0
- package/dist/chunk-OF2JCXTZ.js +15 -0
- package/dist/chunk-TOT3P7AK.js +1229 -0
- package/dist/chunk-TSYLDY6H.js +64 -0
- package/dist/chunk-X4F2BHNM.js +179 -0
- package/dist/context.js +8 -64
- package/dist/index.cjs +139 -13
- package/dist/index.js +139 -13
- package/dist/react.d.ts +47 -2
- package/dist/react.js +2 -0
- package/dist/runtime-entry.d.ts +44 -2
- package/dist/runtime-entry.js +1 -1
- package/dist/runtime.js +19 -19
- package/dist/vue.js +2 -0
- package/docs/webpack-mf-/345/257/271/347/205/247/344/270/216/347/274/272/345/217/243.md +1 -1
- package/docs//350/277/201/347/247/273/346/214/207/345/215/227.md +1 -1
- package/examples/README.en.md +20 -0
- package/examples/README.md +20 -0
- package/examples/bridge/README.md +36 -0
- package/examples/bridge/react-host/README.md +23 -0
- package/examples/bridge/react-host/fulgurjs.config.ts +20 -0
- package/examples/bridge/react-host/index.html +11 -0
- package/examples/bridge/react-host/package-lock.json +2457 -0
- package/examples/bridge/react-host/package.json +24 -0
- package/examples/bridge/react-host/src/env.d.ts +7 -0
- package/examples/bridge/react-host/src/host-session.ts +26 -0
- package/examples/bridge/react-host/src/main.tsx +62 -0
- package/examples/bridge/react-host/tsconfig.json +13 -0
- package/examples/bridge/react-host/vite.config.ts +9 -0
- package/examples/bridge/react-remote/README.md +20 -0
- package/examples/bridge/react-remote/fulgurjs.config.ts +17 -0
- package/examples/bridge/react-remote/index.html +11 -0
- package/examples/bridge/react-remote/package-lock.json +2382 -0
- package/examples/bridge/react-remote/package.json +24 -0
- package/examples/bridge/react-remote/src/bridge.tsx +66 -0
- package/examples/bridge/react-remote/src/env.d.ts +7 -0
- package/examples/bridge/react-remote/src/main.tsx +7 -0
- package/examples/bridge/react-remote/tsconfig.json +13 -0
- package/examples/bridge/react-remote/vite.config.ts +9 -0
- package/examples/bridge/vue-host/README.md +24 -0
- package/examples/bridge/vue-host/fulgurjs.config.ts +22 -0
- package/examples/bridge/vue-host/index.html +11 -0
- package/examples/bridge/vue-host/package-lock.json +1885 -0
- package/examples/bridge/vue-host/package.json +24 -0
- package/examples/bridge/vue-host/src/App.vue +70 -0
- package/examples/bridge/vue-host/src/env.d.ts +7 -0
- package/examples/bridge/vue-host/src/host-session.ts +29 -0
- package/examples/bridge/vue-host/src/main.ts +4 -0
- package/examples/bridge/vue-host/tsconfig.json +13 -0
- package/examples/bridge/vue-host/vite.config.ts +9 -0
- package/examples/bridge/vue-remote/README.md +19 -0
- package/examples/bridge/vue-remote/fulgurjs.config.ts +16 -0
- package/examples/bridge/vue-remote/index.html +11 -0
- package/examples/bridge/vue-remote/package-lock.json +1856 -0
- package/examples/bridge/vue-remote/package.json +21 -0
- package/examples/bridge/vue-remote/src/App.vue +12 -0
- package/examples/bridge/vue-remote/src/bridge.ts +57 -0
- package/examples/bridge/vue-remote/src/env.d.ts +7 -0
- package/examples/bridge/vue-remote/src/main.ts +15 -0
- package/examples/bridge/vue-remote/tsconfig.json +13 -0
- package/examples/bridge/vue-remote/vite.config.ts +9 -0
- package/package.json +43 -2
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Component } from 'vue';
|
|
2
|
+
import { A as AppContext } from './bridge-core-6uLJiMVa.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Vue 宿主桥接工厂(/bridge、/bridge/vue 导出 createVueBridgeApp;任务书 §3.4)。
|
|
6
|
+
*
|
|
7
|
+
* 定位:接收注入的 loadRemote(不静态引用运行时内核,dist 壳绑定),返回一个
|
|
8
|
+
* 包装组件 `{ appProps: P; sessionKey?: string | null }`,负责:
|
|
9
|
+
* - DOM 所有权(§4.5):只创建并保持稳定的空挂载 el(多根 fragment 的固定子节点);
|
|
10
|
+
* pending/error 占位是它的兄弟节点,宿主重渲染不 patch 子应用 root 内部;
|
|
11
|
+
* - 会话代次(§4.3):受控 sessionKey 驱动 挂载/换会话/登出;同会话重渲染不重挂;
|
|
12
|
+
* - 错误诊断:契约非法 MFU-015、生命周期失败 MFU-016(phase 区分)、会话矛盾 MFU-017;
|
|
13
|
+
* 默认占位含「重试加载 / 刷新页面重试」两条恢复操作(§4.4)。
|
|
14
|
+
*
|
|
15
|
+
* appProps 是挂载时浅拷贝快照(顶层替换不追踪、不重渲染子应用,重挂由 :key 重建);
|
|
16
|
+
* sessionKey 是桥接控制参数,不混入业务 props。
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
interface VueBridgeAppOptions {
|
|
20
|
+
/** 加载 pending 占位组件(默认无占位节点) */
|
|
21
|
+
loadingComponent?: Component;
|
|
22
|
+
/** 加载/挂载失败占位(完全接管,契约不变:只接收 error prop;默认内置中文诊断 + 两条恢复操作) */
|
|
23
|
+
errorComponent?: Component;
|
|
24
|
+
/** 透传现行 loadRemote 的重试选项(0-10 整数),不另叠自动重试 */
|
|
25
|
+
retries?: number;
|
|
26
|
+
/** 本次桥接加载等待上限 ms;不取消已发出的共享请求,迟到结果一律丢弃 */
|
|
27
|
+
timeout?: number;
|
|
28
|
+
/** 无副作用的同步 getter:首次、重试及换会话的实际加载前返回本次所需上下文快照 */
|
|
29
|
+
getContext?: () => Partial<AppContext> & Record<string, unknown>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export type { VueBridgeAppOptions as V };
|
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/bridge-host-vue.ts
|
|
21
|
+
var bridge_host_vue_exports = {};
|
|
22
|
+
__export(bridge_host_vue_exports, {
|
|
23
|
+
createVueBridgeAppWithLoader: () => createVueBridgeAppWithLoader
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(bridge_host_vue_exports);
|
|
26
|
+
var import_vue = require("vue");
|
|
27
|
+
|
|
28
|
+
// src/runtime/errors.ts
|
|
29
|
+
var FgError = class extends Error {
|
|
30
|
+
code;
|
|
31
|
+
details;
|
|
32
|
+
constructor(code, message, details, options) {
|
|
33
|
+
super(`[fulgurjs:${code}] ${message}`, options);
|
|
34
|
+
this.name = "FgError";
|
|
35
|
+
this.code = code;
|
|
36
|
+
this.details = details;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// src/context.ts
|
|
41
|
+
var ContextErrorCodes = {
|
|
42
|
+
/** context 必需字段缺失(三段式,修法指向宿主桥) */
|
|
43
|
+
CONTEXT_MISSING_KEY: "CC-001",
|
|
44
|
+
/** 运行时单例不可用(独立直开远程页,修法=经宿主联邦加载) */
|
|
45
|
+
CONTEXT_NO_RUNTIME: "CC-002"
|
|
46
|
+
};
|
|
47
|
+
var APP_CONTEXT_STORAGE_KEY = "__FULGURJS_APP_CONFIG__";
|
|
48
|
+
function requireRuntime() {
|
|
49
|
+
const rt = globalThis.__FULGURJS_RUNTIME__;
|
|
50
|
+
if (!rt || typeof rt.loadRemote !== "function") {
|
|
51
|
+
throw new FgError(
|
|
52
|
+
ContextErrorCodes.CONTEXT_NO_RUNTIME,
|
|
53
|
+
"\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",
|
|
54
|
+
{ runtime: false }
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
return rt;
|
|
58
|
+
}
|
|
59
|
+
function provideAppContext(config) {
|
|
60
|
+
requireRuntime();
|
|
61
|
+
const g = globalThis;
|
|
62
|
+
g[APP_CONTEXT_STORAGE_KEY] = { ...g[APP_CONTEXT_STORAGE_KEY] ?? {}, ...config };
|
|
63
|
+
}
|
|
64
|
+
function clearAppContext() {
|
|
65
|
+
const g = globalThis;
|
|
66
|
+
delete g[APP_CONTEXT_STORAGE_KEY];
|
|
67
|
+
try {
|
|
68
|
+
g.__FULGURJS_RUNTIME__?.clearSessionState?.();
|
|
69
|
+
} catch {
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// src/bridge-errors.ts
|
|
74
|
+
var BridgeErrorCodes = {
|
|
75
|
+
/** 桥接契约非法(./bridge 默认导出缺 mount/unmount 或不是函数) */
|
|
76
|
+
BRIDGE_INVALID_CONTRACT: "MFU-015",
|
|
77
|
+
/** 桥接准备或生命周期失败(details.phase = getContext | mount | unmount) */
|
|
78
|
+
BRIDGE_LIFECYCLE_FAILED: "MFU-016",
|
|
79
|
+
/** 桥接会话参数与 AppContext 不一致(受控 sessionKey 与全局会话矛盾) */
|
|
80
|
+
BRIDGE_SESSION_MISMATCH: "MFU-017"
|
|
81
|
+
};
|
|
82
|
+
function invalidBridgeContractError(spec, mod) {
|
|
83
|
+
const actual = mod === null ? "null" : typeof mod;
|
|
84
|
+
const missing = [];
|
|
85
|
+
if (typeof mod?.mount !== "function") missing.push("mount");
|
|
86
|
+
if (typeof mod?.unmount !== "function") missing.push("unmount");
|
|
87
|
+
return new FgError(
|
|
88
|
+
BridgeErrorCodes.BRIDGE_INVALID_CONTRACT,
|
|
89
|
+
`\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
|
|
90
|
+
\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") + `
|
|
91
|
+
\u4FEE\u6CD5: \u8FDC\u7A0B\u4FA7\u7528 defineBridgeApp(...) \u521B\u5EFA\u5951\u7EA6\u5E76\u4F5C\u4E3A\u6A21\u5757\u9ED8\u8BA4\u5BFC\u51FA\u2014\u2014
|
|
92
|
+
Vue \u5B50\u5E94\u7528: import { defineBridgeApp } from '@fulgurjs/federation/runtime'\uFF1B
|
|
93
|
+
React \u5B50\u5E94\u7528: import { defineBridgeApp } from '@fulgurjs/federation/react'\u3002
|
|
94
|
+
\u5951\u7EA6\u5FC5\u987B\u5305\u542B mount(el, props?) \u4E0E unmount(el) \u4E24\u4E2A\u51FD\u6570\u3002`,
|
|
95
|
+
{ spec, missing, actual }
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
function bridgeLifecycleError(phase, spec, cause, extra) {
|
|
99
|
+
const raw = cause instanceof Error ? cause.message : String(cause ?? "unknown");
|
|
100
|
+
const fixes = {
|
|
101
|
+
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",
|
|
102
|
+
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",
|
|
103
|
+
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"
|
|
104
|
+
};
|
|
105
|
+
return new FgError(
|
|
106
|
+
BridgeErrorCodes.BRIDGE_LIFECYCLE_FAILED,
|
|
107
|
+
`\u73B0\u8C61\uFF1A\u6865\u63A5\u5E94\u7528 "${spec}" \u7684 ${phase} \u9636\u6BB5\u5931\u8D25\u3002
|
|
108
|
+
\u6839\u56E0: ${raw}
|
|
109
|
+
` + fixes[phase],
|
|
110
|
+
{ phase, spec, cause: String(raw), ...extra },
|
|
111
|
+
{ cause }
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
function bridgeSessionMismatchError(spec, controlled, actual, extra) {
|
|
115
|
+
const actualText = actual === void 0 ? "undefined\uFF08\u9875\u9762\u5C1A\u65E0 AppContext\uFF09" : typeof actual === "string" ? `"${actual}"` : String(actual);
|
|
116
|
+
return new FgError(
|
|
117
|
+
BridgeErrorCodes.BRIDGE_SESSION_MISMATCH,
|
|
118
|
+
`\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
|
|
119
|
+
\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
|
|
120
|
+
getContext \u5FEB\u7167\u7684 sessionKey\uFF08\u6216\u73B0\u6709 AppContext \u7684 sessionKey\uFF09\u4E0E\u53D7\u63A7\u503C\u4E0D\u540C\u3002
|
|
121
|
+
\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
|
|
122
|
+
\u51C6\u5907\u65B0\u8D26\u53F7\u5B8C\u6574\u5FEB\u7167\u540E\u66F4\u65B0\u53D7\u63A7 prop\uFF1B
|
|
123
|
+
\u2461 \u65E0 getContext \u65F6\uFF0C\u5148\u7531\u5BBF\u4E3B\u6865 provideAppContext({...\u65B0\u4F1A\u8BDD, sessionKey}) \u518D\u6302\u8F7D\uFF1B
|
|
124
|
+
\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`,
|
|
125
|
+
{ spec, controlled, actual: String(actual), ...extra }
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
function invalidSessionKeyError(spec, value) {
|
|
129
|
+
return new FgError(
|
|
130
|
+
BridgeErrorCodes.BRIDGE_SESSION_MISMATCH,
|
|
131
|
+
`\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
|
|
132
|
+
\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
|
|
133
|
+
\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`,
|
|
134
|
+
{ spec, value: String(value) }
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// src/bridge-core.ts
|
|
139
|
+
function assertBridgeContract(spec, mod) {
|
|
140
|
+
const contract = mod?.default ?? mod;
|
|
141
|
+
if (contract === null || typeof contract !== "object" || typeof contract.mount !== "function" || typeof contract.unmount !== "function") {
|
|
142
|
+
throw invalidBridgeContractError(spec, mod);
|
|
143
|
+
}
|
|
144
|
+
return contract;
|
|
145
|
+
}
|
|
146
|
+
var activeBridgeSessions = /* @__PURE__ */ new Map();
|
|
147
|
+
function acquireBridgeSession(spec, sessionKey) {
|
|
148
|
+
for (const [active] of activeBridgeSessions) {
|
|
149
|
+
if (active !== sessionKey && (activeBridgeSessions.get(active) ?? 0) > 0) {
|
|
150
|
+
throw bridgeSessionMismatchError(spec, sessionKey, active, { reason: "active-instance" });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
activeBridgeSessions.set(sessionKey, (activeBridgeSessions.get(sessionKey) ?? 0) + 1);
|
|
154
|
+
}
|
|
155
|
+
function releaseBridgeSession(sessionKey) {
|
|
156
|
+
const n = activeBridgeSessions.get(sessionKey) ?? 0;
|
|
157
|
+
if (n <= 1) activeBridgeSessions.delete(sessionKey);
|
|
158
|
+
else activeBridgeSessions.set(sessionKey, n - 1);
|
|
159
|
+
}
|
|
160
|
+
function resolveBridgeContext(spec, controlled, getContext) {
|
|
161
|
+
if (getContext) {
|
|
162
|
+
let snapshot;
|
|
163
|
+
try {
|
|
164
|
+
snapshot = getContext();
|
|
165
|
+
} catch (e) {
|
|
166
|
+
throw bridgeLifecycleError("getContext", spec, e);
|
|
167
|
+
}
|
|
168
|
+
if (snapshot === null || typeof snapshot !== "object" || typeof snapshot.then === "function") {
|
|
169
|
+
throw bridgeLifecycleError("getContext", spec, describeContextValue(snapshot));
|
|
170
|
+
}
|
|
171
|
+
if (controlled !== void 0 && snapshot.sessionKey !== controlled) {
|
|
172
|
+
throw bridgeSessionMismatchError(spec, controlled, snapshot.sessionKey, { reason: "getter-snapshot" });
|
|
173
|
+
}
|
|
174
|
+
const current = currentSessionKey();
|
|
175
|
+
if (snapshot.sessionKey !== void 0 && snapshot.sessionKey !== current) {
|
|
176
|
+
clearAppContext();
|
|
177
|
+
}
|
|
178
|
+
return { provide: snapshot };
|
|
179
|
+
}
|
|
180
|
+
if (controlled !== void 0) {
|
|
181
|
+
const current = currentSessionKey();
|
|
182
|
+
if (current !== controlled) {
|
|
183
|
+
throw bridgeSessionMismatchError(spec, controlled, current, { reason: "app-context" });
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return {};
|
|
187
|
+
}
|
|
188
|
+
function currentSessionKey() {
|
|
189
|
+
const sk = (globalThis.__FULGURJS_APP_CONFIG__ ?? {}).sessionKey;
|
|
190
|
+
return typeof sk === "string" && sk !== "" ? sk : void 0;
|
|
191
|
+
}
|
|
192
|
+
function describeContextValue(snapshot) {
|
|
193
|
+
if (snapshot === null) {
|
|
194
|
+
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";
|
|
195
|
+
}
|
|
196
|
+
if (snapshot === void 0) {
|
|
197
|
+
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";
|
|
198
|
+
}
|
|
199
|
+
if (typeof snapshot.then === "function") {
|
|
200
|
+
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";
|
|
201
|
+
}
|
|
202
|
+
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`;
|
|
203
|
+
}
|
|
204
|
+
function assertControlledSessionKey(spec, value) {
|
|
205
|
+
if (value === void 0 || value === null) return;
|
|
206
|
+
if (typeof value === "string" && value !== "") return;
|
|
207
|
+
throw invalidSessionKeyError(spec, value);
|
|
208
|
+
}
|
|
209
|
+
function assertBridgeOptions(source, opts) {
|
|
210
|
+
if (opts.retries !== void 0 && (typeof opts.retries !== "number" || !Number.isInteger(opts.retries) || opts.retries < 0 || opts.retries > 10)) {
|
|
211
|
+
throw new Error(
|
|
212
|
+
`[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`
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
if (opts.timeout !== void 0 && (typeof opts.timeout !== "number" || !Number.isFinite(opts.timeout) || opts.timeout <= 0)) {
|
|
216
|
+
throw new Error(
|
|
217
|
+
`[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`
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
function withBridgeTimeout(p, ms, spec) {
|
|
222
|
+
return new Promise((resolve, reject) => {
|
|
223
|
+
const timer = setTimeout(() => {
|
|
224
|
+
reject(new Error(
|
|
225
|
+
`[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`
|
|
226
|
+
));
|
|
227
|
+
}, ms);
|
|
228
|
+
p.then(
|
|
229
|
+
(v) => {
|
|
230
|
+
clearTimeout(timer);
|
|
231
|
+
resolve(v);
|
|
232
|
+
},
|
|
233
|
+
(e) => {
|
|
234
|
+
clearTimeout(timer);
|
|
235
|
+
reject(e);
|
|
236
|
+
}
|
|
237
|
+
);
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// src/bridge-host-vue.ts
|
|
242
|
+
var ERROR_STYLE = {
|
|
243
|
+
padding: "16px",
|
|
244
|
+
border: "1px solid #fde2e2",
|
|
245
|
+
borderRadius: "4px",
|
|
246
|
+
background: "#fef0f0",
|
|
247
|
+
color: "#c45656",
|
|
248
|
+
fontSize: "13px",
|
|
249
|
+
lineHeight: "1.6"
|
|
250
|
+
};
|
|
251
|
+
var BUTTON_STYLE = {
|
|
252
|
+
padding: "4px 14px",
|
|
253
|
+
marginRight: "8px",
|
|
254
|
+
border: "1px solid #c45656",
|
|
255
|
+
borderRadius: "4px",
|
|
256
|
+
background: "#c45656",
|
|
257
|
+
color: "#fff",
|
|
258
|
+
fontSize: "13px",
|
|
259
|
+
cursor: "pointer"
|
|
260
|
+
};
|
|
261
|
+
var SECONDARY_BUTTON_STYLE = {
|
|
262
|
+
padding: "4px 14px",
|
|
263
|
+
border: "1px solid #c45656",
|
|
264
|
+
borderRadius: "4px",
|
|
265
|
+
background: "#fff",
|
|
266
|
+
color: "#c45656",
|
|
267
|
+
fontSize: "13px",
|
|
268
|
+
cursor: "pointer"
|
|
269
|
+
};
|
|
270
|
+
var BridgeErrorPlaceholder = (0, import_vue.defineComponent)({
|
|
271
|
+
name: "FulgurjsBridgeError",
|
|
272
|
+
props: {
|
|
273
|
+
error: { type: Object, default: void 0 },
|
|
274
|
+
retry: { type: Function, required: false, default: void 0 }
|
|
275
|
+
},
|
|
276
|
+
setup(props) {
|
|
277
|
+
const reloadPage = () => {
|
|
278
|
+
window.location.reload();
|
|
279
|
+
};
|
|
280
|
+
return () => {
|
|
281
|
+
const err = props.error;
|
|
282
|
+
const code = err?.code ?? "UNKNOWN";
|
|
283
|
+
const message = err?.message ?? String(props.error ?? "unknown error");
|
|
284
|
+
return (0, import_vue.h)("div", { style: ERROR_STYLE, "data-fulgurjs-error": code }, [
|
|
285
|
+
(0, import_vue.h)("p", { style: "margin:0 0 4px;font-weight:600" }, `\u6865\u63A5\u5E94\u7528\u52A0\u8F7D\u5931\u8D25\uFF08\u9519\u8BEF\u7801 ${code}\uFF09`),
|
|
286
|
+
(0, import_vue.h)("p", { style: "margin:0 0 8px;word-break:break-all" }, message),
|
|
287
|
+
(0, import_vue.h)("p", { style: "margin:0 0 10px" }, "\u300C\u91CD\u8BD5\u52A0\u8F7D\u300D\u5728\u5F53\u524D\u9875\u9762\u91CD\u5EFA\u6302\u8F7D\uFF1B\u82E5\u6D4F\u89C8\u5668\u5DF2\u7F13\u5B58\u5931\u8D25\u7684\u6A21\u5757\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"),
|
|
288
|
+
(0, import_vue.h)("div", { style: "margin:0" }, [
|
|
289
|
+
props.retry ? (0, import_vue.h)("button", {
|
|
290
|
+
key: "retry",
|
|
291
|
+
style: BUTTON_STYLE,
|
|
292
|
+
type: "button",
|
|
293
|
+
"data-fulgurjs-retry": "",
|
|
294
|
+
onClick: () => props.retry?.()
|
|
295
|
+
}, "\u91CD\u8BD5\u52A0\u8F7D") : null,
|
|
296
|
+
(0, import_vue.h)("button", {
|
|
297
|
+
key: "reload",
|
|
298
|
+
style: SECONDARY_BUTTON_STYLE,
|
|
299
|
+
type: "button",
|
|
300
|
+
"data-fulgurjs-reload": "",
|
|
301
|
+
onClick: reloadPage
|
|
302
|
+
}, "\u5237\u65B0\u9875\u9762\u91CD\u8BD5")
|
|
303
|
+
])
|
|
304
|
+
]);
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
function createVueBridgeAppWithLoader(loadRemote) {
|
|
309
|
+
return function createVueBridgeApp(spec, options = {}) {
|
|
310
|
+
assertBridgeOptions(`createVueBridgeApp("${spec}")`, options);
|
|
311
|
+
const componentName = "FulgurjsBridge_" + spec.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
312
|
+
return (0, import_vue.defineComponent)({
|
|
313
|
+
name: componentName,
|
|
314
|
+
// 多根组件:宿主传入的 class/style 等不自动继承(v1 不提供样式透传,外观由宿主外层元素控制)
|
|
315
|
+
inheritAttrs: false,
|
|
316
|
+
props: {
|
|
317
|
+
appProps: { type: Object, required: false, default: void 0 },
|
|
318
|
+
// 桥接控制参数:undefined(不启用受控会话)/ null(登出态)/ 非空字符串(登录代次)
|
|
319
|
+
sessionKey: { type: null, required: false, default: void 0 }
|
|
320
|
+
},
|
|
321
|
+
setup(props) {
|
|
322
|
+
const status = (0, import_vue.ref)("idle");
|
|
323
|
+
const error = (0, import_vue.shallowRef)(void 0);
|
|
324
|
+
const container = (0, import_vue.ref)(null);
|
|
325
|
+
let generation = 0;
|
|
326
|
+
let contract;
|
|
327
|
+
let containerBlocked = false;
|
|
328
|
+
let acquiredSession;
|
|
329
|
+
const releaseAcquired = () => {
|
|
330
|
+
if (acquiredSession !== void 0) {
|
|
331
|
+
releaseBridgeSession(acquiredSession);
|
|
332
|
+
acquiredSession = void 0;
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
const invalidate = () => {
|
|
336
|
+
if (containerBlocked) return false;
|
|
337
|
+
generation++;
|
|
338
|
+
const el = container.value;
|
|
339
|
+
releaseAcquired();
|
|
340
|
+
if (contract && el) {
|
|
341
|
+
const c = contract;
|
|
342
|
+
contract = void 0;
|
|
343
|
+
try {
|
|
344
|
+
c.unmount(el);
|
|
345
|
+
} catch (e) {
|
|
346
|
+
const err = bridgeLifecycleError("unmount", spec, e instanceof Error ? e : String(e));
|
|
347
|
+
containerBlocked = true;
|
|
348
|
+
console.error("[fulgurjs] \u6865\u63A5\u5E94\u7528\u5378\u8F7D\u5931\u8D25\uFF0C\u8BE5\u5BB9\u5668\u5DF2\u5C01\u9501\uFF08\u540C\u9875\u53EA\u80FD\u5237\u65B0\u6062\u590D\uFF09\uFF1A", err);
|
|
349
|
+
error.value = err;
|
|
350
|
+
status.value = "error";
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return true;
|
|
355
|
+
};
|
|
356
|
+
const run = async (myGeneration) => {
|
|
357
|
+
const el = container.value;
|
|
358
|
+
if (!el) return;
|
|
359
|
+
const sk = props.sessionKey;
|
|
360
|
+
status.value = "pending";
|
|
361
|
+
error.value = void 0;
|
|
362
|
+
if (sk === null) {
|
|
363
|
+
status.value = "idle";
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
try {
|
|
367
|
+
assertControlledSessionKey(spec, sk);
|
|
368
|
+
const controlled = typeof sk === "string" ? sk : void 0;
|
|
369
|
+
if (controlled !== void 0 && controlled !== acquiredSession) {
|
|
370
|
+
releaseAcquired();
|
|
371
|
+
acquireBridgeSession(spec, controlled);
|
|
372
|
+
acquiredSession = controlled;
|
|
373
|
+
}
|
|
374
|
+
const resolution = resolveBridgeContext(spec, controlled, options.getContext);
|
|
375
|
+
if (resolution.provide) provideAppContext(resolution.provide);
|
|
376
|
+
const load = loadRemote(spec, { retries: options.retries });
|
|
377
|
+
const mod = options.timeout !== void 0 ? await withBridgeTimeout(load, options.timeout, spec) : await load;
|
|
378
|
+
if (myGeneration !== generation) return;
|
|
379
|
+
contract = assertBridgeContract(spec, mod);
|
|
380
|
+
const appPropsSource = props.appProps ? (0, import_vue.toRaw)(props.appProps) : void 0;
|
|
381
|
+
await contract.mount(el, { ...appPropsSource ?? {} });
|
|
382
|
+
if (myGeneration !== generation) return;
|
|
383
|
+
status.value = "ready";
|
|
384
|
+
} catch (e) {
|
|
385
|
+
if (myGeneration !== generation) return;
|
|
386
|
+
error.value = e;
|
|
387
|
+
status.value = "error";
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
const start = () => {
|
|
391
|
+
const ok = invalidate();
|
|
392
|
+
if (!ok) return;
|
|
393
|
+
const myGeneration = generation;
|
|
394
|
+
void run(myGeneration);
|
|
395
|
+
};
|
|
396
|
+
const retry = () => {
|
|
397
|
+
start();
|
|
398
|
+
};
|
|
399
|
+
(0, import_vue.onMounted)(() => {
|
|
400
|
+
const myGeneration = generation;
|
|
401
|
+
void run(myGeneration);
|
|
402
|
+
});
|
|
403
|
+
(0, import_vue.watch)(() => props.sessionKey, () => {
|
|
404
|
+
start();
|
|
405
|
+
});
|
|
406
|
+
(0, import_vue.onBeforeUnmount)(() => {
|
|
407
|
+
invalidate();
|
|
408
|
+
});
|
|
409
|
+
return () => {
|
|
410
|
+
const children = [];
|
|
411
|
+
if (status.value === "pending" && options.loadingComponent) {
|
|
412
|
+
children.push((0, import_vue.h)(options.loadingComponent, { key: "loading" }));
|
|
413
|
+
}
|
|
414
|
+
if (status.value === "error") {
|
|
415
|
+
const err = error.value;
|
|
416
|
+
if (options.errorComponent) {
|
|
417
|
+
children.push((0, import_vue.h)(options.errorComponent, { key: "error", error: err }));
|
|
418
|
+
} else {
|
|
419
|
+
children.push((0, import_vue.h)(BridgeErrorPlaceholder, { key: "error", error: err, retry: containerBlocked ? void 0 : retry }));
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
children.push((0, import_vue.h)("div", {
|
|
423
|
+
key: "bridge-root",
|
|
424
|
+
ref: container,
|
|
425
|
+
"data-fulgurjs-bridge-root": spec,
|
|
426
|
+
"data-fulgurjs-bridge-status": status.value
|
|
427
|
+
}));
|
|
428
|
+
return children;
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
435
|
+
0 && (module.exports = {
|
|
436
|
+
createVueBridgeAppWithLoader
|
|
437
|
+
});
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Component } from 'vue';
|
|
2
|
+
import { AppContext } from './context.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Vue 宿主桥接工厂(/bridge、/bridge/vue 导出 createVueBridgeApp;任务书 §3.4)。
|
|
6
|
+
*
|
|
7
|
+
* 定位:接收注入的 loadRemote(不静态引用运行时内核,dist 壳绑定),返回一个
|
|
8
|
+
* 包装组件 `{ appProps: P; sessionKey?: string | null }`,负责:
|
|
9
|
+
* - DOM 所有权(§4.5):只创建并保持稳定的空挂载 el(多根 fragment 的固定子节点);
|
|
10
|
+
* pending/error 占位是它的兄弟节点,宿主重渲染不 patch 子应用 root 内部;
|
|
11
|
+
* - 会话代次(§4.3):受控 sessionKey 驱动 挂载/换会话/登出;同会话重渲染不重挂;
|
|
12
|
+
* - 错误诊断:契约非法 MFU-015、生命周期失败 MFU-016(phase 区分)、会话矛盾 MFU-017;
|
|
13
|
+
* 默认占位含「重试加载 / 刷新页面重试」两条恢复操作(§4.4)。
|
|
14
|
+
*
|
|
15
|
+
* appProps 是挂载时浅拷贝快照(顶层替换不追踪、不重渲染子应用,重挂由 :key 重建);
|
|
16
|
+
* sessionKey 是桥接控制参数,不混入业务 props。
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
type LoadRemoteFn = (spec: string, opts?: {
|
|
20
|
+
retries?: number;
|
|
21
|
+
}) => Promise<any>;
|
|
22
|
+
interface VueBridgeAppOptions {
|
|
23
|
+
/** 加载 pending 占位组件(默认无占位节点) */
|
|
24
|
+
loadingComponent?: Component;
|
|
25
|
+
/** 加载/挂载失败占位(完全接管,契约不变:只接收 error prop;默认内置中文诊断 + 两条恢复操作) */
|
|
26
|
+
errorComponent?: Component;
|
|
27
|
+
/** 透传现行 loadRemote 的重试选项(0-10 整数),不另叠自动重试 */
|
|
28
|
+
retries?: number;
|
|
29
|
+
/** 本次桥接加载等待上限 ms;不取消已发出的共享请求,迟到结果一律丢弃 */
|
|
30
|
+
timeout?: number;
|
|
31
|
+
/** 无副作用的同步 getter:首次、重试及换会话的实际加载前返回本次所需上下文快照 */
|
|
32
|
+
getContext?: () => Partial<AppContext> & Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 构建 Vue 宿主桥接工厂(dist 壳与 dev 门面绑定各自运行时的 loadRemote 后导出)。
|
|
36
|
+
*/
|
|
37
|
+
declare function createVueBridgeAppWithLoader(loadRemote: LoadRemoteFn): <P = Record<string, unknown>>(spec: string, options?: VueBridgeAppOptions) => Component;
|
|
38
|
+
|
|
39
|
+
export { type VueBridgeAppOptions, createVueBridgeAppWithLoader };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Component } from 'vue';
|
|
2
|
+
import { AppContext } from './context.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Vue 宿主桥接工厂(/bridge、/bridge/vue 导出 createVueBridgeApp;任务书 §3.4)。
|
|
6
|
+
*
|
|
7
|
+
* 定位:接收注入的 loadRemote(不静态引用运行时内核,dist 壳绑定),返回一个
|
|
8
|
+
* 包装组件 `{ appProps: P; sessionKey?: string | null }`,负责:
|
|
9
|
+
* - DOM 所有权(§4.5):只创建并保持稳定的空挂载 el(多根 fragment 的固定子节点);
|
|
10
|
+
* pending/error 占位是它的兄弟节点,宿主重渲染不 patch 子应用 root 内部;
|
|
11
|
+
* - 会话代次(§4.3):受控 sessionKey 驱动 挂载/换会话/登出;同会话重渲染不重挂;
|
|
12
|
+
* - 错误诊断:契约非法 MFU-015、生命周期失败 MFU-016(phase 区分)、会话矛盾 MFU-017;
|
|
13
|
+
* 默认占位含「重试加载 / 刷新页面重试」两条恢复操作(§4.4)。
|
|
14
|
+
*
|
|
15
|
+
* appProps 是挂载时浅拷贝快照(顶层替换不追踪、不重渲染子应用,重挂由 :key 重建);
|
|
16
|
+
* sessionKey 是桥接控制参数,不混入业务 props。
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
type LoadRemoteFn = (spec: string, opts?: {
|
|
20
|
+
retries?: number;
|
|
21
|
+
}) => Promise<any>;
|
|
22
|
+
interface VueBridgeAppOptions {
|
|
23
|
+
/** 加载 pending 占位组件(默认无占位节点) */
|
|
24
|
+
loadingComponent?: Component;
|
|
25
|
+
/** 加载/挂载失败占位(完全接管,契约不变:只接收 error prop;默认内置中文诊断 + 两条恢复操作) */
|
|
26
|
+
errorComponent?: Component;
|
|
27
|
+
/** 透传现行 loadRemote 的重试选项(0-10 整数),不另叠自动重试 */
|
|
28
|
+
retries?: number;
|
|
29
|
+
/** 本次桥接加载等待上限 ms;不取消已发出的共享请求,迟到结果一律丢弃 */
|
|
30
|
+
timeout?: number;
|
|
31
|
+
/** 无副作用的同步 getter:首次、重试及换会话的实际加载前返回本次所需上下文快照 */
|
|
32
|
+
getContext?: () => Partial<AppContext> & Record<string, unknown>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 构建 Vue 宿主桥接工厂(dist 壳与 dev 门面绑定各自运行时的 loadRemote 后导出)。
|
|
36
|
+
*/
|
|
37
|
+
declare function createVueBridgeAppWithLoader(loadRemote: LoadRemoteFn): <P = Record<string, unknown>>(spec: string, options?: VueBridgeAppOptions) => Component;
|
|
38
|
+
|
|
39
|
+
export { type VueBridgeAppOptions, createVueBridgeAppWithLoader };
|