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