@axewo/fin-micro 0.0.0-stage → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/README.md +223 -2
  2. package/cli/index.mjs +240 -0
  3. package/dist/browser.cjs +1 -0
  4. package/dist/browser.mjs +169 -0
  5. package/dist/core.cjs +1 -0
  6. package/dist/core.mjs +654 -0
  7. package/dist/iframe.cjs +1 -0
  8. package/dist/iframe.mjs +146 -0
  9. package/dist/index.cjs +1 -0
  10. package/dist/index.mjs +35 -0
  11. package/dist/router-view-impl-B1NzCGKV.mjs +76 -0
  12. package/dist/router-view-impl-BvOToV_Z.cjs +1 -0
  13. package/dist/runtime.cjs +1 -0
  14. package/dist/runtime.mjs +149 -0
  15. package/dist/sdk.cjs +1 -0
  16. package/dist/sdk.mjs +118 -0
  17. package/dist/types/browser/index.d.ts +46 -0
  18. package/dist/types/core/index.d.ts +63 -0
  19. package/dist/types/iframe/index.d.ts +77 -0
  20. package/dist/types/index.d.ts +8 -0
  21. package/dist/types/runtime/index.d.ts +21 -0
  22. package/dist/types/runtime/lifecycle.d.ts +23 -0
  23. package/dist/types/sdk/index.d.ts +397 -0
  24. package/dist/types/vue/index.d.ts +102 -0
  25. package/dist/types/vue/router-view-impl.d.ts +41 -0
  26. package/dist/types/vue/router-view.d.ts +42 -0
  27. package/dist/types/vue/tab-context.d.ts +6 -0
  28. package/dist/vue.cjs +1 -0
  29. package/dist/vue.mjs +180 -0
  30. package/package.json +91 -3
  31. package/scaffold/base/index.html +12 -0
  32. package/scaffold/base/package.json.template +33 -0
  33. package/scaffold/base/scripts/check-runtime.mjs +43 -0
  34. package/scaffold/base/src/components/.gitkeep +1 -0
  35. package/scaffold/base/src/components/AppProviders.vue +25 -0
  36. package/scaffold/base/src/hooks/.gitkeep +1 -0
  37. package/scaffold/base/src/layout/.gitkeep +1 -0
  38. package/scaffold/base/src/locales/.gitkeep +1 -0
  39. package/scaffold/base/src/locales/en.ts +12 -0
  40. package/scaffold/base/src/locales/index.ts +13 -0
  41. package/scaffold/base/src/locales/zh-CN.ts +12 -0
  42. package/scaffold/base/src/main.ts +22 -0
  43. package/scaffold/base/src/router/.gitkeep +1 -0
  44. package/scaffold/base/src/router/demo.ts +6 -0
  45. package/scaffold/base/src/router/index.ts +13 -0
  46. package/scaffold/base/src/stores/.gitkeep +1 -0
  47. package/scaffold/base/src/stores/demo.ts +12 -0
  48. package/scaffold/base/src/stores/index.ts +11 -0
  49. package/scaffold/base/src/styles/index.css +18 -0
  50. package/scaffold/base/src/types/.gitkeep +1 -0
  51. package/scaffold/base/src/utils/.gitkeep +1 -0
  52. package/scaffold/base/src/utils/appConfig.ts +49 -0
  53. package/scaffold/base/src/utils/errorUtils.ts +92 -0
  54. package/scaffold/base/src/utils/requestUtils.ts +144 -0
  55. package/scaffold/base/src/views/.gitkeep +1 -0
  56. package/scaffold/base/src/views/DemoView.vue +26 -0
  57. package/scaffold/base/tsconfig.json +16 -0
  58. package/scaffold/base/vite.config.ts +8 -0
  59. package/scaffold/host/.env.example +13 -0
  60. package/scaffold/host/src/App.vue +4 -0
  61. package/scaffold/host/src/bootstrap.ts +50 -0
  62. package/scaffold/host/src/hooks/usePlatformSession.ts +91 -0
  63. package/scaffold/host/src/layout/HostHeader.vue +69 -0
  64. package/scaffold/host/src/layout/HostLayout.vue +168 -0
  65. package/scaffold/host/src/layout/HostMenu.vue +15 -0
  66. package/scaffold/host/src/layout/HostTabs.vue +47 -0
  67. package/scaffold/host/src/layout/HostWorkspace.vue +26 -0
  68. package/scaffold/host/src/localPages.ts +45 -0
  69. package/scaffold/host/src/platform.ts +239 -0
  70. package/scaffold/host/src/router/guards.ts +128 -0
  71. package/scaffold/host/src/router/index.ts +25 -0
  72. package/scaffold/host/src/router/targets.ts +30 -0
  73. package/scaffold/host/src/router/workspace.ts +63 -0
  74. package/scaffold/host/src/styles/shell.css +761 -0
  75. package/scaffold/host/src/types/platform.ts +12 -0
  76. package/scaffold/host/src/utils/request.ts +42 -0
  77. package/scaffold/host/src/views/HomeView.vue +19 -0
  78. package/scaffold/host/src/views/LoginRoute.vue +16 -0
  79. package/scaffold/host/src/views/LoginView.vue +88 -0
  80. package/scaffold/host/src/views/NotFoundView.vue +1 -0
  81. package/scaffold/host/src/views/ProfileView.vue +24 -0
  82. package/scaffold/host/src/views/WorkspaceRoute.vue +11 -0
  83. package/scaffold/vue/src/App.vue +24 -0
  84. package/scaffold/vue/src/bootstrap.ts +67 -0
  85. package/scaffold/vue/src/bridge/auth.ts +23 -0
  86. package/scaffold/vue/src/bridge/session.ts +16 -0
  87. package/scaffold/vue/src/main.ts +13 -0
  88. package/scaffold/vue/src/router/index.ts +16 -0
  89. package/scaffold/vue/src/views/DetailView.vue +57 -0
  90. package/scaffold/vue/src/views/ListView.vue +55 -0
  91. package/scaffold/vue/src/views/NotFoundView.vue +1 -0
