@fulgurjs/federation 5.0.4 → 5.1.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 +18 -0
- package/DESIGN.md +11 -0
- package/README.en.md +356 -0
- package/README.md +117 -4
- package/dist/chunk-4PLNAYZX.js +84 -0
- package/dist/cli.js +4 -1
- package/dist/host-pages-core-BFxp-IOj.d.cts +37 -0
- package/dist/host-pages-core-DLfMeDrO.d.ts +37 -0
- package/dist/index.cjs +282 -27
- package/dist/index.js +282 -27
- package/dist/react-adapter.cjs +521 -0
- package/dist/react-adapter.d.cts +117 -0
- package/dist/react-adapter.d.ts +117 -0
- package/dist/react-adapter.js +295 -0
- package/dist/react.d.ts +442 -0
- package/dist/react.js +10 -0
- package/dist/runtime-entry.d.ts +29 -17
- package/dist/runtime.js +13 -13
- package/dist/vue-adapter.cjs +66 -50
- package/dist/vue-adapter.d.cts +4 -25
- package/dist/vue-adapter.d.ts +4 -25
- package/dist/vue-adapter.js +14 -66
- package/examples/README.md +5 -0
- package/examples/react-host/README.md +28 -0
- package/examples/react-host/fulgurjs.config.ts +19 -0
- package/examples/react-host/index.html +11 -0
- package/examples/react-host/package.json +23 -0
- package/examples/react-host/src/App.tsx +66 -0
- package/examples/react-host/src/federation/pages.data.ts +21 -0
- package/examples/react-host/src/main.tsx +8 -0
- package/examples/react-host/tsconfig.json +14 -0
- package/examples/react-host/vite.config.ts +8 -0
- package/examples/react-remote/fulgurjs.config.ts +17 -0
- package/examples/react-remote/index.html +11 -0
- package/examples/react-remote/package.json +22 -0
- package/examples/react-remote/src/Button.tsx +15 -0
- package/examples/react-remote/src/main.tsx +6 -0
- package/examples/react-remote/src/pages/Detail.tsx +10 -0
- package/examples/react-remote/src/pages/Home.tsx +14 -0
- package/examples/react-remote/src/utils.ts +3 -0
- package/examples/react-remote/tsconfig.json +14 -0
- package/examples/react-remote/vite.config.ts +8 -0
- package/package.json +30 -3
|
@@ -0,0 +1,521 @@
|
|
|
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/react-adapter.ts
|
|
21
|
+
var react_adapter_exports = {};
|
|
22
|
+
__export(react_adapter_exports, {
|
|
23
|
+
RemoteErrorBoundary: () => RemoteErrorBoundary,
|
|
24
|
+
createReactHostPages: () => createReactHostPages,
|
|
25
|
+
createRemoteComponent: () => createRemoteComponent,
|
|
26
|
+
createUseLoadRemote: () => createUseLoadRemote
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(react_adapter_exports);
|
|
29
|
+
var import_react = require("react");
|
|
30
|
+
|
|
31
|
+
// src/pages.ts
|
|
32
|
+
var normSpec = (s) => s.replace(/^[./]+/, "").replace(/\/+$/, "");
|
|
33
|
+
var hasParam = (route) => /(^|\/):[\w]+/.test(route);
|
|
34
|
+
var segs = (route) => route.split("/").filter(Boolean);
|
|
35
|
+
function defaultDeriveSpec(route) {
|
|
36
|
+
return segs(route).slice(1).filter((seg) => !seg.startsWith(":")).join("/");
|
|
37
|
+
}
|
|
38
|
+
function shadows(earlier, later) {
|
|
39
|
+
const a = segs(earlier);
|
|
40
|
+
const b = segs(later);
|
|
41
|
+
if (a.length !== b.length) return null;
|
|
42
|
+
if (earlier === later) return "full";
|
|
43
|
+
let hasParam2 = false;
|
|
44
|
+
for (let i = 0; i < a.length; i++) {
|
|
45
|
+
if (a[i] === b[i]) continue;
|
|
46
|
+
if (a[i].startsWith(":")) {
|
|
47
|
+
hasParam2 = true;
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return hasParam2 ? "param" : null;
|
|
53
|
+
}
|
|
54
|
+
function remoteOfRoute(route, remotes) {
|
|
55
|
+
if (!remotes) return null;
|
|
56
|
+
for (const [prefix, name] of Object.entries(remotes)) {
|
|
57
|
+
if (route === prefix.replace(/\/+$/, "") || route.startsWith(prefix)) return name;
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
function validatePages(pages, options = {}) {
|
|
62
|
+
const deriveSpec = options.deriveSpec ?? defaultDeriveSpec;
|
|
63
|
+
const violations = [];
|
|
64
|
+
const effective = pages.map((p) => normSpec(p.spec ?? deriveSpec(p.route)));
|
|
65
|
+
pages.forEach((p, i) => {
|
|
66
|
+
if (p.spec || !hasParam(p.route)) return;
|
|
67
|
+
pages.forEach((q, j) => {
|
|
68
|
+
if (i === j) return;
|
|
69
|
+
if (effective[j] !== effective[i]) return;
|
|
70
|
+
violations.push({
|
|
71
|
+
rule: "R1",
|
|
72
|
+
level: "error",
|
|
73
|
+
message: `[R1] \u8DEF\u7531 "${p.route}" \u5265\u53C2\u63A8\u5BFC spec "${effective[i]}" \u4E0E\u8DEF\u7531 "${q.route}"${q.spec ? "\uFF08\u663E\u5F0F spec\uFF09" : ""}\u6536\u655B\u76F8\u540C\u3002
|
|
74
|
+
\u6839\u56E0\uFF1A\u53C2\u6570\u6BB5\u5265\u79BB\u540E expose \u952E\u4E0E\u5176\u5B83\u6761\u76EE\u76F8\u540C\uFF0C\u8BE5\u8DEF\u7531\u4F1A\u9759\u9ED8\u52A0\u8F7D\u9519\u8BEF\u7EC4\u4EF6
|
|
75
|
+
\uFF08\u771F\u5B9E\u4E8B\u6545\uFF1A\u5E26\u53C2\u52A8\u4F5C\u9875\u8BEF\u8D70\u5217\u8868/\u66F4\u65B0\u8BED\u4E49\uFF0C\u6570\u636E\u98CE\u9669\uFF09\u3002
|
|
76
|
+
\u4FEE\u6CD5\uFF1A\u4E3A\u8BE5\u6761\u76EE\u663E\u5F0F\u58F0\u660E spec: '...'\uFF08\u6307\u5411\u5E26\u53C2\u9875\u9762\u81EA\u5DF1\u7684 expose\uFF09\uFF0C\u6216\u8C03\u6574\u8DEF\u7531\u62C6\u5206\u3002`
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
const seenSpec = /* @__PURE__ */ new Map();
|
|
81
|
+
pages.forEach((p, i) => {
|
|
82
|
+
const key = effective[i];
|
|
83
|
+
if (seenSpec.has(key)) {
|
|
84
|
+
const first = pages[seenSpec.get(key)];
|
|
85
|
+
const bothExplicit = !!p.spec && !!first.spec;
|
|
86
|
+
violations.push({
|
|
87
|
+
rule: "R2",
|
|
88
|
+
level: "warn",
|
|
89
|
+
message: `[R2] \u8DEF\u7531 "${p.route}" \u4E0E "${first.route}" \u6709\u6548 spec \u76F8\u540C\uFF08"${key}"\uFF09\u3002${bothExplicit ? "\u4E24\u6761\u5747\u4E3A\u663E\u5F0F spec" : "\u5176\u4E2D\u542B\u63A8\u5BFC spec"}\u2014\u2014\u82E5\u4E3A\u523B\u610F\u7684\u83DC\u5355\u522B\u540D\u53EF\u5FFD\u7565\u672C\u8B66\u544A\uFF0C\u5426\u5219\u8BF7\u6838\u5BF9\u3002`
|
|
90
|
+
});
|
|
91
|
+
} else {
|
|
92
|
+
seenSpec.set(key, i);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
if (options.schema && options.remotes) {
|
|
96
|
+
pages.forEach((p, i) => {
|
|
97
|
+
const remote = remoteOfRoute(p.route, options.remotes);
|
|
98
|
+
if (!remote) return;
|
|
99
|
+
const entry = options.schema[remote];
|
|
100
|
+
if (!entry || entry.exists === false) return;
|
|
101
|
+
const exposes = new Set((entry.exposes ?? []).map(normSpec));
|
|
102
|
+
if (!exposes.has(effective[i])) {
|
|
103
|
+
violations.push({
|
|
104
|
+
rule: "R3",
|
|
105
|
+
level: "error",
|
|
106
|
+
message: `[R3] \u8DEF\u7531 "${p.route}" \u7684 spec "${effective[i]}" \u4E0D\u5728\u8FDC\u7A0B "${remote}" \u7684 exposes \u6E05\u5355\u4E2D\u3002
|
|
107
|
+
\u6839\u56E0\uFF1Aspec \u62FC\u5199\u4E0E\u8FDC\u7A0B exposes \u952E\u4E0D\u4E00\u81F4\uFF08\u8FD0\u884C\u65F6\u5C06 MFU-006 \u52A0\u8F7D\u5931\u8D25\uFF09\u3002
|
|
108
|
+
\u4FEE\u6CD5\uFF1A\u6838\u5BF9\u8FDC\u7A0B vite.config exposes \u952E\u540D\uFF0C\u6216\u4FEE\u6B63\u672C\u8868 spec\u3002
|
|
109
|
+
\u8FDC\u7A0B\u5B9E\u9645 exposes\uFF08\u524D 10\uFF09\uFF1A${[...exposes].slice(0, 10).join("\u3001") || "\uFF08\u7A7A\uFF09"}`
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
pages.forEach((p, i) => {
|
|
115
|
+
pages.forEach((q, j) => {
|
|
116
|
+
if (j <= i) return;
|
|
117
|
+
const kind = shadows(p.route, q.route);
|
|
118
|
+
if (!kind) return;
|
|
119
|
+
violations.push({
|
|
120
|
+
rule: "R4",
|
|
121
|
+
level: "error",
|
|
122
|
+
message: kind === "full" ? `[R4] \u8DEF\u7531\u5B8C\u5168\u91CD\u590D\uFF1A"${p.route}"\uFF08\u7B2C ${i + 1} \u6761\uFF09\u4E0E "${q.route}"\uFF08\u7B2C ${j + 1} \u6761\uFF09\u3002` : `[R4] \u8DEF\u7531 "${q.route}"\uFF08\u7B2C ${j + 1} \u6761\uFF09\u4F1A\u88AB\u66F4\u9760\u524D\u7684 "${p.route}" \u906E\u853D\uFF08\u5339\u914D\u5148\u5230\u5148\u5F97\uFF0C\u540E\u8005\u6210\u6B7B\u8DEF\u7531\uFF09\u3002
|
|
123
|
+
\u4FEE\u6CD5\uFF1A\u8C03\u6574\u6570\u7EC4\u987A\u5E8F\uFF08\u66F4\u5177\u4F53\u7684\u9759\u6001\u8DEF\u7531\u653E\u524D\u9762\uFF09\u6216\u5408\u5E76\u6761\u76EE\u3002`
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
const seenName = /* @__PURE__ */ new Map();
|
|
128
|
+
pages.forEach((p, i) => {
|
|
129
|
+
const name = typeof p.name === "string" ? p.name : "";
|
|
130
|
+
if (!name) return;
|
|
131
|
+
if (seenName.has(name)) {
|
|
132
|
+
violations.push({
|
|
133
|
+
rule: "R5",
|
|
134
|
+
level: "warn",
|
|
135
|
+
message: `[R5] name "${name}" \u91CD\u590D\uFF08"${pages[seenName.get(name)].route}" \u4E0E "${p.route}"\uFF09\u2014\u2014vue-router \u547D\u540D\u8DF3\u8F6C push({name}) \u5C06\u547D\u4E2D\u524D\u8005\uFF0C\u8BF7\u6838\u5BF9\u3002`
|
|
136
|
+
});
|
|
137
|
+
} else {
|
|
138
|
+
seenName.set(name, i);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
return violations;
|
|
142
|
+
}
|
|
143
|
+
function formatViolations(violations) {
|
|
144
|
+
const errors = violations.filter((v) => v.level === "error");
|
|
145
|
+
const warns = violations.filter((v) => v.level === "warn");
|
|
146
|
+
let out = "";
|
|
147
|
+
if (errors.length) {
|
|
148
|
+
out += `[fulgurjs] \u8DEF\u7531\u8868\u6821\u9A8C\u5931\u8D25\uFF08${errors.length} \u9879\uFF0CERROR\uFF09\uFF1A
|
|
149
|
+
` + errors.map((v) => v.message).join("\n") + "\n";
|
|
150
|
+
}
|
|
151
|
+
if (warns.length) {
|
|
152
|
+
out += `[fulgurjs] \u8DEF\u7531\u8868\u6821\u9A8C\u8B66\u544A\uFF08${warns.length} \u9879\uFF0CWARN\uFF09\uFF1A
|
|
153
|
+
` + warns.map((v) => v.message).join("\n");
|
|
154
|
+
}
|
|
155
|
+
return out.trim();
|
|
156
|
+
}
|
|
157
|
+
function definePages(pages, options = {}) {
|
|
158
|
+
const violations = validatePages(pages, options);
|
|
159
|
+
const errors = violations.filter((v) => v.level === "error");
|
|
160
|
+
const warns = violations.filter((v) => v.level === "warn");
|
|
161
|
+
const text = formatViolations(violations);
|
|
162
|
+
if (text) {
|
|
163
|
+
if (errors.length && options.strict !== false) {
|
|
164
|
+
throw new Error(text);
|
|
165
|
+
}
|
|
166
|
+
console[errors.length ? "error" : "warn"](text);
|
|
167
|
+
}
|
|
168
|
+
return pages;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// src/host-pages-core.ts
|
|
172
|
+
var cleanCompName = (spec) => "Fulgurjs_" + spec.replace(/[^A-Za-z0-9_-]/g, "_");
|
|
173
|
+
function defaultDeriveSpec2(route) {
|
|
174
|
+
return route.split("/").filter(Boolean).slice(1).filter((seg) => !seg.startsWith(":")).join("/");
|
|
175
|
+
}
|
|
176
|
+
function readSessionKey() {
|
|
177
|
+
const sk = (globalThis.__FULGURJS_APP_CONFIG__ ?? {}).sessionKey;
|
|
178
|
+
return typeof sk === "string" && sk !== "" ? sk : void 0;
|
|
179
|
+
}
|
|
180
|
+
function shouldResetSessionCache(sessionKey, cached) {
|
|
181
|
+
return sessionKey !== void 0 && sessionKey !== cached;
|
|
182
|
+
}
|
|
183
|
+
function noRenderableExportError(spec, inner) {
|
|
184
|
+
return new Error(
|
|
185
|
+
`[fulgurjs] \u8FDC\u7A0B\u6A21\u5757 "${spec}" \u6CA1\u6709\u5BFC\u51FA\u53EF\u6E32\u67D3\u7EC4\u4EF6\uFF1B\u5F53\u524D\u503C\u7C7B\u578B\u4E3A ${inner === null ? "null" : typeof inner}\u3002
|
|
186
|
+
\u4FEE\u6CD5: \u6838\u5BF9\u8FDC\u7A0B exposes \u952E\u540D\u4E0E\u9875\u9762\u8868 spec \u662F\u5426\u4E00\u81F4\uFF08MFU-006 \u8BED\u4E49\uFF09`
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
function createHostPagesCore(options) {
|
|
190
|
+
const { pages, remotePrefixes, deriveSpec, schema, strict, base } = options;
|
|
191
|
+
definePages(pages, { deriveSpec, remotes: remotePrefixes, schema, strict });
|
|
192
|
+
const prefixes = Object.entries(remotePrefixes).map(([p, name]) => [p.endsWith("/") ? p : `${p}/`, name]).sort((a, b) => b[0].length - a[0].length);
|
|
193
|
+
const remoteOf = (route) => {
|
|
194
|
+
for (const [prefix, name] of prefixes) {
|
|
195
|
+
if (route.startsWith(prefix) || `${route}/` === prefix) return name;
|
|
196
|
+
}
|
|
197
|
+
throw new Error(
|
|
198
|
+
`[fulgurjs] \u9875\u9762\u8DEF\u7531 "${route}" \u672A\u5339\u914D\u4EFB\u4F55 remotePrefixes\uFF1A{ ${Object.entries(remotePrefixes).map(([k, v]) => `'${k}': '${v}'`).join(", ")} }
|
|
199
|
+
\u6839\u56E0: \u9875\u9762\u8868\u6761\u76EE\u7684\u8DEF\u7531\u4E0D\u5728\u4EFB\u4F55\u5DF2\u58F0\u660E\u7684\u8FDC\u7A0B\u524D\u7F00\u4E0B\u3002
|
|
200
|
+
\u4FEE\u6CD5: \u5728 remotePrefixes \u8865\u5145\u8BE5\u8DEF\u7531\u524D\u7F00 \u2192 \u8FDC\u7A0B\u540D\u7684\u6620\u5C04\uFF0C\u6216\u4FEE\u6B63\u9875\u9762\u8868 route\u3002`
|
|
201
|
+
);
|
|
202
|
+
};
|
|
203
|
+
for (const page of pages) remoteOf(page.route);
|
|
204
|
+
const specOf = (page) => {
|
|
205
|
+
const s = page.spec ?? (deriveSpec ? deriveSpec(page.route) : defaultDeriveSpec2(page.route));
|
|
206
|
+
return `${remoteOf(page.route)}/${String(s).replace(/^[./]+/, "")}`;
|
|
207
|
+
};
|
|
208
|
+
const baseNorm = base && base !== "/" ? base.endsWith("/") ? base : `${base}/` : "";
|
|
209
|
+
const matchSegments = (pattern, path) => {
|
|
210
|
+
const patSegs = pattern.split("/").filter(Boolean);
|
|
211
|
+
const pathSegs = path.split("/").filter(Boolean);
|
|
212
|
+
if (patSegs.length !== pathSegs.length) return null;
|
|
213
|
+
const params = {};
|
|
214
|
+
for (let i = 0; i < patSegs.length; i++) {
|
|
215
|
+
const seg = patSegs[i];
|
|
216
|
+
if (seg.startsWith(":")) {
|
|
217
|
+
try {
|
|
218
|
+
params[seg.slice(1)] = decodeURIComponent(pathSegs[i]);
|
|
219
|
+
} catch {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
} else if (seg !== pathSegs[i]) {
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return params;
|
|
227
|
+
};
|
|
228
|
+
const resolve = (rawPath) => {
|
|
229
|
+
let clean = String(rawPath ?? "");
|
|
230
|
+
if (baseNorm) {
|
|
231
|
+
if (clean === baseNorm.replace(/\/$/, "")) clean = "/";
|
|
232
|
+
else if (clean.startsWith(baseNorm)) clean = `/${clean.slice(baseNorm.length)}`;
|
|
233
|
+
}
|
|
234
|
+
clean = (clean.replace(/\/+$/, "") || "/").split("?")[0].split("#")[0];
|
|
235
|
+
for (const page of pages) {
|
|
236
|
+
const params = matchSegments(page.route, clean);
|
|
237
|
+
if (params) return { page, remote: remoteOf(page.route), spec: specOf(page), params };
|
|
238
|
+
}
|
|
239
|
+
return null;
|
|
240
|
+
};
|
|
241
|
+
return { pages, resolve, remoteOf, specOf };
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// src/react-adapter.ts
|
|
245
|
+
var ERROR_STYLE = {
|
|
246
|
+
padding: "16px",
|
|
247
|
+
border: "1px solid #fde2e2",
|
|
248
|
+
borderRadius: "4px",
|
|
249
|
+
background: "#fef0f0",
|
|
250
|
+
color: "#c45656",
|
|
251
|
+
fontSize: "13px",
|
|
252
|
+
lineHeight: "1.6",
|
|
253
|
+
boxSizing: "border-box"
|
|
254
|
+
};
|
|
255
|
+
var TITLE_STYLE = { margin: "0 0 4px", fontWeight: 600 };
|
|
256
|
+
var MESSAGE_STYLE = { margin: "0 0 8px", wordBreak: "break-all" };
|
|
257
|
+
var FIX_STYLE = { margin: "0 0 8px" };
|
|
258
|
+
var DefaultErrorView = function FulgurjsRemoteError({ error, retry, phase }) {
|
|
259
|
+
const err = error;
|
|
260
|
+
const code = err?.code ?? "UNKNOWN";
|
|
261
|
+
const message = err?.message ?? String(error ?? "unknown error");
|
|
262
|
+
return (0, import_react.createElement)("div", { style: ERROR_STYLE, "data-fulgurjs-error": code }, [
|
|
263
|
+
(0, import_react.createElement)(
|
|
264
|
+
"p",
|
|
265
|
+
{ key: "t", style: TITLE_STYLE },
|
|
266
|
+
phase === "load" ? `\u8FDC\u7A0B\u7EC4\u4EF6\u52A0\u8F7D\u5931\u8D25\uFF08\u9519\u8BEF\u7801 ${code}\uFF09` : `\u8FDC\u7A0B\u7EC4\u4EF6\u6E32\u67D3\u51FA\u9519\uFF08\u9519\u8BEF\u7801 ${code}\uFF09`
|
|
267
|
+
),
|
|
268
|
+
(0, import_react.createElement)("p", { key: "m", style: MESSAGE_STYLE }, message),
|
|
269
|
+
(0, import_react.createElement)("p", { key: "f", style: FIX_STYLE }, phase === "render" ? "\u4FEE\u6CD5\uFF1A\u67E5\u770B\u6D4F\u89C8\u5668 console \u4E2D\u7684\u7EC4\u4EF6\u8C03\u7528\u6808\u5B9A\u4F4D\u8FDC\u7A0B\u7EC4\u4EF6\u5185\u90E8\u7684\u6E32\u67D3\u5F02\u5E38\uFF08\u9519\u8BEF\u629B\u81EA\u8FDC\u7A0B\u4EE3\u7801\u672C\u8EAB\uFF0C\u4E0E\u7F51\u7EDC\u52A0\u8F7D\u65E0\u5173\uFF09\u3002" : "\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\uFF08MFU-001\uFF09\uFF1B\u2462 \u67E5\u770B window \u7684 fulgurjs:error \u4E8B\u4EF6\u4E0E console \u540C\u6E90\u9519\u8BEF\u5B9A\u4F4D\u6839\u56E0\u3002"),
|
|
270
|
+
(0, import_react.createElement)("button", { key: "r", onClick: retry, type: "button" }, "\u91CD\u8BD5")
|
|
271
|
+
]);
|
|
272
|
+
};
|
|
273
|
+
function isValidComponentType(v) {
|
|
274
|
+
if (typeof v === "function") return true;
|
|
275
|
+
if (v !== null && typeof v === "object" && typeof v.$$typeof === "symbol") return true;
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
function assertAdapterOptions(source, opts) {
|
|
279
|
+
if (opts.retries !== void 0 && (typeof opts.retries !== "number" || !Number.isInteger(opts.retries) || opts.retries < 0 || opts.retries > 10)) {
|
|
280
|
+
throw new Error(
|
|
281
|
+
`[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`
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
if (opts.timeout !== void 0 && (typeof opts.timeout !== "number" || !Number.isFinite(opts.timeout) || opts.timeout <= 0)) {
|
|
285
|
+
throw new Error(
|
|
286
|
+
`[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`
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
function renderErrorNode(errorOpt, error, retry, phase) {
|
|
291
|
+
if (typeof errorOpt === "function") return errorOpt(error, retry);
|
|
292
|
+
if (errorOpt !== void 0) return errorOpt;
|
|
293
|
+
return (0, import_react.createElement)(DefaultErrorView, { error, retry, phase });
|
|
294
|
+
}
|
|
295
|
+
var RemoteErrorBoundary = class extends import_react.Component {
|
|
296
|
+
state = { error: null };
|
|
297
|
+
static getDerivedStateFromError(error) {
|
|
298
|
+
return { error };
|
|
299
|
+
}
|
|
300
|
+
componentDidCatch(error, info) {
|
|
301
|
+
console.error("[fulgurjs] \u8FDC\u7A0B\u7EC4\u4EF6\u6E32\u67D3\u5F02\u5E38\uFF08RemoteErrorBoundary \u6355\u83B7\uFF09\uFF1A", error, info?.componentStack ?? "");
|
|
302
|
+
this.props.onError?.(error, info);
|
|
303
|
+
}
|
|
304
|
+
componentDidUpdate(prev) {
|
|
305
|
+
if (this.state.error !== null && !shallowArrayEq(prev.resetKeys, this.props.resetKeys)) {
|
|
306
|
+
this.reset();
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
reset = () => {
|
|
310
|
+
this.setState({ error: null });
|
|
311
|
+
};
|
|
312
|
+
render() {
|
|
313
|
+
if (this.state.error !== null) {
|
|
314
|
+
const f = this.props.fallback;
|
|
315
|
+
if (typeof f === "function") return f({ error: this.state.error, reset: this.reset });
|
|
316
|
+
if (f !== void 0) return f;
|
|
317
|
+
return renderErrorNode(void 0, this.state.error, this.reset, "render");
|
|
318
|
+
}
|
|
319
|
+
return this.props.children;
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
function shallowArrayEq(a, b) {
|
|
323
|
+
if (a === b) return true;
|
|
324
|
+
if (!a || !b || a.length !== b.length) return false;
|
|
325
|
+
return a.every((v, i) => v === b[i]);
|
|
326
|
+
}
|
|
327
|
+
function RemoteLoader({ spec, opts, loadRemote, beforeLoad, attempt, sessionKey, retry, passthrough, fwdRef }) {
|
|
328
|
+
const [state, setState] = (0, import_react.useState)({ phase: "pending" });
|
|
329
|
+
const latest = (0, import_react.useRef)({ passthrough, fwdRef });
|
|
330
|
+
latest.current = { passthrough, fwdRef };
|
|
331
|
+
(0, import_react.useEffect)(() => {
|
|
332
|
+
let cancelled = false;
|
|
333
|
+
let settled = false;
|
|
334
|
+
let timer;
|
|
335
|
+
const gen = `${attempt}@${sessionKey ?? ""}`;
|
|
336
|
+
const commit = (next) => {
|
|
337
|
+
if (cancelled || settled || gen !== `${attempt}@${sessionKey ?? ""}`) return;
|
|
338
|
+
settled = true;
|
|
339
|
+
setState(next);
|
|
340
|
+
};
|
|
341
|
+
setState({ phase: "pending" });
|
|
342
|
+
const run = (async () => {
|
|
343
|
+
await beforeLoad?.();
|
|
344
|
+
return loadRemote(spec, { retries: opts.retries });
|
|
345
|
+
})();
|
|
346
|
+
run.catch(() => {
|
|
347
|
+
});
|
|
348
|
+
const race = opts.timeout === void 0 ? run : Promise.race([
|
|
349
|
+
run,
|
|
350
|
+
new Promise((_, reject) => {
|
|
351
|
+
timer = setTimeout(() => {
|
|
352
|
+
reject(new Error(
|
|
353
|
+
`[fulgurjs] \u8FDC\u7A0B\u7EC4\u4EF6 "${spec}" \u52A0\u8F7D\u7B49\u5F85\u8D85\u8FC7 ${opts.timeout} \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\u91CD\u8BD5\u5EFA\u7ACB\u65B0\u7684\u52A0\u8F7D\u5C1D\u8BD5\u3002`
|
|
354
|
+
));
|
|
355
|
+
}, opts.timeout);
|
|
356
|
+
})
|
|
357
|
+
]);
|
|
358
|
+
race.then(
|
|
359
|
+
(ns) => {
|
|
360
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
361
|
+
const inner = ns?.default ?? ns;
|
|
362
|
+
if (!isValidComponentType(inner)) {
|
|
363
|
+
commit({ phase: "error", error: noRenderableExportError(spec, inner) });
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
commit({ phase: "ready", Comp: inner });
|
|
367
|
+
},
|
|
368
|
+
(err) => {
|
|
369
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
370
|
+
commit({ phase: "error", error: err });
|
|
371
|
+
}
|
|
372
|
+
);
|
|
373
|
+
return () => {
|
|
374
|
+
cancelled = true;
|
|
375
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
376
|
+
};
|
|
377
|
+
}, [attempt, sessionKey]);
|
|
378
|
+
if (state.phase === "pending") return opts.fallback ?? null;
|
|
379
|
+
if (state.phase === "error") return renderErrorNode(opts.error, state.error, retry, "load");
|
|
380
|
+
const Comp = state.Comp;
|
|
381
|
+
const merged = fwdRef === void 0 ? latest.current.passthrough : { ...latest.current.passthrough, ref: latest.current.fwdRef };
|
|
382
|
+
return (0, import_react.createElement)(Comp, merged);
|
|
383
|
+
}
|
|
384
|
+
var RemoteRenderBoundary = class extends import_react.Component {
|
|
385
|
+
state = { error: null };
|
|
386
|
+
static getDerivedStateFromError(error) {
|
|
387
|
+
return { error };
|
|
388
|
+
}
|
|
389
|
+
componentDidCatch(error, info) {
|
|
390
|
+
console.error("[fulgurjs] \u8FDC\u7A0B\u7EC4\u4EF6\u6E32\u67D3\u5F02\u5E38\uFF08remoteComponent \u5185\u7F6E\u8FB9\u754C\uFF09\uFF1A", error, info?.componentStack ?? "");
|
|
391
|
+
}
|
|
392
|
+
retry = () => {
|
|
393
|
+
this.props.onRetry();
|
|
394
|
+
};
|
|
395
|
+
render() {
|
|
396
|
+
return this.props.render(this.state.error, this.retry);
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
function buildRemoteComponent(spec, opts, loadRemote, beforeLoad) {
|
|
400
|
+
const displayName = cleanCompName(spec);
|
|
401
|
+
function RemoteRoot(props) {
|
|
402
|
+
const [attempt, setAttempt] = (0, import_react.useState)(0);
|
|
403
|
+
const retry = (0, import_react.useCallback)(() => setAttempt((a) => a + 1), []);
|
|
404
|
+
const { __fulgurjsRef, ...rest } = props;
|
|
405
|
+
const passthrough = rest;
|
|
406
|
+
const sessionKey = readSessionKey();
|
|
407
|
+
return (0, import_react.createElement)(
|
|
408
|
+
RemoteRenderBoundary,
|
|
409
|
+
{
|
|
410
|
+
key: attempt,
|
|
411
|
+
onRetry: retry,
|
|
412
|
+
render: (boundaryError) => boundaryError !== null ? renderErrorNode(opts.error, boundaryError, retry, "render") : (0, import_react.createElement)(RemoteLoader, {
|
|
413
|
+
spec,
|
|
414
|
+
opts,
|
|
415
|
+
loadRemote,
|
|
416
|
+
beforeLoad,
|
|
417
|
+
attempt,
|
|
418
|
+
sessionKey,
|
|
419
|
+
retry,
|
|
420
|
+
passthrough,
|
|
421
|
+
fwdRef: __fulgurjsRef
|
|
422
|
+
})
|
|
423
|
+
}
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
RemoteRoot.displayName = displayName;
|
|
427
|
+
const Wrapped = (0, import_react.forwardRef)(
|
|
428
|
+
(props, ref) => (0, import_react.createElement)(RemoteRoot, { ...props, __fulgurjsRef: ref })
|
|
429
|
+
);
|
|
430
|
+
Wrapped.displayName = displayName;
|
|
431
|
+
return Wrapped;
|
|
432
|
+
}
|
|
433
|
+
function createRemoteComponent(loadRemote) {
|
|
434
|
+
return function remoteComponent(spec, opts = {}) {
|
|
435
|
+
assertAdapterOptions(`remoteComponent("${spec}")`, opts);
|
|
436
|
+
return buildRemoteComponent(spec, opts, loadRemote);
|
|
437
|
+
};
|
|
438
|
+
}
|
|
439
|
+
function createUseLoadRemote(loadRemote) {
|
|
440
|
+
return function useLoadRemote(spec, opts = {}) {
|
|
441
|
+
assertAdapterOptions(`useLoadRemote("${spec}")`, opts);
|
|
442
|
+
const { shareScope, retries, fallbackModule } = opts;
|
|
443
|
+
const [data, setData] = (0, import_react.useState)(void 0);
|
|
444
|
+
const [error, setError] = (0, import_react.useState)(void 0);
|
|
445
|
+
const [loading, setLoading] = (0, import_react.useState)(true);
|
|
446
|
+
const reqGen = (0, import_react.useRef)(0);
|
|
447
|
+
const optsRef = (0, import_react.useRef)(opts);
|
|
448
|
+
optsRef.current = opts;
|
|
449
|
+
const sessionKey = readSessionKey();
|
|
450
|
+
(0, import_react.useEffect)(() => {
|
|
451
|
+
const gen = ++reqGen.current;
|
|
452
|
+
let cancelled = false;
|
|
453
|
+
const isCurrent = () => gen === reqGen.current && !cancelled;
|
|
454
|
+
setData(void 0);
|
|
455
|
+
setError(void 0);
|
|
456
|
+
setLoading(true);
|
|
457
|
+
loadRemote(spec, { shareScope, retries, fallbackModule }).then(
|
|
458
|
+
(ns) => {
|
|
459
|
+
if (!isCurrent()) return;
|
|
460
|
+
setData(ns);
|
|
461
|
+
setLoading(false);
|
|
462
|
+
},
|
|
463
|
+
(err) => {
|
|
464
|
+
if (!isCurrent()) return;
|
|
465
|
+
setError(err);
|
|
466
|
+
setLoading(false);
|
|
467
|
+
}
|
|
468
|
+
);
|
|
469
|
+
return () => {
|
|
470
|
+
cancelled = true;
|
|
471
|
+
};
|
|
472
|
+
}, [spec, shareScope, retries, fallbackModule, loadRemote, sessionKey]);
|
|
473
|
+
const reload = (0, import_react.useCallback)(async () => {
|
|
474
|
+
const gen = ++reqGen.current;
|
|
475
|
+
setLoading(true);
|
|
476
|
+
setError(void 0);
|
|
477
|
+
const o = optsRef.current;
|
|
478
|
+
try {
|
|
479
|
+
const ns = await loadRemote(spec, { shareScope: o.shareScope, retries: o.retries, fallbackModule: o.fallbackModule });
|
|
480
|
+
if (gen === reqGen.current) {
|
|
481
|
+
setData(ns);
|
|
482
|
+
setLoading(false);
|
|
483
|
+
}
|
|
484
|
+
} catch (err) {
|
|
485
|
+
if (gen === reqGen.current) {
|
|
486
|
+
setError(err);
|
|
487
|
+
setLoading(false);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
}, [spec, loadRemote]);
|
|
491
|
+
return { data, error, loading, reload };
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
function createReactHostPages(options, loadRemote) {
|
|
495
|
+
const { beforeLoad, fallback, error, retries, timeout } = options;
|
|
496
|
+
assertAdapterOptions("createReactHostPages()", { retries, timeout });
|
|
497
|
+
const core = createHostPagesCore(options);
|
|
498
|
+
let cacheSession;
|
|
499
|
+
const compCache = /* @__PURE__ */ new Map();
|
|
500
|
+
const component = (spec) => {
|
|
501
|
+
const sessionKey = readSessionKey();
|
|
502
|
+
if (shouldResetSessionCache(sessionKey, cacheSession)) {
|
|
503
|
+
compCache.clear();
|
|
504
|
+
cacheSession = sessionKey;
|
|
505
|
+
}
|
|
506
|
+
let comp = compCache.get(spec);
|
|
507
|
+
if (!comp) {
|
|
508
|
+
comp = buildRemoteComponent(spec, { fallback, error, retries, timeout }, loadRemote, beforeLoad);
|
|
509
|
+
compCache.set(spec, comp);
|
|
510
|
+
}
|
|
511
|
+
return comp;
|
|
512
|
+
};
|
|
513
|
+
return { pages: core.pages, resolve: core.resolve, component };
|
|
514
|
+
}
|
|
515
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
516
|
+
0 && (module.exports = {
|
|
517
|
+
RemoteErrorBoundary,
|
|
518
|
+
createReactHostPages,
|
|
519
|
+
createRemoteComponent,
|
|
520
|
+
createUseLoadRemote
|
|
521
|
+
});
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { ComponentType, ReactNode, Component, ErrorInfo, Ref } from 'react';
|
|
2
|
+
import { PageRouteLike } from './pages.cjs';
|
|
3
|
+
import { R as ResolvedHostPage, H as HostPagesCoreOptions } from './host-pages-core-BFxp-IOj.cjs';
|
|
4
|
+
|
|
5
|
+
interface LoadRemoteOptions {
|
|
6
|
+
shareScope?: string;
|
|
7
|
+
retries?: number;
|
|
8
|
+
fallbackModule?: () => any;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* React 适配层:remoteComponent() / useLoadRemote() / RemoteErrorBoundary /
|
|
13
|
+
* createReactHostPages()。只接收加载函数,不静态引用运行时内核(同 vue-adapter 形态,
|
|
14
|
+
* dist/react.js 由构建脚本绑定 loadRemote 后再导出)。
|
|
15
|
+
*
|
|
16
|
+
* 语义要点(README React 章节的实现依据):
|
|
17
|
+
* - remoteComponent:工厂与页面表声明零加载副作用;首次渲染才 loadRemote;
|
|
18
|
+
* 加载状态机自带 pending/error/ready 三态,不用 React.lazy——lazy 的失败 Promise
|
|
19
|
+
* 会缓存在实例上,仅重置错误边界无法恢复,必须重建加载尝试。retry 以受控 attempt
|
|
20
|
+
* 计数触发(不在普通 render 中重建加载器,组件本地状态不因重渲染反复重置)。
|
|
21
|
+
* - timeout 是本次组件加载等待上限(适配层实现),不取消 loadRemote 的共享请求,
|
|
22
|
+
* 不修改远程入口配置超时;迟到的成功/失败一律丢弃,不产生未处理 rejection。
|
|
23
|
+
* retry 对已成功缓存的模块不重新下载(运行时缓存),只重走生命周期(onSession 按代次去重)。
|
|
24
|
+
* - 渲染期异常由内置 RemoteRenderBoundary 捕获,与网络/导出错误分开记录与展示;
|
|
25
|
+
* ErrorBoundary 不捕获事件处理器与任意异步回调(React 边界语义,README 已声明)。
|
|
26
|
+
* - 代码全部 createElement(无 JSX):适配器自身不依赖 jsx-runtime,减少共享子路径面。
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
type LoadRemoteFn = (spec: string, opts?: LoadRemoteOptions) => Promise<any>;
|
|
30
|
+
/** 渲染函数形态的错误占位:(error, retry) => ReactNode;与 remoteComponent.error 一致 */
|
|
31
|
+
type RemoteErrorFallback = (error: unknown, retry: () => void) => ReactNode;
|
|
32
|
+
interface ReactRemoteComponentOptions {
|
|
33
|
+
/** 本次加载 pending 时的占位(区别于失败占位);默认 null */
|
|
34
|
+
fallback?: ReactNode;
|
|
35
|
+
/** 加载失败或子树渲染错误的展示;默认内置中文占位(错误码+根因+修法+重试) */
|
|
36
|
+
error?: ReactNode | RemoteErrorFallback;
|
|
37
|
+
/** 透传现行 loadRemote 的重试选项(0-10 整数),不另叠自动重试 */
|
|
38
|
+
retries?: number;
|
|
39
|
+
/** 本次组件加载等待上限 ms;不提供则沿用 loadRemote 自身超时。不取消已发出的共享请求 */
|
|
40
|
+
timeout?: number;
|
|
41
|
+
}
|
|
42
|
+
interface RemoteErrorBoundaryProps {
|
|
43
|
+
children?: ReactNode;
|
|
44
|
+
/** 错误占位:节点或 ({ error, reset }) 渲染函数;默认内置中文占位 */
|
|
45
|
+
fallback?: ReactNode | ((args: {
|
|
46
|
+
error: unknown;
|
|
47
|
+
reset: () => void;
|
|
48
|
+
}) => ReactNode);
|
|
49
|
+
/** 渲染异常回调(error + React componentStack 信息) */
|
|
50
|
+
onError?: (error: unknown, info: ErrorInfo) => void;
|
|
51
|
+
/** 任一项变化时重置边界状态(受控重试的常用形态:传 [retryEpoch]) */
|
|
52
|
+
resetKeys?: readonly unknown[];
|
|
53
|
+
}
|
|
54
|
+
interface BoundaryState {
|
|
55
|
+
error: unknown | null;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* 页面级错误边界:只捕获传到它的子树渲染错误。remoteComponent 内置边界已处理自身错误,
|
|
59
|
+
* 外层边界看不到内层已消费的异常。reset 只重置边界状态;子组件若持有失败缓存
|
|
60
|
+
* (如 React.lazy),还需要由调用方重建加载尝试(remoteComponent 的 retry 已含两者)。
|
|
61
|
+
*/
|
|
62
|
+
declare class RemoteErrorBoundary extends Component<RemoteErrorBoundaryProps, BoundaryState> {
|
|
63
|
+
state: BoundaryState;
|
|
64
|
+
static getDerivedStateFromError(error: unknown): BoundaryState;
|
|
65
|
+
componentDidCatch(error: unknown, info: ErrorInfo): void;
|
|
66
|
+
componentDidUpdate(prev: RemoteErrorBoundaryProps): void;
|
|
67
|
+
readonly reset: () => void;
|
|
68
|
+
render(): ReactNode;
|
|
69
|
+
}
|
|
70
|
+
declare function createRemoteComponent(loadRemote: LoadRemoteFn): <P = Record<string, unknown>>(spec: string, opts?: ReactRemoteComponentOptions) => ComponentType<P & {
|
|
71
|
+
ref?: Ref<unknown>;
|
|
72
|
+
}>;
|
|
73
|
+
interface UseLoadRemoteOptions {
|
|
74
|
+
/** 透传 loadRemote 的 shareScope */
|
|
75
|
+
shareScope?: string;
|
|
76
|
+
/** 透传 loadRemote 的重试次数(0-10 整数) */
|
|
77
|
+
retries?: number;
|
|
78
|
+
/**
|
|
79
|
+
* 用户显式配置的失败兜底模块(透传 loadRemote.fallbackModule):配置后加载失败返回
|
|
80
|
+
* 兜底值而不写 error。这是显式声明的行为,不是静默成功——README 已说明。
|
|
81
|
+
*/
|
|
82
|
+
fallbackModule?: () => any;
|
|
83
|
+
}
|
|
84
|
+
interface UseLoadRemoteResult<Module = any> {
|
|
85
|
+
/** 加载成功后的模块命名空间;未成功时 undefined */
|
|
86
|
+
data: Module | undefined;
|
|
87
|
+
/** 失败原因;无错误时恒为 undefined(统一空值) */
|
|
88
|
+
error: unknown;
|
|
89
|
+
/** 是否有请求在途 */
|
|
90
|
+
loading: boolean;
|
|
91
|
+
/** 重新走一次加载生命周期;已成功的模块经运行时缓存不会重新下载。Promise<void> 正常结束(不抛) */
|
|
92
|
+
reload: () => Promise<void>;
|
|
93
|
+
}
|
|
94
|
+
declare function createUseLoadRemote(loadRemote: LoadRemoteFn): <Module = any>(spec: string, opts?: UseLoadRemoteOptions) => UseLoadRemoteResult<Module>;
|
|
95
|
+
interface ReactHostPagesOptions extends HostPagesCoreOptions {
|
|
96
|
+
/** 每次页面模块实际加载尝试前执行(同步或异步);宿主在此提供最新 context */
|
|
97
|
+
beforeLoad?: () => void | Promise<void>;
|
|
98
|
+
/** 页面加载 pending 占位(同 remoteComponent.fallback) */
|
|
99
|
+
fallback?: ReactNode;
|
|
100
|
+
/** 页面加载/渲染错误占位(同 remoteComponent.error) */
|
|
101
|
+
error?: ReactNode | RemoteErrorFallback;
|
|
102
|
+
/** 透传 loadRemote 重试(0-10 整数) */
|
|
103
|
+
retries?: number;
|
|
104
|
+
/** 单次页面组件加载等待上限 ms(同 remoteComponent.timeout) */
|
|
105
|
+
timeout?: number;
|
|
106
|
+
}
|
|
107
|
+
interface ReactHostPages {
|
|
108
|
+
/** 原页面记录 */
|
|
109
|
+
pages: PageRouteLike[];
|
|
110
|
+
/** 路径 → 页面解析(base 前缀、最长前缀、参数解码、R1–R5 语义与 Vue 完全一致) */
|
|
111
|
+
resolve(path: string): ResolvedHostPage | null;
|
|
112
|
+
/** 按 spec 取页面组件(同 spec 同登录代次复用;新非空 sessionKey 到来时重建以触发 onSession) */
|
|
113
|
+
component<P = Record<string, unknown>>(spec: string): ComponentType<P>;
|
|
114
|
+
}
|
|
115
|
+
declare function createReactHostPages(options: ReactHostPagesOptions, loadRemote: LoadRemoteFn): ReactHostPages;
|
|
116
|
+
|
|
117
|
+
export { type ReactHostPages, type ReactHostPagesOptions, type ReactRemoteComponentOptions, RemoteErrorBoundary, type RemoteErrorBoundaryProps, type RemoteErrorFallback, ResolvedHostPage, type UseLoadRemoteOptions, type UseLoadRemoteResult, createReactHostPages, createRemoteComponent, createUseLoadRemote };
|