@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
@@ -0,0 +1,146 @@
1
+ const v = {
2
+ TIMEOUT: "请求超时",
3
+ CANCELLED: "请求已取消",
4
+ DISPOSED: "连接已释放",
5
+ LIMIT_EXCEEDED: "请求数量超过限制",
6
+ UNKNOWN_METHOD: "未开放此方法",
7
+ HANDLER_FAILED: "远端调用失败",
8
+ SERIALIZATION_FAILED: "消息无法发送或序列化"
9
+ };
10
+ class w extends Error {
11
+ constructor(a) {
12
+ super(v[a]), this.code = a, this.name = "IframeRpcError";
13
+ }
14
+ }
15
+ const x = (n) => typeof n == "object" && n !== null && !Array.isArray(n), p = (n) => typeof n == "string" && n.length > 0 && n.length <= 128, b = (n) => Number.isFinite(n) && n > 0 && n <= 2147483647;
16
+ function U(n) {
17
+ let a;
18
+ try {
19
+ a = new URL(n.remoteOrigin);
20
+ } catch {
21
+ throw new TypeError("remoteOrigin 必须是精确 HTTP(S) origin");
22
+ }
23
+ if (!["https:", "http:"].includes(a.protocol) || a.origin !== n.remoteOrigin)
24
+ throw new TypeError("remoteOrigin 必须是精确 HTTP(S) origin");
25
+ if (!p(n.channel) || !p(n.sessionId)) throw new TypeError("channel/sessionId 必须为 1 至 128 字符");
26
+ const g = n.timeoutMs ?? 3e4, f = n.handlerTimeoutMs ?? g, o = n.maxPending ?? 100;
27
+ if (!b(f) || !b(g) || !Number.isSafeInteger(o) || o < 1) throw new TypeError("请求超时或数量限制非法");
28
+ const { localWindow: l, remoteWindow: A, remoteOrigin: M, channel: P, sessionId: _ } = n, E = /* @__PURE__ */ new Map(), m = /* @__PURE__ */ new Map(), D = (r, e) => {
29
+ const t = m.get(r);
30
+ t && (m.delete(r), clearTimeout(t.timer), e && t.controller.abort());
31
+ }, y = /* @__PURE__ */ new Map();
32
+ let C = 0, R = 0, i = !1;
33
+ const L = (r) => A.postMessage({ protocol: "fin-micro-rpc", version: 1, channel: P, sessionId: _, ...r }, M), O = (r) => {
34
+ try {
35
+ L(r);
36
+ } catch {
37
+ }
38
+ }, T = (r, e) => O({ type: "error", requestId: r, code: e }), h = (r, e, t) => {
39
+ const s = y.get(r);
40
+ s && (y.delete(r), s.cleanup(), e ? s.reject(new w(e)) : s.resolve(t));
41
+ }, j = (r) => {
42
+ if (i || r.source !== A || r.origin !== M) return;
43
+ const e = r.data;
44
+ if (!x(e) || e.protocol !== "fin-micro-rpc" || e.version !== 1 || e.channel !== P || e.sessionId !== _ || !p(e.requestId)) return;
45
+ const t = e.requestId;
46
+ if (e.type === "result") {
47
+ if (!Object.hasOwn(e, "result")) return;
48
+ h(t, void 0, e.result);
49
+ } else if (e.type === "error") {
50
+ if (typeof e.code != "string" || !Object.hasOwn(v, e.code)) return;
51
+ h(t, e.code);
52
+ } else if (e.type === "cancel")
53
+ D(t, !0);
54
+ else if (e.type === "request") {
55
+ if (!p(e.method) || !Array.isArray(e.args) || !/^[1-9]\d{0,15}$/.test(t)) return;
56
+ const s = Number(t);
57
+ if (!Number.isSafeInteger(s) || s <= C) return;
58
+ if (C = s, m.size >= o) {
59
+ T(t, "LIMIT_EXCEEDED");
60
+ return;
61
+ }
62
+ const u = E.get(e.method);
63
+ if (!u) {
64
+ T(t, "UNKNOWN_METHOD");
65
+ return;
66
+ }
67
+ const c = new AbortController(), N = setTimeout(() => {
68
+ D(t, !0), i || T(t, "TIMEOUT");
69
+ }, f);
70
+ m.set(t, { controller: c, timer: N }), Promise.resolve().then(() => {
71
+ if (!(i || c.signal.aborted))
72
+ return u(e.args, { signal: c.signal });
73
+ }).then((d) => {
74
+ if (!(i || c.signal.aborted))
75
+ try {
76
+ L({ type: "result", requestId: t, result: d });
77
+ } catch {
78
+ T(t, "SERIALIZATION_FAILED");
79
+ }
80
+ }, () => {
81
+ !i && !c.signal.aborted && T(t, "HANDLER_FAILED");
82
+ }).finally(() => {
83
+ var d;
84
+ ((d = m.get(t)) == null ? void 0 : d.controller) === c && D(t, !1);
85
+ });
86
+ }
87
+ };
88
+ return l.addEventListener("message", j), {
89
+ request(r, e, t = {}) {
90
+ var c;
91
+ if (i) return Promise.reject(new w("DISPOSED"));
92
+ if (!p(r) || !Array.isArray(e)) return Promise.reject(new TypeError("方法与参数非法"));
93
+ const s = t.timeoutMs ?? g;
94
+ if (!b(s)) return Promise.reject(new TypeError("timeoutMs 非法"));
95
+ if ((c = t.signal) != null && c.aborted) return Promise.reject(new w("CANCELLED"));
96
+ if (y.size >= o || R >= Number.MAX_SAFE_INTEGER) return Promise.reject(new w("LIMIT_EXCEEDED"));
97
+ const u = String(++R);
98
+ return new Promise((N, d) => {
99
+ const q = (S) => {
100
+ h(u, S), O({ type: "cancel", requestId: u });
101
+ }, H = () => q("CANCELLED"), F = setTimeout(() => q("TIMEOUT"), s), I = t.signal;
102
+ y.set(u, { resolve: (S) => N(S), reject: d, cleanup() {
103
+ clearTimeout(F), I == null || I.removeEventListener("abort", H);
104
+ } }), I == null || I.addEventListener("abort", H, { once: !0 });
105
+ try {
106
+ L({ type: "request", requestId: u, method: r, args: e });
107
+ } catch {
108
+ h(u, "SERIALIZATION_FAILED");
109
+ }
110
+ });
111
+ },
112
+ expose(r, e) {
113
+ if (i) throw new w("DISPOSED");
114
+ if (!p(r) || typeof e != "function" || E.has(r)) throw new TypeError("方法非法或已注册");
115
+ E.set(r, e);
116
+ let t = !0;
117
+ return () => {
118
+ t && (t = !1, E.get(r) === e && E.delete(r));
119
+ };
120
+ },
121
+ dispose() {
122
+ if (!i) {
123
+ i = !0, l.removeEventListener("message", j);
124
+ for (const r of y.keys())
125
+ h(r, "DISPOSED"), O({ type: "cancel", requestId: r });
126
+ for (const r of m.keys()) D(r, !0);
127
+ E.clear();
128
+ }
129
+ }
130
+ };
131
+ }
132
+ function W(n, a, g) {
133
+ const f = [];
134
+ try {
135
+ for (const o of new Set(g))
136
+ f.push(n.expose(o, (l, { signal: A }) => a.callHostWithOptions(o, { signal: A }, ...l)));
137
+ } catch (o) {
138
+ throw f.forEach((l) => l()), o;
139
+ }
140
+ return () => f.forEach((o) => o());
141
+ }
142
+ export {
143
+ w as IframeRpcError,
144
+ U as createIframeRpc,
145
+ W as exposeHostCapabilities
146
+ };
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("./sdk.cjs"),c=require("./core.cjs"),r=require("./runtime.cjs"),o=require("./vue.cjs"),i=require("./browser.cjs"),t=1;exports.HostCallError=e.HostCallError;exports.NavigationError=e.NavigationError;exports.TokenError=e.TokenError;exports.createAuthFailureCapabilities=e.createAuthFailureCapabilities;exports.createNotificationBus=e.createNotificationBus;exports.createTokenCapability=e.createTokenCapability;exports.getMicroBridge=e.getMicroBridge;exports.getToken=e.getToken;exports.hasAll=e.hasAll;exports.hasAny=e.hasAny;exports.hasPermission=e.hasPermission;exports.createMicroHost=c.createMicroHost;exports.createWujieRuntime=r.createWujieRuntime;exports.isMicroFrontend=r.isMicroFrontend;exports.setupWujieLifecycle=r.setupWujieLifecycle;exports.MicroAppContainer=o.MicroAppContainer;exports.MicroRouteView=o.MicroRouteView;exports.MicroTabCache=o.MicroTabCache;exports.connectMicroRouter=o.connectMicroRouter;exports.useMicroHost=o.useMicroHost;exports.useMicroPage=o.useMicroPage;exports.useMicroTab=o.useMicroTab;exports.usePermission=o.usePermission;exports.connectMicroBrowser=i.connectMicroBrowser;exports.decodeMicroLocation=i.decodeMicroLocation;exports.encodeMicroLocation=i.encodeMicroLocation;exports.hostRouterProtocol=t;
package/dist/index.mjs ADDED
@@ -0,0 +1,35 @@
1
+ import { HostCallError as i, NavigationError as t, TokenError as c, createAuthFailureCapabilities as a, createNotificationBus as n, createTokenCapability as s, getMicroBridge as u, getToken as M, hasAll as p, hasAny as l, hasPermission as m } from "./sdk.mjs";
2
+ import { createMicroHost as h } from "./core.mjs";
3
+ import { createWujieRuntime as d, isMicroFrontend as g, setupWujieLifecycle as C } from "./runtime.mjs";
4
+ import { MicroAppContainer as b, MicroRouteView as A, MicroTabCache as P, connectMicroRouter as R, useMicroHost as k, useMicroPage as y, useMicroTab as B, usePermission as E } from "./vue.mjs";
5
+ import { connectMicroBrowser as L, decodeMicroLocation as j, encodeMicroLocation as w } from "./browser.mjs";
6
+ const o = 1;
7
+ export {
8
+ i as HostCallError,
9
+ b as MicroAppContainer,
10
+ A as MicroRouteView,
11
+ P as MicroTabCache,
12
+ t as NavigationError,
13
+ c as TokenError,
14
+ L as connectMicroBrowser,
15
+ R as connectMicroRouter,
16
+ a as createAuthFailureCapabilities,
17
+ h as createMicroHost,
18
+ n as createNotificationBus,
19
+ s as createTokenCapability,
20
+ d as createWujieRuntime,
21
+ j as decodeMicroLocation,
22
+ w as encodeMicroLocation,
23
+ u as getMicroBridge,
24
+ M as getToken,
25
+ p as hasAll,
26
+ l as hasAny,
27
+ m as hasPermission,
28
+ o as hostRouterProtocol,
29
+ g as isMicroFrontend,
30
+ C as setupWujieLifecycle,
31
+ k as useMicroHost,
32
+ y as useMicroPage,
33
+ B as useMicroTab,
34
+ E as usePermission
35
+ };
@@ -0,0 +1,76 @@
1
+ import { defineComponent as w, shallowRef as v, provide as m, shallowReactive as R, onBeforeUnmount as O, h as f } from "vue";
2
+ import { useRouter as E, START_LOCATION as h, routeLocationKey as g, viewDepthKey as L, RouterView as b, loadRouteLocation as D } from "vue-router";
3
+ import { useMicroTab as I } from "./vue.mjs";
4
+ class d extends Error {
5
+ constructor(o) {
6
+ super(o), this.code = o;
7
+ }
8
+ }
9
+ function T(c, o) {
10
+ let e = c.resolve(o, h);
11
+ const p = e, u = /* @__PURE__ */ new Set();
12
+ for (let n = 0; n < 30; n++) {
13
+ if (!e.matched.length) throw new d("NOT_FOUND");
14
+ const i = e.matched[e.matched.length - 1];
15
+ if (!i.redirect) return e === p ? e : { ...e, redirectedFrom: p };
16
+ if (u.has(e.fullPath)) throw new d("REDIRECT_LOOP");
17
+ u.add(e.fullPath);
18
+ const t = typeof i.redirect == "function" ? i.redirect(e) : i.redirect;
19
+ let a;
20
+ if (typeof t == "string")
21
+ if (t.includes("?") || t.includes("#")) {
22
+ const r = c.resolve(t, h);
23
+ a = { path: r.path, query: r.query, hash: r.hash };
24
+ } else a = { path: t };
25
+ else a = t;
26
+ const s = "path" in a && a.path != null;
27
+ if (!s && !("name" in a)) throw new d("RESOLVE_FAILED");
28
+ e = c.resolve({
29
+ query: e.query,
30
+ hash: e.hash,
31
+ params: s ? {} : e.params,
32
+ ...a
33
+ }, h);
34
+ }
35
+ throw new d("REDIRECT_LOOP");
36
+ }
37
+ const V = w({
38
+ name: "MicroRouteViewImpl",
39
+ props: { name: { type: String, default: "default" } },
40
+ slots: Object,
41
+ setup(c, { slots: o }) {
42
+ const e = I(), p = E(), u = v(), n = v(), i = v(!1);
43
+ let t = !1;
44
+ const a = {};
45
+ for (const r of Object.keys(h))
46
+ Object.defineProperty(a, r, { enumerable: !0, get: () => (u.value ?? h)[r] });
47
+ m(g, R(a)), m(L, 0);
48
+ async function s() {
49
+ if (t || i.value) return;
50
+ i.value = !0, n.value = void 0;
51
+ let r = "RESOLVE_FAILED";
52
+ try {
53
+ const l = T(p, e.route);
54
+ r = "LOAD_FAILED";
55
+ const y = await D(l);
56
+ t || (u.value = y);
57
+ } catch (l) {
58
+ t || (n.value = l instanceof d ? l.code : r);
59
+ } finally {
60
+ t || (i.value = !1);
61
+ }
62
+ }
63
+ return O(() => {
64
+ t = !0;
65
+ }), s(), () => {
66
+ var r, l;
67
+ return n.value ? ((r = o.error) == null ? void 0 : r.call(o, { tab: e, code: n.value, retry: s })) ?? f("div", { role: "alert", "data-micro-route-error": n.value }, [
68
+ f("p", `页面加载失败(${n.value})`),
69
+ f("button", { type: "button", onClick: s }, "重新加载")
70
+ ]) : i.value || !u.value ? ((l = o.loading) == null ? void 0 : l.call(o, { tab: e })) ?? f("p", { role: "status" }, "页面加载中…") : f(b, { route: u.value, name: c.name, key: e.key });
71
+ };
72
+ }
73
+ });
74
+ export {
75
+ V as MicroRouteViewImpl
76
+ };
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const t=require("vue"),u=require("vue-router"),O=require("./vue.cjs");class f extends Error{constructor(a){super(a),this.code=a}}function R(h,a){let e=h.resolve(a,u.START_LOCATION);const p=e,c=new Set;for(let i=0;i<30;i++){if(!e.matched.length)throw new f("NOT_FOUND");const l=e.matched[e.matched.length-1];if(!l.redirect)return e===p?e:{...e,redirectedFrom:p};if(c.has(e.fullPath))throw new f("REDIRECT_LOOP");c.add(e.fullPath);const r=typeof l.redirect=="function"?l.redirect(e):l.redirect;let n;if(typeof r=="string")if(r.includes("?")||r.includes("#")){const o=h.resolve(r,u.START_LOCATION);n={path:o.path,query:o.query,hash:o.hash}}else n={path:r};else n=r;const d="path"in n&&n.path!=null;if(!d&&!("name"in n))throw new f("RESOLVE_FAILED");e=h.resolve({query:e.query,hash:e.hash,params:d?{}:e.params,...n},u.START_LOCATION)}throw new f("REDIRECT_LOOP")}const w=t.defineComponent({name:"MicroRouteViewImpl",props:{name:{type:String,default:"default"}},slots:Object,setup(h,{slots:a}){const e=O.useMicroTab(),p=u.useRouter(),c=t.shallowRef(),i=t.shallowRef(),l=t.shallowRef(!1);let r=!1;const n={};for(const o of Object.keys(u.START_LOCATION))Object.defineProperty(n,o,{enumerable:!0,get:()=>(c.value??u.START_LOCATION)[o]});t.provide(u.routeLocationKey,t.shallowReactive(n)),t.provide(u.viewDepthKey,0);async function d(){if(r||l.value)return;l.value=!0,i.value=void 0;let o="RESOLVE_FAILED";try{const s=R(p,e.route);o="LOAD_FAILED";const v=await u.loadRouteLocation(s);r||(c.value=v)}catch(s){r||(i.value=s instanceof f?s.code:o)}finally{r||(l.value=!1)}}return t.onBeforeUnmount(()=>{r=!0}),d(),()=>{var o,s;return i.value?((o=a.error)==null?void 0:o.call(a,{tab:e,code:i.value,retry:d}))??t.h("div",{role:"alert","data-micro-route-error":i.value},[t.h("p",`页面加载失败(${i.value})`),t.h("button",{type:"button",onClick:d},"重新加载")]):l.value||!c.value?((s=a.loading)==null?void 0:s.call(a,{tab:e}))??t.h("p",{role:"status"},"页面加载中…"):t.h(u.RouterView,{route:c.value,name:h.name,key:e.key})}}});exports.MicroRouteViewImpl=w;
@@ -0,0 +1 @@
1
+ "use strict";var J=Object.create;var W=Object.defineProperty;var O=Object.getOwnPropertyDescriptor;var L=Object.getOwnPropertyNames;var $=Object.getPrototypeOf,k=Object.prototype.hasOwnProperty;var A=(n,t,s,c)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of L(t))!k.call(n,i)&&i!==s&&W(n,i,{get:()=>t[i],enumerable:!(c=O(t,i))||c.enumerable});return n};var F=(n,t,s)=>(s=n!=null?J($(n)):{},A(t||!n||!n.__esModule?W(s,"default",{value:n,enumerable:!0}):s,n));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const v=new WeakMap;function T(){var n;return typeof window<"u"&&typeof((n=window.__WUJIE)==null?void 0:n.mount)=="function"}function R(n){var y;if(typeof window>"u"){const r=Promise.resolve();return()=>r}const t=window,s=(y=v.get(t))==null?void 0:y(),c=r=>{console.error("[fin-micro] 应用生命周期失败")};let i=(s==null?void 0:s.catch(()=>{}))??Promise.resolve(),f=!1,d,e;function o(){const r=e;if(!r||(e=void 0,!r.started))return i;try{i=Promise.resolve(n.unmount())}catch(u){i=Promise.reject(u)}return i.catch(c),i}function a(){if(f)return Promise.resolve();if(e)return e.ready;const r={started:!1,ready:Promise.resolve()};e=r;const u=async()=>{if(!(f||e!==r)){r.started=!0;try{await n.mount()}catch(h){throw e===r&&await o().catch(()=>{}),h}}};return r.ready=i.then(u,u),r.ready.catch(c),r.ready}function m(){return d||(f=!0,t.removeEventListener("pagehide",g),t.__WUJIE_MOUNT===a&&delete t.__WUJIE_MOUNT,t.__WUJIE_UNMOUNT===o&&delete t.__WUJIE_UNMOUNT,v.get(t)===m&&v.delete(t),d=o(),d)}function g(r){r.persisted||m()}if(v.set(t,m),t.__WUJIE_MOUNT=a,t.__WUJIE_UNMOUNT=o,t.addEventListener("pagehide",g),T())try{Promise.resolve(t.__WUJIE.mount()).catch(c)}catch{c()}else a();return m}let S=0;function C(n,t={}){const s=t.mountTimeoutMs??3e4;if(!Number.isFinite(s)||s<=0||s>2147483647)throw new Error("mountTimeoutMs 必须在 (0, 2147483647] 范围内");const c=new Map;let i=!1,f;const d={async mount(e,o){if(i)throw new Error("Runtime 已销毁");const a=c.get(e);if(a!=null&&a.closing)return await a.closing,d.mount(e,o);if(a){if(a.element!==o)throw new Error(`应用 ${e} 已存在共享容器`);return a.ready}const m=n.apps.find(w=>w.appId===e);if(!m)throw new Error(`未知子应用: ${e}`);if(m.kind==="local")throw new Error("本地应用不能通过 Wujie 挂载");const g=n.bridge(e),y=`${e}__fin_micro_${++S}`,r=o.ownerDocument.createElement("div");r.style.height="100%",o.appendChild(r);const u=new AbortController;let h=!1,P,_;const j=new Promise((w,E)=>{_=E}),p=setTimeout(()=>_(new Error(`子应用 ${e} 加载超时`)),s),l={element:o,ready:Promise.resolve(),close(){return l.closing||(l.closing=Promise.resolve().then(async()=>{try{h&&await P(y)}finally{c.get(e)===l&&c.delete(e)}}),r.remove(),clearTimeout(p),u.abort(),_(new Error(`子应用 ${e} 挂载已中断`))),l.closing}};c.set(e,l);const N=(async()=>{const{startApp:w,destroyApp:E}=await import("wujie");if(u.signal.aborted)return;P=E,h=!0;const M=await w({name:y,url:m.entry,el:r,alive:!1,sync:!1,props:{finMicro:g},plugins:[{htmlLoader:U=>U}],fetch:(U,b)=>fetch(U,{...b,signal:u.signal}),loadError(){_(new Error("子应用加载失败"))}});u.signal.aborted&&typeof M=="function"&&await M()})();return l.ready=(async()=>{try{await Promise.race([j,N])}catch(w){throw await l.close().catch(()=>{console.error("[fin-micro] 应用挂载失败后的清理失败")}),w}finally{clearTimeout(p)}})(),l.ready},unmount(e){var o;return((o=c.get(e))==null?void 0:o.close())??Promise.resolve()},dispose(){return f||(i=!0,f=Promise.allSettled([...c.keys()].map(e=>d.unmount(e))).then(e=>{const o=e.find(a=>a.status==="rejected");if((o==null?void 0:o.status)==="rejected")throw o.reason}),f)}};return d}exports.createWujieRuntime=C;exports.isMicroFrontend=T;exports.setupWujieLifecycle=R;
@@ -0,0 +1,149 @@
1
+ const v = /* @__PURE__ */ new WeakMap();
2
+ function J() {
3
+ var u;
4
+ return typeof window < "u" && typeof ((u = window.__WUJIE) == null ? void 0 : u.mount) == "function";
5
+ }
6
+ function j(u) {
7
+ var y;
8
+ if (typeof window > "u") {
9
+ const t = Promise.resolve();
10
+ return () => t;
11
+ }
12
+ const n = window, l = (y = v.get(n)) == null ? void 0 : y(), i = (t) => {
13
+ console.error("[fin-micro] 应用生命周期失败");
14
+ };
15
+ let s = (l == null ? void 0 : l.catch(() => {
16
+ })) ?? Promise.resolve(), f = !1, d, e;
17
+ function r() {
18
+ const t = e;
19
+ if (!t || (e = void 0, !t.started)) return s;
20
+ try {
21
+ s = Promise.resolve(u.unmount());
22
+ } catch (c) {
23
+ s = Promise.reject(c);
24
+ }
25
+ return s.catch(i), s;
26
+ }
27
+ function o() {
28
+ if (f) return Promise.resolve();
29
+ if (e) return e.ready;
30
+ const t = { started: !1, ready: Promise.resolve() };
31
+ e = t;
32
+ const c = async () => {
33
+ if (!(f || e !== t)) {
34
+ t.started = !0;
35
+ try {
36
+ await u.mount();
37
+ } catch (_) {
38
+ throw e === t && await r().catch(() => {
39
+ }), _;
40
+ }
41
+ }
42
+ };
43
+ return t.ready = s.then(c, c), t.ready.catch(i), t.ready;
44
+ }
45
+ function m() {
46
+ return d || (f = !0, n.removeEventListener("pagehide", h), n.__WUJIE_MOUNT === o && delete n.__WUJIE_MOUNT, n.__WUJIE_UNMOUNT === r && delete n.__WUJIE_UNMOUNT, v.get(n) === m && v.delete(n), d = r(), d);
47
+ }
48
+ function h(t) {
49
+ t.persisted || m();
50
+ }
51
+ if (v.set(n, m), n.__WUJIE_MOUNT = o, n.__WUJIE_UNMOUNT = r, n.addEventListener("pagehide", h), J())
52
+ try {
53
+ Promise.resolve(n.__WUJIE.mount()).catch(i);
54
+ } catch {
55
+ i();
56
+ }
57
+ else o();
58
+ return m;
59
+ }
60
+ let b = 0;
61
+ function O(u, n = {}) {
62
+ const l = n.mountTimeoutMs ?? 3e4;
63
+ if (!Number.isFinite(l) || l <= 0 || l > 2147483647) throw new Error("mountTimeoutMs 必须在 (0, 2147483647] 范围内");
64
+ const i = /* @__PURE__ */ new Map();
65
+ let s = !1, f;
66
+ const d = {
67
+ async mount(e, r) {
68
+ if (s) throw new Error("Runtime 已销毁");
69
+ const o = i.get(e);
70
+ if (o != null && o.closing)
71
+ return await o.closing, d.mount(e, r);
72
+ if (o) {
73
+ if (o.element !== r) throw new Error(`应用 ${e} 已存在共享容器`);
74
+ return o.ready;
75
+ }
76
+ const m = u.apps.find((w) => w.appId === e);
77
+ if (!m) throw new Error(`未知子应用: ${e}`);
78
+ if (m.kind === "local") throw new Error("本地应用不能通过 Wujie 挂载");
79
+ const h = u.bridge(e), y = `${e}__fin_micro_${++b}`, t = r.ownerDocument.createElement("div");
80
+ t.style.height = "100%", r.appendChild(t);
81
+ const c = new AbortController();
82
+ let _ = !1, P, g;
83
+ const W = new Promise((w, E) => {
84
+ g = E;
85
+ }), p = setTimeout(() => g(new Error(`子应用 ${e} 加载超时`)), l), a = {
86
+ element: r,
87
+ ready: Promise.resolve(),
88
+ close() {
89
+ return a.closing || (a.closing = Promise.resolve().then(async () => {
90
+ try {
91
+ _ && await P(y);
92
+ } finally {
93
+ i.get(e) === a && i.delete(e);
94
+ }
95
+ }), t.remove(), clearTimeout(p), c.abort(), g(new Error(`子应用 ${e} 挂载已中断`))), a.closing;
96
+ }
97
+ };
98
+ i.set(e, a);
99
+ const T = (async () => {
100
+ const { startApp: w, destroyApp: E } = await import("wujie");
101
+ if (c.signal.aborted) return;
102
+ P = E, _ = !0;
103
+ const M = await w({
104
+ name: y,
105
+ url: m.entry,
106
+ el: t,
107
+ alive: !1,
108
+ sync: !1,
109
+ props: { finMicro: h },
110
+ // HTML 缓存会捕获旧 fetch/loadError;禁用这层缓存,避免重试沿用已中断的信号。
111
+ plugins: [{ htmlLoader: (U) => U }],
112
+ // 只中断资源读取;不触碰业务 bridge,更不自动重试业务写调用。
113
+ fetch: (U, N) => fetch(U, { ...N, signal: c.signal }),
114
+ loadError() {
115
+ g(new Error("子应用加载失败"));
116
+ }
117
+ });
118
+ c.signal.aborted && typeof M == "function" && await M();
119
+ })();
120
+ return a.ready = (async () => {
121
+ try {
122
+ await Promise.race([W, T]);
123
+ } catch (w) {
124
+ throw await a.close().catch(() => {
125
+ console.error("[fin-micro] 应用挂载失败后的清理失败");
126
+ }), w;
127
+ } finally {
128
+ clearTimeout(p);
129
+ }
130
+ })(), a.ready;
131
+ },
132
+ unmount(e) {
133
+ var r;
134
+ return ((r = i.get(e)) == null ? void 0 : r.close()) ?? Promise.resolve();
135
+ },
136
+ dispose() {
137
+ return f || (s = !0, f = Promise.allSettled([...i.keys()].map((e) => d.unmount(e))).then((e) => {
138
+ const r = e.find((o) => o.status === "rejected");
139
+ if ((r == null ? void 0 : r.status) === "rejected") throw r.reason;
140
+ }), f);
141
+ }
142
+ };
143
+ return d;
144
+ }
145
+ export {
146
+ O as createWujieRuntime,
147
+ J as isMicroFrontend,
148
+ j as setupWujieLifecycle
149
+ };
package/dist/sdk.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function f(){const e=new Map;return{on(t,r){let n=e.get(t);return n||e.set(t,n=new Set),n.add(r),()=>{n.delete(r),!n.size&&e.get(t)===n&&e.delete(t)}},emit(t,r){const n=e.get(t);if(n)for(const i of[...n]){if(e.get(t)!==n)break;n.has(i)&&i(r)}},clear(){e.clear()}}}class d extends Error{constructor(t,r){super(r),this.code=t,this.name="HostCallError"}}class h extends Error{constructor(t,r){super(r),this.code=t,this.name="NavigationError"}}class o extends Error{constructor(t){super(`Token 获取失败: ${t}`),this.code=t,this.name="TokenError"}}function c(e){return e===null||typeof e=="string"&&e.length>0&&e.length<=16384&&!/[\s\u0000-\u001f\u007f]/.test(e)}async function g(e={}){const t=e.bridge??l();if(!t)throw new o("NO_BRIDGE");const r=await t.callHostWithOptions("getToken",{signal:e.signal,timeoutMs:e.timeoutMs});if(!r||typeof r!="object")throw new o("INVALID_RESULT");const n=r;if(n.status==="unauthenticated")throw new o("UNAUTHENTICATED");if(n.status==="session-changed")throw new o("SESSION_CHANGED");if(n.status!=="authenticated"||!c(n.token))throw new o("INVALID_RESULT");return n.token}function p(e){const t=Object.freeze([...e.apps]);return{apps:t,async handler(r){if(!t.includes(r.appId))throw new Error("Token 能力未授权");const n=e.getSession();if(!n.authenticated)return{status:"unauthenticated"};const i=await e.provider(r),a=e.getSession();if(!r.isActive()||r.signal.aborted||!a.authenticated||a.revision!==n.revision)return{status:"session-changed"};if(!c(i))throw new Error("Token 格式无效");return{status:"authenticated",token:i}}}}function w(e){const t=Object.freeze([...e.apps]),r=new WeakMap,n=i=>{if(!t.includes(i.appId)||!i.isActive()||i.signal.aborted)throw new Error("失效协调能力未授权或已释放")};return{beginAuthRequest:{apps:t,handler(i){n(i);const a=Object.freeze({request:"auth-request"});return r.set(a,{appId:i.appId,revision:e.getRevision()}),a}},reportAuthFailure:{apps:t,handler(i,a){if(n(i),!a||typeof a!="object")return!1;const s=r.get(a);return!s||s.appId!==i.appId||(r.delete(a),s.revision!==e.getRevision())?!1:(e.invalidate(),!0)}}}}function u(e,t){return!!(e!=null&&e.authenticated&&typeof t=="string"&&t.trim()&&e.permissions.includes(t))}function E(e,t){return Array.isArray(t)&&t.length>0&&t.some(r=>u(e,r))}function b(e,t){return Array.isArray(t)&&t.length>0&&t.every(r=>u(e,r))}function l(e){var r,n;const t=e??(typeof window>"u"?void 0:window);return(n=(r=t==null?void 0:t.$wujie)==null?void 0:r.props)==null?void 0:n.finMicro}exports.HostCallError=d;exports.NavigationError=h;exports.TokenError=o;exports.createAuthFailureCapabilities=w;exports.createNotificationBus=f;exports.createTokenCapability=p;exports.getMicroBridge=l;exports.getToken=g;exports.hasAll=b;exports.hasAny=E;exports.hasPermission=u;
package/dist/sdk.mjs ADDED
@@ -0,0 +1,118 @@
1
+ function d() {
2
+ const e = /* @__PURE__ */ new Map();
3
+ return {
4
+ on(t, r) {
5
+ let n = e.get(t);
6
+ return n || e.set(t, n = /* @__PURE__ */ new Set()), n.add(r), () => {
7
+ n.delete(r), !n.size && e.get(t) === n && e.delete(t);
8
+ };
9
+ },
10
+ emit(t, r) {
11
+ const n = e.get(t);
12
+ if (n)
13
+ for (const a of [...n]) {
14
+ if (e.get(t) !== n) break;
15
+ n.has(a) && a(r);
16
+ }
17
+ },
18
+ clear() {
19
+ e.clear();
20
+ }
21
+ };
22
+ }
23
+ class l extends Error {
24
+ /** 使用稳定 code 与对外 message 创建错误。 */
25
+ constructor(t, r) {
26
+ super(r), this.code = t, this.name = "HostCallError";
27
+ }
28
+ }
29
+ class h extends Error {
30
+ /** 创建公开错误。message 只包含稳定描述,不传递守卫原始 cause。 */
31
+ constructor(t, r) {
32
+ super(r), this.code = t, this.name = "NavigationError";
33
+ }
34
+ }
35
+ class s extends Error {
36
+ constructor(t) {
37
+ super(`Token 获取失败: ${t}`), this.code = t, this.name = "TokenError";
38
+ }
39
+ }
40
+ function u(e) {
41
+ return e === null || typeof e == "string" && e.length > 0 && e.length <= 16384 && !/[\s\u0000-\u001f\u007f]/.test(e);
42
+ }
43
+ async function g(e = {}) {
44
+ const t = e.bridge ?? f();
45
+ if (!t) throw new s("NO_BRIDGE");
46
+ const r = await t.callHostWithOptions("getToken", { signal: e.signal, timeoutMs: e.timeoutMs });
47
+ if (!r || typeof r != "object") throw new s("INVALID_RESULT");
48
+ const n = r;
49
+ if (n.status === "unauthenticated") throw new s("UNAUTHENTICATED");
50
+ if (n.status === "session-changed") throw new s("SESSION_CHANGED");
51
+ if (n.status !== "authenticated" || !u(n.token)) throw new s("INVALID_RESULT");
52
+ return n.token;
53
+ }
54
+ function w(e) {
55
+ const t = Object.freeze([...e.apps]);
56
+ return {
57
+ apps: t,
58
+ async handler(r) {
59
+ if (!t.includes(r.appId)) throw new Error("Token 能力未授权");
60
+ const n = e.getSession();
61
+ if (!n.authenticated) return { status: "unauthenticated" };
62
+ const a = await e.provider(r), i = e.getSession();
63
+ if (!r.isActive() || r.signal.aborted || !i.authenticated || i.revision !== n.revision) return { status: "session-changed" };
64
+ if (!u(a)) throw new Error("Token 格式无效");
65
+ return { status: "authenticated", token: a };
66
+ }
67
+ };
68
+ }
69
+ function p(e) {
70
+ const t = Object.freeze([...e.apps]), r = /* @__PURE__ */ new WeakMap(), n = (a) => {
71
+ if (!t.includes(a.appId) || !a.isActive() || a.signal.aborted) throw new Error("失效协调能力未授权或已释放");
72
+ };
73
+ return {
74
+ beginAuthRequest: {
75
+ apps: t,
76
+ handler(a) {
77
+ n(a);
78
+ const i = Object.freeze({ request: "auth-request" });
79
+ return r.set(i, { appId: a.appId, revision: e.getRevision() }), i;
80
+ }
81
+ },
82
+ reportAuthFailure: {
83
+ apps: t,
84
+ handler(a, i) {
85
+ if (n(a), !i || typeof i != "object") return !1;
86
+ const o = r.get(i);
87
+ return !o || o.appId !== a.appId || (r.delete(i), o.revision !== e.getRevision()) ? !1 : (e.invalidate(), !0);
88
+ }
89
+ }
90
+ };
91
+ }
92
+ function c(e, t) {
93
+ return !!(e != null && e.authenticated && typeof t == "string" && t.trim() && e.permissions.includes(t));
94
+ }
95
+ function E(e, t) {
96
+ return Array.isArray(t) && t.length > 0 && t.some((r) => c(e, r));
97
+ }
98
+ function A(e, t) {
99
+ return Array.isArray(t) && t.length > 0 && t.every((r) => c(e, r));
100
+ }
101
+ function f(e) {
102
+ var r, n;
103
+ const t = e ?? (typeof window > "u" ? void 0 : window);
104
+ return (n = (r = t == null ? void 0 : t.$wujie) == null ? void 0 : r.props) == null ? void 0 : n.finMicro;
105
+ }
106
+ export {
107
+ l as HostCallError,
108
+ h as NavigationError,
109
+ s as TokenError,
110
+ p as createAuthFailureCapabilities,
111
+ d as createNotificationBus,
112
+ w as createTokenCapability,
113
+ f as getMicroBridge,
114
+ g as getToken,
115
+ A as hasAll,
116
+ E as hasAny,
117
+ c as hasPermission
118
+ };
@@ -0,0 +1,46 @@
1
+ import { MicroHost } from '../core';
2
+ import { OpenTab } from '../sdk';
3
+ /** 浏览器同步配置;仅用于宿主端,不注入子应用,也不需要 Vue。 */
4
+ export interface MicroBrowserOptions {
5
+ /** 浏览器对象,缺省为 window;Node 测试必须显式传入适配对象。 */
6
+ window?: Pick<Window, 'location' | 'history' | 'sessionStorage' | 'addEventListener' | 'removeEventListener'>;
7
+ /** sessionStorage 键,默认 fin-micro:tabs:v1;同源多个工作台应分别设置。 */
8
+ storageKey?: string;
9
+ /** false 时仅恢复/持久化页签,不读写 URL 或监听历史;地址栏交由宿主路由适配层。 */
10
+ url?: boolean;
11
+ /** 宿主路由已校验的首次深链;恢复时优先于存储,仍须通过 core 授权。 */
12
+ initialLocation?: (Pick<OpenTab, 'appId' | 'tabId' | 'route'> & Partial<Pick<OpenTab, 'title' | 'activeMenuId'>>) | null;
13
+ /** 存储、URL 编解码或 history 写入错误回调;保留有效页签,编码失败保留原 URL,回调异常被隔离。 */
14
+ onError?: (error: Error) => void;
15
+ }
16
+ /**
17
+ * 将 tab 的应用、页签和路由编码为 hash;route 整段编码,保留子路由 query/hash 边界。
18
+ * @param tab 公开导航元信息,不编码标题或私有状态;应用和路由的业务合法性由宿主校验。
19
+ * @returns 可赋给 location.hash 的完整 hash 字符串。
20
+ * @throws 字段包含未配对 UTF-16 代理项时,由 encodeURIComponent 抛 URIError。
21
+ */
22
+ export declare function encodeMicroLocation(tab: Pick<OpenTab, 'appId' | 'tabId' | 'route'>): string;
23
+ /**
24
+ * 解码 hash 导航元信息;仅解码一次,保留子路由原有的百分号编码。
25
+ * @param hash encodeMicroLocation 生成的完整 hash;空字符串、# 和 #/ 表示空工作台。
26
+ * @returns 导航元信息或 null;应用存在性和路由合法性仍需由连接器及宿主校验。
27
+ * @throws 格式错误抛 Error;百分号编码错误由 decodeURIComponent 抛 URIError。
28
+ */
29
+ export declare function decodeMicroLocation(hash: string): Pick<OpenTab, 'appId' | 'tabId' | 'route'> | null;
30
+ /**
31
+ * 将 host 页签与 URL/sessionStorage 双向同步,应在基座渲染和 runtime 挂载前连接。
32
+ * @param host 浏览器导航所需的宿主控制器;标题更新通过状态订阅自动持久化。
33
+ * @param options 浏览器对象、存储键及容错回调;Node 环境需提供 window 适配。
34
+ * @returns 带 dispose 的连接器;必须在 host.dispose 前释放,以移除两个浏览器监听和宿主订阅。
35
+ * @remarks 仅保存 appId/tabId/title/route 等公开导航元信息,不保存令牌、实例、表单、缓存或业务数据。
36
+ * 必须在认证 ready 完成后才创建并连接 host。深链优先于存储;历史仅激活本次连接会话仍存活的 key。
37
+ * 整页刷新恢复公开元信息并产生新 key;旧页面会话 history 不复活页签,而是 replace 回落当前页。
38
+ * #/ 在已有页签时回落当前页,不隐式关闭;关闭全部后 URL 为 #/、存储 tabs 为空。
39
+ * 历史拒绝或失败只 replace 当前历史位置为有效地址,不保证恢复遍历之前的 history 索引。
40
+ * 整页刷新无法 await 异步离页守卫;业务可选 beforeunload 通用同步确认,但浏览器不保证触发。
41
+ * @throws 缺少浏览器环境或宿主已销毁;存储和非法 URL 的运行错误交给 onError,适配器安全回落。
42
+ */
43
+ export declare function connectMicroBrowser(host: Pick<MicroHost, 'apps' | 'getState' | 'subscribe' | 'openTab' | 'activateTab' | 'operate'>, options?: MicroBrowserOptions): {
44
+ /** 幂等移除本连接器的宿主订阅和浏览器事件;不清空存储、不关闭页签、不销毁宿主。 */
45
+ dispose(): void;
46
+ };
@@ -0,0 +1,63 @@
1
+ import { AppBridge, MicroApp, MicroState, MicroTab, OpenTab, Unsubscribe, HostCapabilities, HostCallContext, CapabilityArgs, CapabilityContract, NavigationOperation, NavigationResult, SessionInput, SessionSnapshot, BusinessEventBus } from '../sdk';
2
+ /** 显式公开的宿主业务函数;未注册键一律拒绝,不会按对象路径访问任意宿主成员。 */
3
+ export type HostCapabilityRegistry<C extends CapabilityContract<C>> = {
4
+ [K in keyof C]?: {
5
+ /** 接收可信 context 与业务参数,返回值或 Promise;必须自行校验外部参数,异常统一转 HANDLER_FAILED。 */
6
+ handler: (context: HostCallContext, ...args: CapabilityArgs<C, K>) => ReturnType<C[K]> | Promise<Awaited<ReturnType<C[K]>>>;
7
+ /** 允许调用的应用白名单;省略表示所有已注册应用,空数组表示全部拒绝。创建时复制,不是安全沙箱。 */
8
+ apps?: readonly string[];
9
+ };
10
+ };
11
+ /** 宿主创建配置;能力只在创建时注册,不注入整个宿主或业务服务对象。 */
12
+ export interface MicroHostOptions<C extends CapabilityContract<C>> {
13
+ /** 公开业务能力映射;缺省或未登记的方法默认拒绝。 */
14
+ capabilities?: HostCapabilityRegistry<C>;
15
+ /** 能力调用默认超时,单位毫秒,默认30000;必须为不超过2147483647的正有限数,避免计时器溢出。 */
16
+ callTimeoutMs?: number;
17
+ /** 初始公开会话;仅白名单字段会被复制。Token 必须由宿主私有保存。 */
18
+ session?: SessionInput;
19
+ /** 同步前端页面准入门禁,仅严格返回 true 放行;不支持异步 ACL,不代替服务端鉴权。
20
+ * 打开、激活、返回目标及刷新均检查,异步事务提交前复核;关闭不受阻止。
21
+ * 参数为仅含导航字段的冻结副本;异常、Promise 或非 boolean 返回均按权限拒绝处理。
22
+ */
23
+ canOpen?: (input: Readonly<OpenTab>) => boolean;
24
+ /**
25
+ * 页面 navigateTo 的跨应用授权;默认拒绝,仅同步严格返回 true 放行,不替代 canOpen 目标准入。
26
+ * 来源身份由宿主捕获,context、来源页快照及目标均冻结;执行及异步守卫通过后提交前各复核一次。
27
+ * 异常、Promise 及其他返回值统一拒绝,不泄漏策略内部错误;不授权其他跨应用导航命令。
28
+ */
29
+ canNavigate?: (context: {
30
+ readonly appId: string;
31
+ readonly tab: MicroTab;
32
+ }, target: Readonly<OpenTab>) => boolean;
33
+ /** 接收状态监听器或清理函数异常。异常不会中断已提交导航及其他监听;该回调自身抛错也被隔离。守卫异常由 operate 拒绝返回。 */
34
+ onError?: (error: unknown) => void;
35
+ }
36
+ /**
37
+ * 创建无框架的页签宿主。
38
+ * @param apps 应用注册表,appId 不可重复;远端 entry 必须为 HTTP(S),local 不配置执行地址。
39
+ * @param options 显式能力与应用白名单;泛型 C 仅提供编译检查,不代替运行时业务校验。
40
+ * @returns 宿主控制器;使用结束先释放 browser/runtime,再 dispose 宿主。
41
+ * @throws 应用重复、URL 非法;同步导航错误由各方法抛出,callHost 则始终 Promise 拒绝。
42
+ * @remarks 函数桥仅支持同窗口;支持超时与协作取消,不提供 postMessage、副作用回滚或恶意代码隔离。
43
+ */
44
+ export declare function createMicroHost<C extends CapabilityContract<C> = HostCapabilities>(apps: readonly MicroApp[], options?: MicroHostOptions<C>): {
45
+ /** 注册应用的冻结元信息;不暴露 capabilities 注册表。 */
46
+ apps: Readonly<MicroApp>[];
47
+ getState: () => MicroState;
48
+ subscribe: (handler: (state: MicroState) => void) => Unsubscribe;
49
+ openTab: (input: OpenTab) => MicroTab;
50
+ activateTab: (tabId: string) => boolean;
51
+ closeTab: (tabId: string, expectedKey?: string) => boolean;
52
+ updateTab: (tabId: string, patch: {
53
+ title: string;
54
+ }, expectedKey?: string) => MicroTab | undefined;
55
+ updateSession: (input: SessionInput) => SessionSnapshot;
56
+ operate: (operation: NavigationOperation) => Promise<NavigationResult>;
57
+ bridge: (appId: string) => AppBridge<C>;
58
+ /** 宿主侧业务事件总线;无响应广播,来源元信息由宿主生成,异常通过 onError 隔离。 */
59
+ bus: BusinessEventBus;
60
+ dispose: () => void;
61
+ };
62
+ /** 宿主控制器结构;框架适配器只依赖此结构,业务能力由 bridge<C> 在创建点保留类型。 */
63
+ export type MicroHost = ReturnType<typeof createMicroHost>;