package/dist/core.mjs ADDED
@@ -0,0 +1,654 @@
1
+ import { createNotificationBus as We, NavigationError as p, HostCallError as N } from "./sdk.mjs";
2
+ function qe(Re, x = {}) {
3
+ var Ae;
4
+ const Z = /* @__PURE__ */ new Map(), pe = /* @__PURE__ */ new Map(), z = (e) => typeof e == "string" && !!e.trim() && !/[\u0000-\u001f\u007f]/.test(e), de = (e) => z(e) && e.startsWith("/") && !e.startsWith("//") && !e.includes("\\"), we = (e) => e.split(/[?#]/, 1)[0];
5
+ for (const e of Re) {
6
+ let s = function(r) {
7
+ if (!Array.isArray(r)) throw new Error("menus 必须为数组");
8
+ return Object.freeze(
9
+ r.map((i) => {
10
+ if (!i || !z(i.menuId) || !z(i.name) || c.has(i.menuId))
11
+ throw new Error("菜单标识/名称无效或菜单标识重复");
12
+ if (c.add(i.menuId), i.children !== void 0) {
13
+ if (i.target !== void 0 || !Array.isArray(i.children) || !i.children.length)
14
+ throw new Error("菜单分组需要非空 children 且不能配置 target");
15
+ return Object.freeze({
16
+ menuId: i.menuId,
17
+ name: i.name,
18
+ children: s(i.children)
19
+ });
20
+ }
21
+ if (!i.target || !z(i.target.tabId) || !de(i.target.route))
22
+ throw new Error("菜单叶节点需要有效 target");
23
+ return h.add(i.menuId), b.push({ pathPrefix: we(i.target.route), menuId: i.menuId }), Object.freeze({
24
+ menuId: i.menuId,
25
+ name: i.name,
26
+ target: Object.freeze({ tabId: i.target.tabId, route: i.target.route })
27
+ });
28
+ })
29
+ );
30
+ };
31
+ if (Z.has(e.appId)) throw new Error(`重复的 appId: ${e.appId}`);
32
+ if (e.kind === "local") {
33
+ if (e.entry !== void 0) throw new Error("本地应用不能配置 entry");
34
+ } else {
35
+ if (e.kind !== void 0 && e.kind !== "remote") throw new Error("未知应用类型");
36
+ const r = new URL(e.entry);
37
+ if (!["http:", "https:"].includes(r.protocol)) throw new Error("子应用地址必须使用 HTTP(S)");
38
+ }
39
+ const c = /* @__PURE__ */ new Set(), h = /* @__PURE__ */ new Set(), b = [], u = e.menus === void 0 ? void 0 : s(e.menus);
40
+ if (e.menuRoutes !== void 0 && !Array.isArray(e.menuRoutes))
41
+ throw new Error("menuRoutes 必须为数组");
42
+ const o = (Ae = e.menuRoutes) == null ? void 0 : Ae.map((r) => {
43
+ if (!r || !de(r.pathPrefix) || /[?#]/.test(r.pathPrefix) || !h.has(r.menuId))
44
+ throw new Error("菜单归属规则需要有效路径与本应用可导航叶节点");
45
+ return Object.freeze({ pathPrefix: r.pathPrefix, menuId: r.menuId });
46
+ });
47
+ b.push(...(o ?? []).map((r) => ({ ...r })));
48
+ const t = /* @__PURE__ */ new Map();
49
+ for (const r of b) {
50
+ if (r.pathPrefix = r.pathPrefix.replace(/\/+$/, "") || "/", t.has(r.pathPrefix) && t.get(r.pathPrefix) !== r.menuId)
51
+ throw new Error("菜单归属前缀冲突");
52
+ t.set(r.pathPrefix, r.menuId);
53
+ }
54
+ pe.set(e.appId, {
55
+ leaves: h,
56
+ routes: b.sort((r, i) => i.pathPrefix.length - r.pathPrefix.length)
57
+ }), Z.set(
58
+ e.appId,
59
+ Object.freeze({
60
+ appId: e.appId,
61
+ name: e.name,
62
+ ...e.kind === "local" ? { kind: "local" } : { ...e.kind ? { kind: e.kind } : {}, entry: e.entry },
63
+ ...u ? { menus: u } : {},
64
+ ...o ? { menuRoutes: Object.freeze(o) } : {}
65
+ })
66
+ );
67
+ }
68
+ const ae = We(), xe = Object.freeze({ source: "host" }), le = {
69
+ on(e, s) {
70
+ return ae.on(e, ({ payload: c, metadata: h }) => q(() => s(c, h)));
71
+ },
72
+ emit(e, s) {
73
+ ae.emit(e, { payload: s, metadata: xe });
74
+ },
75
+ clear: () => ae.clear()
76
+ }, ie = x.callTimeoutMs ?? 3e4;
77
+ if (!Number.isFinite(ie) || ie <= 0 || ie > 2147483647)
78
+ throw new Error("callTimeoutMs 必须为不超过2147483647的正有限数");
79
+ const K = /* @__PURE__ */ new Set();
80
+ function ke(e, s) {
81
+ if (!e || typeof e.authenticated != "boolean" || !Array.isArray(e.permissions) || !e.permissions.every((c) => typeof c == "string") || e.user !== void 0 && (!e.user || typeof e.user.id != "string" || e.user.name !== void 0 && typeof e.user.name != "string"))
82
+ throw new Error("会话快照无效");
83
+ return Object.freeze({
84
+ authenticated: e.authenticated,
85
+ permissions: Object.freeze([...e.permissions]),
86
+ ...e.user ? { user: Object.freeze({ id: e.user.id, ...e.user.name !== void 0 ? { name: e.user.name } : {} }) } : {},
87
+ revision: s
88
+ });
89
+ }
90
+ let ee = ke(x.session ?? { authenticated: !1, permissions: [] }, 0);
91
+ function ze(e) {
92
+ j(), ee = ke(e, ee.revision + 1);
93
+ const s = ee;
94
+ for (const c of [...K]) {
95
+ if (s !== ee) break;
96
+ K.has(c) && q(() => c(s));
97
+ }
98
+ return s;
99
+ }
100
+ const te = /* @__PURE__ */ new Set(), re = /* @__PURE__ */ new Map(), ve = new Map(
101
+ Object.entries(x.capabilities ?? {}).filter(([, e]) => e !== void 0).map(([e, s]) => {
102
+ const c = s;
103
+ return [
104
+ e,
105
+ { handler: c.handler, apps: c.apps ? new Set(c.apps) : void 0 }
106
+ ];
107
+ })
108
+ ), B = /* @__PURE__ */ new Map(), G = /* @__PURE__ */ new Map();
109
+ let d = { tabs: [], activeTabId: null }, be = 0, D = !1, W = 0, Ee = Promise.resolve();
110
+ const oe = /* @__PURE__ */ new Map(), U = /* @__PURE__ */ new Map(), _e = (e) => {
111
+ var s;
112
+ try {
113
+ (s = x.onError) == null || s.call(x, e);
114
+ } catch {
115
+ }
116
+ }, q = (e) => {
117
+ try {
118
+ e();
119
+ } catch (s) {
120
+ _e(s);
121
+ }
122
+ }, j = () => {
123
+ if (D) throw new Error("MicroHost 已销毁");
124
+ }, ce = () => ({ tabs: [...d.tabs], activeTabId: d.activeTabId }), ne = () => {
125
+ W++;
126
+ for (const e of [...te])
127
+ te.has(e) && q(() => e(ce()));
128
+ };
129
+ function me(e) {
130
+ return j(), te.add(e), () => {
131
+ te.delete(e);
132
+ };
133
+ }
134
+ const ue = x.canNavigate, ge = x.canOpen;
135
+ function Y(e) {
136
+ if (!ge) return;
137
+ const s = Object.freeze({
138
+ appId: e.appId,
139
+ tabId: e.tabId,
140
+ title: e.title,
141
+ route: e.route,
142
+ ...e.activeMenuId !== void 0 ? { activeMenuId: e.activeMenuId } : {}
143
+ });
144
+ try {
145
+ const c = ge(s);
146
+ if (c === !0) return;
147
+ c && (typeof c == "object" || typeof c == "function") && Promise.resolve(c).catch(() => {
148
+ });
149
+ } catch {
150
+ }
151
+ throw new p("FORBIDDEN", "无权访问该页面(权限策略必须同步返回 true)");
152
+ }
153
+ function he(e, s = !1) {
154
+ var o;
155
+ if (j(), !e || !z(e.title) || e.title.length > 500)
156
+ throw new Error("页签标题无效");
157
+ if (!Z.has(e.appId)) throw new Error(`未知子应用: ${e.appId}`);
158
+ if (!z(e.tabId) || !de(e.route))
159
+ throw new Error("需要 tabId 与应用内绝对路由");
160
+ const c = pe.get(e.appId);
161
+ if (e.activeMenuId !== void 0 && !c.leaves.has(e.activeMenuId))
162
+ throw new Error("activeMenuId 必须指向本应用可导航叶节点");
163
+ const h = we(e.route), b = e.activeMenuId ?? ((o = c.routes.find(
164
+ (t) => t.pathPrefix === "/" || h === t.pathPrefix || h.startsWith(`${t.pathPrefix}/`)
165
+ )) == null ? void 0 : o.menuId);
166
+ let u = d.tabs.find((t) => t.tabId === e.tabId);
167
+ if (u && (u.appId !== e.appId || u.route !== e.route && !s))
168
+ throw new Error("tabId 已绑定到其他页面");
169
+ return u && u.route !== e.route && (u = void 0), Y(u ?? { ...e, activeMenuId: b }), u || (u = Object.freeze({
170
+ appId: e.appId,
171
+ tabId: e.tabId,
172
+ title: e.title,
173
+ route: e.route,
174
+ ...b !== void 0 ? { activeMenuId: b } : {},
175
+ key: `${e.tabId}:${++be}`
176
+ })), u;
177
+ }
178
+ function He(e) {
179
+ const s = he(e);
180
+ return d = {
181
+ tabs: d.tabs.includes(s) ? d.tabs : [...d.tabs, s],
182
+ activeTabId: s.tabId
183
+ }, ne(), s;
184
+ }
185
+ function Fe(e, s, c) {
186
+ if (j(), typeof s.title != "string" || !s.title.trim() || s.title.length > 500 || /[\u0000-\u001f\u007f]/.test(s.title))
187
+ throw new Error("页签标题无效");
188
+ const h = d.tabs.find(
189
+ (u) => u.tabId === e && (!c || u.key === c)
190
+ );
191
+ if (!h) return;
192
+ const b = Object.freeze({ ...h, title: s.title });
193
+ return d = { ...d, tabs: d.tabs.map((u) => u === h ? b : u) }, ne(), b;
194
+ }
195
+ function $e(e) {
196
+ j();
197
+ const s = d.tabs.find((c) => c.tabId === e);
198
+ return s ? (Y(s), d = { ...d, activeTabId: e }, ne(), !0) : !1;
199
+ }
200
+ function Ce(e, s) {
201
+ var u;
202
+ j();
203
+ const c = d.tabs.findIndex(
204
+ (o) => o.tabId === e && (!s || o.key === s)
205
+ );
206
+ if (c < 0) return !1;
207
+ const h = d.tabs[c], b = d.tabs.filter((o) => o !== h);
208
+ if (d = {
209
+ tabs: b,
210
+ activeTabId: d.activeTabId === e ? ((u = b[Math.min(c, b.length - 1)]) == null ? void 0 : u.tabId) ?? null : d.activeTabId
211
+ }, Oe(h.key), !b.some((o) => o.appId === h.appId)) {
212
+ for (const o of [...G.get(h.appId) ?? []]) q(o);
213
+ G.delete(h.appId), re.delete(h.appId);
214
+ }
215
+ return ne(), !0;
216
+ }
217
+ function Oe(e) {
218
+ oe.delete(e), U.delete(e);
219
+ for (const s of [...B.get(e) ?? []]) q(s);
220
+ B.delete(e);
221
+ }
222
+ function Ve(e) {
223
+ return J(e);
224
+ }
225
+ function J(e, s, c, h = !1, b, u = !1) {
226
+ var o;
227
+ try {
228
+ if (D) throw new p("HOST_DISPOSED", "MicroHost 已销毁");
229
+ if (!e || !["open", "activate", "refresh", "close", "closeMany", "back"].includes(e.type))
230
+ throw new p("INVALID_OPERATION", "导航命令无效");
231
+ const t = {
232
+ ...e,
233
+ ..."target" in e && e.target ? { target: { ...e.target } } : {},
234
+ ..."anchor" in e && e.anchor ? { anchor: { ...e.anchor } } : {}
235
+ }, r = (n) => !!n && z(n.tabId) && (n.key === void 0 || z(n.key));
236
+ if (["activate", "refresh", "close"].includes(t.type) && (!("target" in t) || !r(t.target)))
237
+ throw new p("INVALID_OPERATION", "导航需要合法目标引用");
238
+ if ("target" in t && t.target !== void 0 && !r(t.target) || "anchor" in t && t.anchor !== void 0 && !r(t.anchor))
239
+ throw new p("INVALID_OPERATION", "导航引用无效");
240
+ if (t.type === "closeMany" && t.exclude !== void 0) {
241
+ if (!Array.isArray(t.exclude) || !t.exclude.every(r))
242
+ throw new p("INVALID_OPERATION", "批量排除引用无效");
243
+ t.exclude = Object.freeze(
244
+ t.exclude.map(
245
+ (n) => {
246
+ var g;
247
+ return Object.freeze({
248
+ tabId: n.tabId,
249
+ ...n.key !== void 0 ? { key: n.key } : { key: (g = d.tabs.find((k) => k.tabId === n.tabId)) == null ? void 0 : g.key }
250
+ });
251
+ }
252
+ )
253
+ );
254
+ }
255
+ if (t.type === "open") {
256
+ const n = t.input;
257
+ if (!n) throw new p("INVALID_OPERATION", "导航目标无效");
258
+ t.input = Object.freeze({
259
+ appId: n.appId,
260
+ tabId: n.tabId,
261
+ title: n.title,
262
+ route: n.route,
263
+ ...n.activeMenuId !== void 0 ? { activeMenuId: n.activeMenuId } : {}
264
+ });
265
+ }
266
+ if ("closeFallback" in t && t.closeFallback !== void 0) {
267
+ if (!r(t.closeFallback)) throw new p("INVALID_OPERATION", "关闭回落引用无效");
268
+ const n = t.closeFallback;
269
+ t.closeFallback = Object.freeze({ tabId: n.tabId, key: n.key ?? ((o = d.tabs.find((g) => g.tabId === n.tabId)) == null ? void 0 : o.key) });
270
+ }
271
+ "fallback" in t && t.fallback && (t.fallback = Object.freeze({ ...t.fallback })), "target" in t && t.target && Object.freeze(t.target), "anchor" in t && t.anchor && Object.freeze(t.anchor), Object.freeze(t);
272
+ const i = (n) => d.tabs.find(
273
+ (g) => n ? g.tabId === n.tabId && (!n.key || g.key === n.key) : g.tabId === d.activeTabId
274
+ ), w = "target" in t ? i(t.target) : void 0, v = t.type === "closeMany" && t.scope !== "all" ? t.anchor ? i(t.anchor) : c ? d.tabs.find((n) => n.key === c) : i() : void 0, S = i(), f = t.type === "open" ? d.tabs.find((n) => n.tabId === t.input.tabId) : void 0, I = u && c ? d.tabs.find((n) => n.key === c) : void 0, E = I && s ? Object.freeze({ appId: s, tab: Object.freeze({ ...I }) }) : void 0, _ = u && t.type === "open" && !!s && t.input.appId !== s, L = () => {
275
+ if (!(!_ || t.type !== "open")) {
276
+ try {
277
+ const n = E && (ue == null ? void 0 : ue(E, t.input));
278
+ if (n === !0) return;
279
+ n && (typeof n == "object" || typeof n == "function") && Promise.resolve(n).catch(() => {
280
+ });
281
+ } catch {
282
+ }
283
+ throw new p("FORBIDDEN", "无权跨应用导航(权限策略必须同步返回 true)");
284
+ }
285
+ }, H = (n) => !s || n.appId === s;
286
+ if (w && !H(w) || v && !H(v) || t.type === "open" && s && t.input.appId !== s && !_ || t.type === "back" && t.fallback && s && t.fallback.appId !== s)
287
+ throw new p("FORBIDDEN", "页面只能操作本应用");
288
+ if (t.type === "closeMany" && !["all", "others", "left", "right"].includes(t.scope))
289
+ throw new p("INVALID_OPERATION", "批量范围无效");
290
+ const F = v ? d.tabs.indexOf(v) : -1, Q = t.type === "closeMany" ? d.tabs.filter(
291
+ (n, g) => {
292
+ var k;
293
+ return H(n) && !((k = t.exclude) != null && k.some((C) => C.tabId === n.tabId && C.key === n.key)) && (t.scope === "all" || F >= 0 && (t.scope === "others" ? n.key !== v.key : t.scope === "left" ? g < F : g > F));
294
+ }
295
+ ) : [], m = t.type === "back" ? w ?? (!("target" in t) || !t.target ? S : void 0) : w, $ = async () => {
296
+ var Te, Pe, De, Me, je, Se;
297
+ const n = (a) => ({
298
+ status: a,
299
+ affected: []
300
+ });
301
+ if (D) throw new p("HOST_DISPOSED", "MicroHost 已销毁");
302
+ if ((Te = t.signal) != null && Te.aborted) return n("stale");
303
+ if (c && !h && (b != null && b() || !d.tabs.some((a) => a.key === c)))
304
+ throw new p("PAGE_RELEASED", "页面上下文已释放");
305
+ if (t.type === "open" && (f ? !d.tabs.some((a) => a.key === f.key) : d.tabs.some((a) => a.tabId === t.input.tabId)) || m && !d.tabs.some((a) => a.key === m.key) || v && !d.tabs.some((a) => a.key === v.key) || Q.some((a) => !d.tabs.some((O) => O.key === a.key)))
306
+ return n("stale");
307
+ if (t.type !== "open" && t.type !== "closeMany" && !m) return n("noop");
308
+ const g = W;
309
+ if (L(), c && !h && (b != null && b() || !d.tabs.some((a) => a.key === c)))
310
+ throw new p("PAGE_RELEASED", "页面上下文已释放");
311
+ if (W !== g || D || (Pe = t.signal) != null && Pe.aborted) return n("stale");
312
+ const k = d, C = W;
313
+ let y = [...d.tabs], l, M = [], V = d.activeTabId;
314
+ try {
315
+ if (t.type === "open" && (l = he(t.input, !0), f && f.key !== l.key ? (M = [f], y = y.map((a) => a.key === f.key ? l : a)) : y.some((a) => a.key === l.key) || y.push(l), V = l.tabId), t.type === "activate" && (l = m, Y(l), V = l.tabId), t.type === "refresh" && (Y(m), M = [m], l = Object.freeze({
316
+ ...m,
317
+ key: `${m.tabId}:${++be}`
318
+ }), y = y.map((a) => a.key === m.key ? l : a)), (t.type === "close" || t.type === "back") && (M = [m]), t.type === "closeMany" && (M = Q), M.length && t.type !== "refresh" && t.type !== "open") {
319
+ const a = new Set(M.map((A) => A.key)), O = k.tabs.findIndex((A) => A.tabId === k.activeTabId);
320
+ if (y = y.filter((A) => !a.has(A.key)), t.type === "back") {
321
+ const A = U.get(m.key);
322
+ l = y.find((P) => P.key === A && H(P)), !l && t.fallback && (l = he(t.fallback), a.has(l.key) && (l = Object.freeze({ ...l, key: `${l.tabId}:${++be}` })), y.some((P) => P.key === l.key) || y.push(l));
323
+ const T = k.tabs.filter((P) => H(P)), R = T.findIndex((P) => P.key === m.key);
324
+ l ?? (l = T.slice(0, R).reverse().find((P) => y.some((se) => se.key === P.key)) ?? T.slice(R + 1).find((P) => y.some((se) => se.key === P.key))), l ?? (l = y[Math.min(O, y.length - 1)]), l && Y(l), V = (l == null ? void 0 : l.tabId) ?? null;
325
+ } else if (M.some((A) => A.tabId === V)) {
326
+ const A = "closeFallback" in t ? t.closeFallback : void 0, T = A ? y.find((R) => R.tabId === A.tabId && R.key === A.key && H(R)) : void 0;
327
+ T && (Y(T), l = T), V = ((De = T ?? y[Math.min(O, y.length - 1)]) == null ? void 0 : De.tabId) ?? null;
328
+ }
329
+ }
330
+ } catch (a) {
331
+ throw a instanceof p ? a : new p("INVALID_OPERATION", "导航参数无效");
332
+ }
333
+ if (y.length === k.tabs.length && y.every((a, O) => a.key === k.tabs[O].key) && V === k.activeTabId)
334
+ return n("noop");
335
+ const Ie = [...M], fe = k.tabs.find((a) => a.tabId === k.activeTabId);
336
+ fe && fe.tabId !== V && !Ie.some((a) => a.key === fe.key) && Ie.push(fe);
337
+ for (const a of Ie)
338
+ for (const O of [...oe.get(a.key) ?? []]) {
339
+ let A;
340
+ const T = t.signal;
341
+ let R = () => {
342
+ };
343
+ try {
344
+ const P = Promise.resolve(O({ operation: t, tab: a })), se = new Promise((Le) => {
345
+ if (!T) return;
346
+ const Ne = () => Le();
347
+ T.addEventListener("abort", Ne, { once: !0 }), R = () => T.removeEventListener("abort", Ne), T.aborted && Le();
348
+ });
349
+ A = await Promise.race([P, se]);
350
+ } catch {
351
+ if (T != null && T.aborted) return n("stale");
352
+ throw new p("GUARD_FAILED", "离页守卫执行失败");
353
+ } finally {
354
+ R();
355
+ }
356
+ if (W !== C || D || (Me = t.signal) != null && Me.aborted) return n("stale");
357
+ if (A === !1) return n("cancelled");
358
+ }
359
+ if (W !== C || D || (je = t.signal) != null && je.aborted) return n("stale");
360
+ if (b != null && b()) throw new p("PAGE_RELEASED", "页面上下文已释放");
361
+ if (L(), l && Y(l), b != null && b()) throw new p("PAGE_RELEASED", "页面上下文已释放");
362
+ if (W !== C || D || (Se = t.signal) != null && Se.aborted) return n("stale");
363
+ if (d = { tabs: y, activeTabId: V }, t.type === "open" && l && c && !_ && l.key !== c && !U.has(l.key) && U.set(l.key, c), t.type === "refresh" && l) {
364
+ const a = U.get(m.key);
365
+ a && U.set(l.key, a);
366
+ }
367
+ for (const a of M) Oe(a.key);
368
+ for (const a of new Set(M.map((O) => O.appId)))
369
+ if (!y.some((O) => O.appId === a)) {
370
+ for (const O of [...G.get(a) ?? []]) q(O);
371
+ G.delete(a), re.delete(a);
372
+ }
373
+ const ye = [
374
+ ...M,
375
+ ...y.filter((a) => !k.tabs.some((O) => O.key === a.key))
376
+ ];
377
+ return !ye.length && l && ye.push(l), ne(), {
378
+ status: "committed",
379
+ affected: ye.map((a) => ({ tabId: a.tabId, key: a.key })),
380
+ ...l ? { tab: l } : {}
381
+ };
382
+ }, X = Ee.then($);
383
+ return Ee = X.catch(() => {
384
+ }), X;
385
+ } catch (t) {
386
+ return Promise.reject(t);
387
+ }
388
+ }
389
+ function Be(e) {
390
+ if (j(), !Z.has(e)) throw new Error(`未知子应用: ${e}`);
391
+ const s = re.get(e);
392
+ if (s) return s;
393
+ let c = !0;
394
+ const h = /* @__PURE__ */ new Set([
395
+ () => {
396
+ c = !1;
397
+ }
398
+ ]);
399
+ G.set(e, h);
400
+ async function b(o, t, r, i = {}) {
401
+ const w = () => {
402
+ if (D) throw new N("HOST_DISPOSED", "MicroHost 已销毁");
403
+ if (r && (r.released() || !d.tabs.some((L) => L.key === r.key)))
404
+ throw new N("PAGE_RELEASED", "页面上下文已释放");
405
+ if (!c) throw new N("BRIDGE_RELEASED", "应用 bridge 已释放");
406
+ };
407
+ w();
408
+ const v = ve.get(o);
409
+ if (!v) throw new N("UNKNOWN_CAPABILITY", `未注册宿主能力: ${o}`);
410
+ if (v.apps && !v.apps.has(e))
411
+ throw new N("FORBIDDEN", `应用无权调用宿主能力: ${o}`);
412
+ const S = i.timeoutMs ?? ie;
413
+ if (!Number.isFinite(S) || S <= 0 || S > 2147483647)
414
+ throw new TypeError("timeoutMs 必须为不超过2147483647的正有限数");
415
+ const f = i.signal;
416
+ if (f != null && f.aborted) throw new N("CANCELLED", "宿主能力调用已取消");
417
+ const I = new AbortController(), E = r ? d.tabs.find((L) => L.key === r.key) : void 0, _ = Object.freeze({
418
+ appId: e,
419
+ signal: I.signal,
420
+ ...E ? { tab: Object.freeze({ ...E }) } : {},
421
+ isActive: () => !I.signal.aborted && !D && c && (!r || !r.released() && d.tabs.some((L) => L.key === r.key))
422
+ });
423
+ return new Promise((L, H) => {
424
+ let F = !1, Q;
425
+ const m = () => {
426
+ Q !== void 0 && clearTimeout(Q), f == null || f.removeEventListener("abort", X), h.delete(n), r == null || r.cleanups.delete(n);
427
+ }, $ = (k) => {
428
+ F || (F = !0, m(), H(k), I.abort());
429
+ }, X = () => $(new N("CANCELLED", "宿主能力调用已取消")), n = () => {
430
+ try {
431
+ w();
432
+ } catch (k) {
433
+ $(k);
434
+ }
435
+ };
436
+ if (h.add(n), r == null || r.cleanups.add(n), f == null || f.addEventListener("abort", X, { once: !0 }), Q = setTimeout(() => $(new N("TIMEOUT", "宿主能力调用超时")), S), f != null && f.aborted) {
437
+ X();
438
+ return;
439
+ }
440
+ let g;
441
+ try {
442
+ g = v.handler(_, ...t);
443
+ } catch {
444
+ $(new N("HANDLER_FAILED", `宿主能力执行失败: ${o}`));
445
+ return;
446
+ }
447
+ Promise.resolve(g).then((k) => {
448
+ if (!F) {
449
+ try {
450
+ w();
451
+ } catch (C) {
452
+ $(C);
453
+ return;
454
+ }
455
+ F = !0, m(), L(k);
456
+ }
457
+ }, () => $(new N("HANDLER_FAILED", `宿主能力执行失败: ${o}`)));
458
+ });
459
+ }
460
+ const u = {
461
+ appId: e,
462
+ getState: ce,
463
+ callHost(o, ...t) {
464
+ return b(o, t);
465
+ },
466
+ callHostWithOptions(o, t, ...r) {
467
+ return b(o, r, void 0, t);
468
+ },
469
+ protocol() {
470
+ if (j(), !c) throw new Error("应用 bridge 已释放");
471
+ return Object.freeze({
472
+ version: "1.0",
473
+ capabilities: Object.freeze([...ve].filter(([, o]) => !o.apps || o.apps.has(e)).map(([o]) => o)),
474
+ features: Object.freeze(["call-options", "session", "event-metadata", "navigate-to"])
475
+ });
476
+ },
477
+ negotiate(o = {}) {
478
+ const t = u.protocol(), r = Object.freeze((o.capabilities ?? []).filter((i) => !t.capabilities.includes(i)));
479
+ return Object.freeze({ compatible: (!o.version || o.version === t.version) && !r.length, protocol: t, missingCapabilities: r });
480
+ },
481
+ getSession() {
482
+ if (j(), !c) throw new Error("应用 bridge 已释放");
483
+ return ee;
484
+ },
485
+ subscribeSession(o) {
486
+ const t = u.getSession(), r = (w) => o(w), i = () => {
487
+ K.delete(r), h.delete(i);
488
+ };
489
+ return K.add(r), h.add(i), q(() => o(t)), i;
490
+ },
491
+ subscribe(o) {
492
+ if (!c) throw new Error("应用 bridge 已释放");
493
+ const t = me(o), r = () => {
494
+ t(), h.delete(r);
495
+ };
496
+ return h.add(r), r;
497
+ },
498
+ ready(o) {
499
+ const t = (i) => {
500
+ const w = i.tabs.find(
501
+ (v) => v.tabId === i.activeTabId && v.appId === e
502
+ );
503
+ w && o(w);
504
+ }, r = u.subscribe(t);
505
+ try {
506
+ t(ce());
507
+ } catch (i) {
508
+ throw r(), i;
509
+ }
510
+ return r;
511
+ },
512
+ bindPage(o) {
513
+ const t = d.tabs.find((r) => r.tabId === o && r.appId === e);
514
+ if (!t) throw new Error("页面不属于该应用");
515
+ return u.page(t.key);
516
+ },
517
+ page(o) {
518
+ if (j(), !c) throw new Error("应用 bridge 已释放");
519
+ const t = d.tabs.find((f) => f.key === o && f.appId === e);
520
+ if (!t) throw new Error("页面上下文不存在或不属于该应用");
521
+ let r = !1;
522
+ const i = /* @__PURE__ */ new Set(), w = () => {
523
+ if (D) throw new p("HOST_DISPOSED", "MicroHost 已销毁");
524
+ if (r || !d.tabs.some((f) => f.key === o))
525
+ throw new p("PAGE_RELEASED", "页面上下文已释放");
526
+ }, v = () => {
527
+ var f;
528
+ r = !0, i.forEach((I) => I()), i.clear(), (f = B.get(o)) == null || f.delete(v);
529
+ };
530
+ let S = B.get(o);
531
+ return S || B.set(o, S = /* @__PURE__ */ new Set()), S.add(v), {
532
+ get tab() {
533
+ return d.tabs.find((f) => f.key === o) ?? t;
534
+ },
535
+ callHost(f, ...I) {
536
+ return b(f, I, { key: o, released: () => r, cleanups: i });
537
+ },
538
+ callHostWithOptions(f, I, ...E) {
539
+ return b(f, E, { key: o, released: () => r, cleanups: i }, I);
540
+ },
541
+ async openTab(f) {
542
+ w();
543
+ const I = await J(
544
+ { type: "open", input: f },
545
+ e,
546
+ o,
547
+ !1,
548
+ () => r
549
+ );
550
+ if (I.tab) return I.tab;
551
+ const E = d.tabs.find(
552
+ (_) => _.tabId === f.tabId && _.appId === e
553
+ );
554
+ if (I.status === "noop" && E) return E;
555
+ throw new p(
556
+ I.status === "cancelled" ? "CANCELLED" : "STALE",
557
+ "导航未提交"
558
+ );
559
+ },
560
+ async navigateTo(f, I = {}) {
561
+ return w(), J(
562
+ { type: "open", input: f, signal: I.signal },
563
+ e,
564
+ o,
565
+ !1,
566
+ () => r,
567
+ !0
568
+ );
569
+ },
570
+ // 释放后仅允许关闭捕获的原 key;所有守卫仍然执行,不能误关同 id 新生命周期。
571
+ async closeCurrent() {
572
+ return D ? !1 : (await J(
573
+ { type: "close", target: { tabId: t.tabId, key: o } },
574
+ e,
575
+ void 0,
576
+ !0
577
+ )).status === "committed";
578
+ },
579
+ beforeLeave(f) {
580
+ if (w(), typeof f != "function")
581
+ throw new p("INVALID_OPERATION", "离页守卫必须为函数");
582
+ let I = oe.get(o);
583
+ I || oe.set(o, I = /* @__PURE__ */ new Set()), I.add(f);
584
+ const E = () => {
585
+ I.delete(f), i.delete(E);
586
+ };
587
+ return i.add(E), E;
588
+ },
589
+ async refreshCurrent() {
590
+ return w(), J(
591
+ { type: "refresh", target: { tabId: t.tabId, key: o } },
592
+ e,
593
+ o,
594
+ !1,
595
+ () => r
596
+ );
597
+ },
598
+ async back(f) {
599
+ return w(), J(
600
+ { type: "back", target: { tabId: t.tabId, key: o }, fallback: f },
601
+ e,
602
+ o,
603
+ !1,
604
+ () => r
605
+ );
606
+ },
607
+ async operate(f) {
608
+ return w(), J(f, e, o, !1, () => r);
609
+ },
610
+ emit(f, I) {
611
+ w(), ae.emit(f, { payload: I, metadata: Object.freeze({ source: "app", appId: e, tabKey: o }) });
612
+ },
613
+ on(f, I) {
614
+ w();
615
+ const E = le.on(f, I);
616
+ return i.add(E), () => {
617
+ E(), i.delete(E);
618
+ };
619
+ },
620
+ dispose: v
621
+ };
622
+ }
623
+ };
624
+ return re.set(e, u), u;
625
+ }
626
+ function Ge() {
627
+ if (!D) {
628
+ D = !0;
629
+ for (const e of B.values()) [...e].forEach((s) => s());
630
+ B.clear(), oe.clear(), U.clear();
631
+ for (const e of G.values()) [...e].forEach((s) => s());
632
+ G.clear(), te.clear(), K.clear(), re.clear(), le.clear(), d = { tabs: [], activeTabId: null };
633
+ }
634
+ }
635
+ return {
636
+ /** 注册应用的冻结元信息;不暴露 capabilities 注册表。 */
637
+ apps: [...Z.values()],
638
+ getState: ce,
639
+ subscribe: me,
640
+ openTab: He,
641
+ activateTab: $e,
642
+ closeTab: Ce,
643
+ updateTab: Fe,
644
+ updateSession: ze,
645
+ operate: Ve,
646
+ bridge: Be,
647
+ /** 宿主侧业务事件总线;无响应广播,来源元信息由宿主生成,异常通过 onError 隔离。 */
648
+ bus: le,
649
+ dispose: Ge
650
+ };
651
+ }
652
+ export {
653
+ qe as createMicroHost
654
+ };
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const v={TIMEOUT:"请求超时",CANCELLED:"请求已取消",DISPOSED:"连接已释放",LIMIT_EXCEEDED:"请求数量超过限制",UNKNOWN_METHOD:"未开放此方法",HANDLER_FAILED:"远端调用失败",SERIALIZATION_FAILED:"消息无法发送或序列化"};class g extends Error{constructor(a){super(v[a]),this.code=a,this.name="IframeRpcError"}}const x=n=>typeof n=="object"&&n!==null&&!Array.isArray(n),p=n=>typeof n=="string"&&n.length>0&&n.length<=128,M=n=>Number.isFinite(n)&&n>0&&n<=2147483647;function U(n){let a;try{a=new URL(n.remoteOrigin)}catch{throw new TypeError("remoteOrigin 必须是精确 HTTP(S) origin")}if(!["https:","http:"].includes(a.protocol)||a.origin!==n.remoteOrigin)throw new TypeError("remoteOrigin 必须是精确 HTTP(S) origin");if(!p(n.channel)||!p(n.sessionId))throw new TypeError("channel/sessionId 必须为 1 至 128 字符");const y=n.timeoutMs??3e4,u=n.handlerTimeoutMs??y,o=n.maxPending??100;if(!M(u)||!M(y)||!Number.isSafeInteger(o)||o<1)throw new TypeError("请求超时或数量限制非法");const{localWindow:f,remoteWindow:A,remoteOrigin:N,channel:P,sessionId:R}=n,E=new Map,m=new Map,D=(r,e)=>{const t=m.get(r);t&&(m.delete(r),clearTimeout(t.timer),e&&t.controller.abort())},T=new Map;let C=0,_=0,i=!1;const L=r=>A.postMessage({protocol:"fin-micro-rpc",version:1,channel:P,sessionId:R,...r},N),O=r=>{try{L(r)}catch{}},h=(r,e)=>O({type:"error",requestId:r,code:e}),w=(r,e,t)=>{const s=T.get(r);s&&(T.delete(r),s.cleanup(),e?s.reject(new g(e)):s.resolve(t))},j=r=>{if(i||r.source!==A||r.origin!==N)return;const e=r.data;if(!x(e)||e.protocol!=="fin-micro-rpc"||e.version!==1||e.channel!==P||e.sessionId!==R||!p(e.requestId))return;const t=e.requestId;if(e.type==="result"){if(!Object.hasOwn(e,"result"))return;w(t,void 0,e.result)}else if(e.type==="error"){if(typeof e.code!="string"||!Object.hasOwn(v,e.code))return;w(t,e.code)}else if(e.type==="cancel")D(t,!0);else if(e.type==="request"){if(!p(e.method)||!Array.isArray(e.args)||!/^[1-9]\d{0,15}$/.test(t))return;const s=Number(t);if(!Number.isSafeInteger(s)||s<=C)return;if(C=s,m.size>=o){h(t,"LIMIT_EXCEEDED");return}const l=E.get(e.method);if(!l){h(t,"UNKNOWN_METHOD");return}const c=new AbortController,b=setTimeout(()=>{D(t,!0),i||h(t,"TIMEOUT")},u);m.set(t,{controller:c,timer:b}),Promise.resolve().then(()=>{if(!(i||c.signal.aborted))return l(e.args,{signal:c.signal})}).then(d=>{if(!(i||c.signal.aborted))try{L({type:"result",requestId:t,result:d})}catch{h(t,"SERIALIZATION_FAILED")}},()=>{!i&&!c.signal.aborted&&h(t,"HANDLER_FAILED")}).finally(()=>{var d;((d=m.get(t))==null?void 0:d.controller)===c&&D(t,!1)})}};return f.addEventListener("message",j),{request(r,e,t={}){var c;if(i)return Promise.reject(new g("DISPOSED"));if(!p(r)||!Array.isArray(e))return Promise.reject(new TypeError("方法与参数非法"));const s=t.timeoutMs??y;if(!M(s))return Promise.reject(new TypeError("timeoutMs 非法"));if((c=t.signal)!=null&&c.aborted)return Promise.reject(new g("CANCELLED"));if(T.size>=o||_>=Number.MAX_SAFE_INTEGER)return Promise.reject(new g("LIMIT_EXCEEDED"));const l=String(++_);return new Promise((b,d)=>{const q=S=>{w(l,S),O({type:"cancel",requestId:l})},H=()=>q("CANCELLED"),F=setTimeout(()=>q("TIMEOUT"),s),I=t.signal;T.set(l,{resolve:S=>b(S),reject:d,cleanup(){clearTimeout(F),I==null||I.removeEventListener("abort",H)}}),I==null||I.addEventListener("abort",H,{once:!0});try{L({type:"request",requestId:l,method:r,args:e})}catch{w(l,"SERIALIZATION_FAILED")}})},expose(r,e){if(i)throw new g("DISPOSED");if(!p(r)||typeof e!="function"||E.has(r))throw new TypeError("方法非法或已注册");E.set(r,e);let t=!0;return()=>{t&&(t=!1,E.get(r)===e&&E.delete(r))}},dispose(){if(!i){i=!0,f.removeEventListener("message",j);for(const r of T.keys())w(r,"DISPOSED"),O({type:"cancel",requestId:r});for(const r of m.keys())D(r,!0);E.clear()}}}}function W(n,a,y){const u=[];try{for(const o of new Set(y))u.push(n.expose(o,(f,{signal:A})=>a.callHostWithOptions(o,{signal:A},...f)))}catch(o){throw u.forEach(f=>f()),o}return()=>u.forEach(o=>o())}exports.IframeRpcError=g;exports.createIframeRpc=U;exports.exposeHostCapabilities=W;