@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.
- package/README.md +223 -2
- package/cli/index.mjs +240 -0
- package/dist/browser.cjs +1 -0
- package/dist/browser.mjs +169 -0
- package/dist/core.cjs +1 -0
- package/dist/core.mjs +654 -0
- package/dist/iframe.cjs +1 -0
- package/dist/iframe.mjs +146 -0
- package/dist/index.cjs +1 -0
- package/dist/index.mjs +35 -0
- package/dist/router-view-impl-B1NzCGKV.mjs +76 -0
- package/dist/router-view-impl-BvOToV_Z.cjs +1 -0
- package/dist/runtime.cjs +1 -0
- package/dist/runtime.mjs +149 -0
- package/dist/sdk.cjs +1 -0
- package/dist/sdk.mjs +118 -0
- package/dist/types/browser/index.d.ts +46 -0
- package/dist/types/core/index.d.ts +63 -0
- package/dist/types/iframe/index.d.ts +77 -0
- package/dist/types/index.d.ts +8 -0
- package/dist/types/runtime/index.d.ts +21 -0
- package/dist/types/runtime/lifecycle.d.ts +23 -0
- package/dist/types/sdk/index.d.ts +397 -0
- package/dist/types/vue/index.d.ts +102 -0
- package/dist/types/vue/router-view-impl.d.ts +41 -0
- package/dist/types/vue/router-view.d.ts +42 -0
- package/dist/types/vue/tab-context.d.ts +6 -0
- package/dist/vue.cjs +1 -0
- package/dist/vue.mjs +180 -0
- package/package.json +91 -3
- package/scaffold/base/index.html +12 -0
- package/scaffold/base/package.json.template +33 -0
- package/scaffold/base/scripts/check-runtime.mjs +43 -0
- package/scaffold/base/src/components/.gitkeep +1 -0
- package/scaffold/base/src/components/AppProviders.vue +25 -0
- package/scaffold/base/src/hooks/.gitkeep +1 -0
- package/scaffold/base/src/layout/.gitkeep +1 -0
- package/scaffold/base/src/locales/.gitkeep +1 -0
- package/scaffold/base/src/locales/en.ts +12 -0
- package/scaffold/base/src/locales/index.ts +13 -0
- package/scaffold/base/src/locales/zh-CN.ts +12 -0
- package/scaffold/base/src/main.ts +22 -0
- package/scaffold/base/src/router/.gitkeep +1 -0
- package/scaffold/base/src/router/demo.ts +6 -0
- package/scaffold/base/src/router/index.ts +13 -0
- package/scaffold/base/src/stores/.gitkeep +1 -0
- package/scaffold/base/src/stores/demo.ts +12 -0
- package/scaffold/base/src/stores/index.ts +11 -0
- package/scaffold/base/src/styles/index.css +18 -0
- package/scaffold/base/src/types/.gitkeep +1 -0
- package/scaffold/base/src/utils/.gitkeep +1 -0
- package/scaffold/base/src/utils/appConfig.ts +49 -0
- package/scaffold/base/src/utils/errorUtils.ts +92 -0
- package/scaffold/base/src/utils/requestUtils.ts +144 -0
- package/scaffold/base/src/views/.gitkeep +1 -0
- package/scaffold/base/src/views/DemoView.vue +26 -0
- package/scaffold/base/tsconfig.json +16 -0
- package/scaffold/base/vite.config.ts +8 -0
- package/scaffold/host/.env.example +13 -0
- package/scaffold/host/src/App.vue +4 -0
- package/scaffold/host/src/bootstrap.ts +50 -0
- package/scaffold/host/src/hooks/usePlatformSession.ts +91 -0
- package/scaffold/host/src/layout/HostHeader.vue +69 -0
- package/scaffold/host/src/layout/HostLayout.vue +168 -0
- package/scaffold/host/src/layout/HostMenu.vue +15 -0
- package/scaffold/host/src/layout/HostTabs.vue +47 -0
- package/scaffold/host/src/layout/HostWorkspace.vue +26 -0
- package/scaffold/host/src/localPages.ts +45 -0
- package/scaffold/host/src/platform.ts +239 -0
- package/scaffold/host/src/router/guards.ts +128 -0
- package/scaffold/host/src/router/index.ts +25 -0
- package/scaffold/host/src/router/targets.ts +30 -0
- package/scaffold/host/src/router/workspace.ts +63 -0
- package/scaffold/host/src/styles/shell.css +761 -0
- package/scaffold/host/src/types/platform.ts +12 -0
- package/scaffold/host/src/utils/request.ts +42 -0
- package/scaffold/host/src/views/HomeView.vue +19 -0
- package/scaffold/host/src/views/LoginRoute.vue +16 -0
- package/scaffold/host/src/views/LoginView.vue +88 -0
- package/scaffold/host/src/views/NotFoundView.vue +1 -0
- package/scaffold/host/src/views/ProfileView.vue +24 -0
- package/scaffold/host/src/views/WorkspaceRoute.vue +11 -0
- package/scaffold/vue/src/App.vue +24 -0
- package/scaffold/vue/src/bootstrap.ts +67 -0
- package/scaffold/vue/src/bridge/auth.ts +23 -0
- package/scaffold/vue/src/bridge/session.ts +16 -0
- package/scaffold/vue/src/main.ts +13 -0
- package/scaffold/vue/src/router/index.ts +16 -0
- package/scaffold/vue/src/views/DetailView.vue +57 -0
- package/scaffold/vue/src/views/ListView.vue +55 -0
- package/scaffold/vue/src/views/NotFoundView.vue +1 -0
package/dist/browser.mjs
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
function x(r) {
|
|
2
|
+
return `#/apps/${encodeURIComponent(r.appId)}/${encodeURIComponent(r.tabId)}?route=${encodeURIComponent(r.route)}`;
|
|
3
|
+
}
|
|
4
|
+
function H(r) {
|
|
5
|
+
if (!r || r === "#/" || r === "#") return null;
|
|
6
|
+
const i = /^#\/apps\/([^/?#]+)\/([^/?#]+)\?route=([^&#]*)$/.exec(r);
|
|
7
|
+
if (!i) throw new Error("无效的微应用 URL");
|
|
8
|
+
const [, o, v, y] = i;
|
|
9
|
+
return {
|
|
10
|
+
appId: decodeURIComponent(o),
|
|
11
|
+
tabId: decodeURIComponent(v),
|
|
12
|
+
route: decodeURIComponent(y)
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function J(r, i = {}) {
|
|
16
|
+
const o = i.window ?? window, v = i.storageKey ?? "fin-micro:tabs:v1", y = "fin-micro:navigation:v1", S = `${Date.now().toString(36)}:${Math.random().toString(36).slice(2)}`, u = (t) => {
|
|
17
|
+
var e;
|
|
18
|
+
try {
|
|
19
|
+
(e = i.onError) == null || e.call(i, t instanceof Error ? t : new Error(String(t)));
|
|
20
|
+
} catch {
|
|
21
|
+
}
|
|
22
|
+
}, j = new Set(r.apps.map((t) => t.appId)), w = (t, e) => typeof t == "string" && t.length > 0 && t.length <= e && !/[\u0000-\u001f\u007f]/.test(t);
|
|
23
|
+
function m(t) {
|
|
24
|
+
if (!t || typeof t != "object") return !1;
|
|
25
|
+
const e = t;
|
|
26
|
+
return w(e.appId, 200) && j.has(e.appId) && w(e.tabId, 500) && w(e.title, 500) && w(e.route, 8192) && e.route.startsWith("/") && !e.route.startsWith("//") && !e.route.includes("\\");
|
|
27
|
+
}
|
|
28
|
+
const U = (t) => ({
|
|
29
|
+
appId: t.appId,
|
|
30
|
+
tabId: t.tabId,
|
|
31
|
+
title: t.title,
|
|
32
|
+
route: t.route,
|
|
33
|
+
...t.activeMenuId !== void 0 ? { activeMenuId: t.activeMenuId } : {}
|
|
34
|
+
});
|
|
35
|
+
function $(t) {
|
|
36
|
+
try {
|
|
37
|
+
o.sessionStorage.setItem(
|
|
38
|
+
v,
|
|
39
|
+
JSON.stringify({
|
|
40
|
+
version: 1,
|
|
41
|
+
tabs: t.tabs.map(U),
|
|
42
|
+
activeTabId: t.activeTabId
|
|
43
|
+
})
|
|
44
|
+
);
|
|
45
|
+
} catch (e) {
|
|
46
|
+
u(e);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function k(t, e = !1) {
|
|
50
|
+
if (i.url !== !1)
|
|
51
|
+
try {
|
|
52
|
+
const n = t.tabs.find((R) => R.tabId === t.activeTabId), s = n ? x(n) : "#/", a = o.history.state, d = a == null ? void 0 : a[y], f = o.location.hash === s;
|
|
53
|
+
if (f && (d == null ? void 0 : d.session) === S && (d == null ? void 0 : d.key) === ((n == null ? void 0 : n.key) ?? null)) return;
|
|
54
|
+
const c = `${o.location.pathname}${o.location.search}${s}`, b = {
|
|
55
|
+
...a && typeof a == "object" ? a : {},
|
|
56
|
+
[y]: { session: S, key: (n == null ? void 0 : n.key) ?? null }
|
|
57
|
+
};
|
|
58
|
+
e && !f ? o.history.pushState(b, "", c) : o.history.replaceState(b, "", c);
|
|
59
|
+
} catch (n) {
|
|
60
|
+
u(n);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function A(t) {
|
|
64
|
+
var s;
|
|
65
|
+
const e = r.getState().tabs.find((a) => a.tabId === t.tabId), n = {
|
|
66
|
+
...t,
|
|
67
|
+
title: (e == null ? void 0 : e.title) ?? ((s = i.initialLocation) == null ? void 0 : s.title) ?? `${t.appId} · ${t.route}`.slice(0, 500)
|
|
68
|
+
};
|
|
69
|
+
if (!m(n)) throw new Error("URL 包含未知应用或无效页签/路由");
|
|
70
|
+
r.openTab(n);
|
|
71
|
+
}
|
|
72
|
+
let p = null;
|
|
73
|
+
try {
|
|
74
|
+
p = i.initialLocation ?? (i.url === !1 ? null : H(o.location.hash));
|
|
75
|
+
} catch (t) {
|
|
76
|
+
u(t);
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
const t = o.sessionStorage.getItem(v);
|
|
80
|
+
if (t) {
|
|
81
|
+
const e = JSON.parse(t);
|
|
82
|
+
if (!e || typeof e != "object") throw new Error("无效的页签存储");
|
|
83
|
+
const n = e;
|
|
84
|
+
if (n.version !== 1 || !Array.isArray(n.tabs) || n.tabs.length > 100)
|
|
85
|
+
throw new Error("不支持的页签存储版本或格式");
|
|
86
|
+
const s = new Set(r.getState().tabs.map((a) => a.tabId));
|
|
87
|
+
for (const a of n.tabs)
|
|
88
|
+
if (!(!m(a) || s.has(a.tabId)) && !(p && m({ ...p, title: "深链恢复" }) && p.tabId === a.tabId && (p.appId !== a.appId || p.route !== a.route)))
|
|
89
|
+
try {
|
|
90
|
+
r.openTab(U(a)), s.add(a.tabId);
|
|
91
|
+
} catch (d) {
|
|
92
|
+
u(d);
|
|
93
|
+
}
|
|
94
|
+
typeof n.activeTabId == "string" && r.activateTab(n.activeTabId);
|
|
95
|
+
}
|
|
96
|
+
} catch (t) {
|
|
97
|
+
u(t);
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
p && A(p);
|
|
101
|
+
} catch (t) {
|
|
102
|
+
u(t);
|
|
103
|
+
}
|
|
104
|
+
let l = r.getState();
|
|
105
|
+
k(l), $(l);
|
|
106
|
+
let E = !1, h = !1;
|
|
107
|
+
const K = r.subscribe((t) => {
|
|
108
|
+
const e = l.tabs.some((n) => !t.tabs.some((s) => s.key === n.key));
|
|
109
|
+
l = t, $(t), E || k(t, !e);
|
|
110
|
+
});
|
|
111
|
+
let g = 0, I, L;
|
|
112
|
+
function T(t) {
|
|
113
|
+
var d;
|
|
114
|
+
if (h) return;
|
|
115
|
+
const e = ++g;
|
|
116
|
+
I == null || I.abort();
|
|
117
|
+
const n = new AbortController();
|
|
118
|
+
I = n;
|
|
119
|
+
const s = o.location.hash, a = (d = o.history.state) == null ? void 0 : d[y];
|
|
120
|
+
E = !0, (async () => {
|
|
121
|
+
await Promise.resolve();
|
|
122
|
+
try {
|
|
123
|
+
if (h || e !== g) return;
|
|
124
|
+
const f = H(s);
|
|
125
|
+
if (!f) return;
|
|
126
|
+
const c = r.getState().tabs.find((R) => R.tabId === f.tabId), b = {
|
|
127
|
+
...f,
|
|
128
|
+
title: (c == null ? void 0 : c.title) ?? `${f.appId} · ${f.route}`.slice(0, 500)
|
|
129
|
+
};
|
|
130
|
+
if (!m(b)) throw new Error("URL 包含未知应用或无效页签/路由");
|
|
131
|
+
if (t) {
|
|
132
|
+
if ((a == null ? void 0 : a.session) !== S || !c || a.key !== c.key || c.appId !== b.appId || c.route !== b.route)
|
|
133
|
+
return;
|
|
134
|
+
await r.operate({
|
|
135
|
+
type: "activate",
|
|
136
|
+
target: { tabId: c.tabId, key: c.key },
|
|
137
|
+
signal: n.signal
|
|
138
|
+
});
|
|
139
|
+
} else
|
|
140
|
+
await r.operate({ type: "open", input: b, signal: n.signal });
|
|
141
|
+
} catch (f) {
|
|
142
|
+
!h && e === g && u(f);
|
|
143
|
+
} finally {
|
|
144
|
+
!h && e === g && (E = !1, l = r.getState(), k(l), $(l));
|
|
145
|
+
}
|
|
146
|
+
})();
|
|
147
|
+
}
|
|
148
|
+
const C = () => {
|
|
149
|
+
L = o.location.hash, T(!0);
|
|
150
|
+
}, M = (t) => {
|
|
151
|
+
const e = t.newURL, n = e ? new URL(e).hash : o.location.hash;
|
|
152
|
+
if (L === n) {
|
|
153
|
+
L = void 0;
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
L = void 0, T(!1);
|
|
157
|
+
};
|
|
158
|
+
return i.url !== !1 && (o.addEventListener("popstate", C), o.addEventListener("hashchange", M)), {
|
|
159
|
+
/** 幂等移除本连接器的宿主订阅和浏览器事件;不清空存储、不关闭页签、不销毁宿主。 */
|
|
160
|
+
dispose() {
|
|
161
|
+
h || (h = !0, g++, I == null || I.abort(), K(), i.url !== !1 && (o.removeEventListener("popstate", C), o.removeEventListener("hashchange", M)));
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
export {
|
|
166
|
+
J as connectMicroBrowser,
|
|
167
|
+
H as decodeMicroLocation,
|
|
168
|
+
x as encodeMicroLocation
|
|
169
|
+
};
|
package/dist/core.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("./sdk.cjs");function Ge(Le,R={}){var Oe;const X=new Map,ye=new Map,z=e=>typeof e=="string"&&!!e.trim()&&!/[\u0000-\u001f\u007f]/.test(e),fe=e=>z(e)&&e.startsWith("/")&&!e.startsWith("//")&&!e.includes("\\"),pe=e=>e.split(/[?#]/,1)[0];for(const e of Le){let a=function(r){if(!Array.isArray(r))throw new Error("menus 必须为数组");return Object.freeze(r.map(i=>{if(!i||!z(i.menuId)||!z(i.name)||c.has(i.menuId))throw new Error("菜单标识/名称无效或菜单标识重复");if(c.add(i.menuId),i.children!==void 0){if(i.target!==void 0||!Array.isArray(i.children)||!i.children.length)throw new Error("菜单分组需要非空 children 且不能配置 target");return Object.freeze({menuId:i.menuId,name:i.name,children:a(i.children)})}if(!i.target||!z(i.target.tabId)||!fe(i.target.route))throw new Error("菜单叶节点需要有效 target");return I.add(i.menuId),b.push({pathPrefix:pe(i.target.route),menuId:i.menuId}),Object.freeze({menuId:i.menuId,name:i.name,target:Object.freeze({tabId:i.target.tabId,route:i.target.route})})}))};if(X.has(e.appId))throw new Error(`重复的 appId: ${e.appId}`);if(e.kind==="local"){if(e.entry!==void 0)throw new Error("本地应用不能配置 entry")}else{if(e.kind!==void 0&&e.kind!=="remote")throw new Error("未知应用类型");const r=new URL(e.entry);if(!["http:","https:"].includes(r.protocol))throw new Error("子应用地址必须使用 HTTP(S)")}const c=new Set,I=new Set,b=[],h=e.menus===void 0?void 0:a(e.menus);if(e.menuRoutes!==void 0&&!Array.isArray(e.menuRoutes))throw new Error("menuRoutes 必须为数组");const o=(Oe=e.menuRoutes)==null?void 0:Oe.map(r=>{if(!r||!fe(r.pathPrefix)||/[?#]/.test(r.pathPrefix)||!I.has(r.menuId))throw new Error("菜单归属规则需要有效路径与本应用可导航叶节点");return Object.freeze({pathPrefix:r.pathPrefix,menuId:r.menuId})});b.push(...(o??[]).map(r=>({...r})));const t=new Map;for(const r of b){if(r.pathPrefix=r.pathPrefix.replace(/\/+$/,"")||"/",t.has(r.pathPrefix)&&t.get(r.pathPrefix)!==r.menuId)throw new Error("菜单归属前缀冲突");t.set(r.pathPrefix,r.menuId)}ye.set(e.appId,{leaves:I,routes:b.sort((r,i)=>i.pathPrefix.length-r.pathPrefix.length)}),X.set(e.appId,Object.freeze({appId:e.appId,name:e.name,...e.kind==="local"?{kind:"local"}:{...e.kind?{kind:e.kind}:{},entry:e.entry},...h?{menus:h}:{},...o?{menuRoutes:Object.freeze(o)}:{}}))}const ae=u.createNotificationBus(),Re=Object.freeze({source:"host"}),de={on(e,a){return ae.on(e,({payload:c,metadata:I})=>U(()=>a(c,I)))},emit(e,a){ae.emit(e,{payload:a,metadata:Re})},clear:()=>ae.clear()},se=R.callTimeoutMs??3e4;if(!Number.isFinite(se)||se<=0||se>2147483647)throw new Error("callTimeoutMs 必须为不超过2147483647的正有限数");const Z=new Set;function we(e,a){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"))throw new Error("会话快照无效");return Object.freeze({authenticated:e.authenticated,permissions:Object.freeze([...e.permissions]),...e.user?{user:Object.freeze({id:e.user.id,...e.user.name!==void 0?{name:e.user.name}:{}})}:{},revision:a})}let K=we(R.session??{authenticated:!1,permissions:[]},0);function ze(e){D(),K=we(e,K.revision+1);const a=K;for(const c of[...Z]){if(a!==K)break;Z.has(c)&&U(()=>c(a))}return a}const ee=new Set,te=new Map,ve=new Map(Object.entries(R.capabilities??{}).filter(([,e])=>e!==void 0).map(([e,a])=>{const c=a;return[e,{handler:c.handler,apps:c.apps?new Set(c.apps):void 0}]})),V=new Map,B=new Map;let d={tabs:[],activeTabId:null},le=0,P=!1,G=0,Ee=Promise.resolve();const re=new Map,W=new Map,xe=e=>{var a;try{(a=R.onError)==null||a.call(R,e)}catch{}},U=e=>{try{e()}catch(a){xe(a)}},D=()=>{if(P)throw new Error("MicroHost 已销毁")},ie=()=>({tabs:[...d.tabs],activeTabId:d.activeTabId}),oe=()=>{G++;for(const e of[...ee])ee.has(e)&&U(()=>e(ie()))};function ke(e){return D(),ee.add(e),()=>{ee.delete(e)}}const be=R.canNavigate,ge=R.canOpen;function q(e){if(!ge)return;const a=Object.freeze({appId:e.appId,tabId:e.tabId,title:e.title,route:e.route,...e.activeMenuId!==void 0?{activeMenuId:e.activeMenuId}:{}});try{const c=ge(a);if(c===!0)return;c&&(typeof c=="object"||typeof c=="function")&&Promise.resolve(c).catch(()=>{})}catch{}throw new u.NavigationError("FORBIDDEN","无权访问该页面(权限策略必须同步返回 true)")}function ue(e,a=!1){var o;if(D(),!e||!z(e.title)||e.title.length>500)throw new Error("页签标题无效");if(!X.has(e.appId))throw new Error(`未知子应用: ${e.appId}`);if(!z(e.tabId)||!fe(e.route))throw new Error("需要 tabId 与应用内绝对路由");const c=ye.get(e.appId);if(e.activeMenuId!==void 0&&!c.leaves.has(e.activeMenuId))throw new Error("activeMenuId 必须指向本应用可导航叶节点");const I=pe(e.route),b=e.activeMenuId??((o=c.routes.find(t=>t.pathPrefix==="/"||I===t.pathPrefix||I.startsWith(`${t.pathPrefix}/`)))==null?void 0:o.menuId);let h=d.tabs.find(t=>t.tabId===e.tabId);if(h&&(h.appId!==e.appId||h.route!==e.route&&!a))throw new Error("tabId 已绑定到其他页面");return h&&h.route!==e.route&&(h=void 0),q(h??{...e,activeMenuId:b}),h||(h=Object.freeze({appId:e.appId,tabId:e.tabId,title:e.title,route:e.route,...b!==void 0?{activeMenuId:b}:{},key:`${e.tabId}:${++le}`})),h}function He(e){const a=ue(e);return d={tabs:d.tabs.includes(a)?d.tabs:[...d.tabs,a],activeTabId:a.tabId},oe(),a}function _e(e,a,c){if(D(),typeof a.title!="string"||!a.title.trim()||a.title.length>500||/[\u0000-\u001f\u007f]/.test(a.title))throw new Error("页签标题无效");const I=d.tabs.find(h=>h.tabId===e&&(!c||h.key===c));if(!I)return;const b=Object.freeze({...I,title:a.title});return d={...d,tabs:d.tabs.map(h=>h===I?b:h)},oe(),b}function Ce(e){D();const a=d.tabs.find(c=>c.tabId===e);return a?(q(a),d={...d,activeTabId:e},oe(),!0):!1}function Fe(e,a){var h;D();const c=d.tabs.findIndex(o=>o.tabId===e&&(!a||o.key===a));if(c<0)return!1;const I=d.tabs[c],b=d.tabs.filter(o=>o!==I);if(d={tabs:b,activeTabId:d.activeTabId===e?((h=b[Math.min(c,b.length-1)])==null?void 0:h.tabId)??null:d.activeTabId},me(I.key),!b.some(o=>o.appId===I.appId)){for(const o of[...B.get(I.appId)??[]])U(o);B.delete(I.appId),te.delete(I.appId)}return oe(),!0}function me(e){re.delete(e),W.delete(e);for(const a of[...V.get(e)??[]])U(a);V.delete(e)}function $e(e){return Y(e)}function Y(e,a,c,I=!1,b,h=!1){var o;try{if(P)throw new u.NavigationError("HOST_DISPOSED","MicroHost 已销毁");if(!e||!["open","activate","refresh","close","closeMany","back"].includes(e.type))throw new u.NavigationError("INVALID_OPERATION","导航命令无效");const t={...e,..."target"in e&&e.target?{target:{...e.target}}:{},..."anchor"in e&&e.anchor?{anchor:{...e.anchor}}:{}},r=n=>!!n&&z(n.tabId)&&(n.key===void 0||z(n.key));if(["activate","refresh","close"].includes(t.type)&&(!("target"in t)||!r(t.target)))throw new u.NavigationError("INVALID_OPERATION","导航需要合法目标引用");if("target"in t&&t.target!==void 0&&!r(t.target)||"anchor"in t&&t.anchor!==void 0&&!r(t.anchor))throw new u.NavigationError("INVALID_OPERATION","导航引用无效");if(t.type==="closeMany"&&t.exclude!==void 0){if(!Array.isArray(t.exclude)||!t.exclude.every(r))throw new u.NavigationError("INVALID_OPERATION","批量排除引用无效");t.exclude=Object.freeze(t.exclude.map(n=>{var m;return Object.freeze({tabId:n.tabId,...n.key!==void 0?{key:n.key}:{key:(m=d.tabs.find(v=>v.tabId===n.tabId))==null?void 0:m.key}})}))}if(t.type==="open"){const n=t.input;if(!n)throw new u.NavigationError("INVALID_OPERATION","导航目标无效");t.input=Object.freeze({appId:n.appId,tabId:n.tabId,title:n.title,route:n.route,...n.activeMenuId!==void 0?{activeMenuId:n.activeMenuId}:{}})}if("closeFallback"in t&&t.closeFallback!==void 0){if(!r(t.closeFallback))throw new u.NavigationError("INVALID_OPERATION","关闭回落引用无效");const n=t.closeFallback;t.closeFallback=Object.freeze({tabId:n.tabId,key:n.key??((o=d.tabs.find(m=>m.tabId===n.tabId))==null?void 0:o.key)})}"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);const i=n=>d.tabs.find(m=>n?m.tabId===n.tabId&&(!n.key||m.key===n.key):m.tabId===d.activeTabId),w="target"in t?i(t.target):void 0,E=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,y=h&&c?d.tabs.find(n=>n.key===c):void 0,k=y&&a?Object.freeze({appId:a,tab:Object.freeze({...y})}):void 0,x=h&&t.type==="open"&&!!a&&t.input.appId!==a,j=()=>{if(!(!x||t.type!=="open")){try{const n=k&&(be==null?void 0:be(k,t.input));if(n===!0)return;n&&(typeof n=="object"||typeof n=="function")&&Promise.resolve(n).catch(()=>{})}catch{}throw new u.NavigationError("FORBIDDEN","无权跨应用导航(权限策略必须同步返回 true)")}},H=n=>!a||n.appId===a;if(w&&!H(w)||E&&!H(E)||t.type==="open"&&a&&t.input.appId!==a&&!x||t.type==="back"&&t.fallback&&a&&t.fallback.appId!==a)throw new u.NavigationError("FORBIDDEN","页面只能操作本应用");if(t.type==="closeMany"&&!["all","others","left","right"].includes(t.scope))throw new u.NavigationError("INVALID_OPERATION","批量范围无效");const _=E?d.tabs.indexOf(E):-1,J=t.type==="closeMany"?d.tabs.filter((n,m)=>{var v;return H(n)&&!((v=t.exclude)!=null&&v.some(F=>F.tabId===n.tabId&&F.key===n.key))&&(t.scope==="all"||_>=0&&(t.scope==="others"?n.key!==E.key:t.scope==="left"?m<_:m>_))}):[],g=t.type==="back"?w??(!("target"in t)||!t.target?S:void 0):w,C=async()=>{var Ne,Ae,Te,Pe,Me,De;const n=s=>({status:s,affected:[]});if(P)throw new u.NavigationError("HOST_DISPOSED","MicroHost 已销毁");if((Ne=t.signal)!=null&&Ne.aborted)return n("stale");if(c&&!I&&(b!=null&&b()||!d.tabs.some(s=>s.key===c)))throw new u.NavigationError("PAGE_RELEASED","页面上下文已释放");if(t.type==="open"&&(f?!d.tabs.some(s=>s.key===f.key):d.tabs.some(s=>s.tabId===t.input.tabId))||g&&!d.tabs.some(s=>s.key===g.key)||E&&!d.tabs.some(s=>s.key===E.key)||J.some(s=>!d.tabs.some(O=>O.key===s.key)))return n("stale");if(t.type!=="open"&&t.type!=="closeMany"&&!g)return n("noop");const m=G;if(j(),c&&!I&&(b!=null&&b()||!d.tabs.some(s=>s.key===c)))throw new u.NavigationError("PAGE_RELEASED","页面上下文已释放");if(G!==m||P||(Ae=t.signal)!=null&&Ae.aborted)return n("stale");const v=d,F=G;let p=[...d.tabs],l,M=[],$=d.activeTabId;try{if(t.type==="open"&&(l=ue(t.input,!0),f&&f.key!==l.key?(M=[f],p=p.map(s=>s.key===f.key?l:s)):p.some(s=>s.key===l.key)||p.push(l),$=l.tabId),t.type==="activate"&&(l=g,q(l),$=l.tabId),t.type==="refresh"&&(q(g),M=[g],l=Object.freeze({...g,key:`${g.tabId}:${++le}`}),p=p.map(s=>s.key===g.key?l:s)),(t.type==="close"||t.type==="back")&&(M=[g]),t.type==="closeMany"&&(M=J),M.length&&t.type!=="refresh"&&t.type!=="open"){const s=new Set(M.map(N=>N.key)),O=v.tabs.findIndex(N=>N.tabId===v.activeTabId);if(p=p.filter(N=>!s.has(N.key)),t.type==="back"){const N=W.get(g.key);l=p.find(T=>T.key===N&&H(T)),!l&&t.fallback&&(l=ue(t.fallback),s.has(l.key)&&(l=Object.freeze({...l,key:`${l.tabId}:${++le}`})),p.some(T=>T.key===l.key)||p.push(l));const A=v.tabs.filter(T=>H(T)),L=A.findIndex(T=>T.key===g.key);l??(l=A.slice(0,L).reverse().find(T=>p.some(ne=>ne.key===T.key))??A.slice(L+1).find(T=>p.some(ne=>ne.key===T.key))),l??(l=p[Math.min(O,p.length-1)]),l&&q(l),$=(l==null?void 0:l.tabId)??null}else if(M.some(N=>N.tabId===$)){const N="closeFallback"in t?t.closeFallback:void 0,A=N?p.find(L=>L.tabId===N.tabId&&L.key===N.key&&H(L)):void 0;A&&(q(A),l=A),$=((Te=A??p[Math.min(O,p.length-1)])==null?void 0:Te.tabId)??null}}}catch(s){throw s instanceof u.NavigationError?s:new u.NavigationError("INVALID_OPERATION","导航参数无效")}if(p.length===v.tabs.length&&p.every((s,O)=>s.key===v.tabs[O].key)&&$===v.activeTabId)return n("noop");const he=[...M],ce=v.tabs.find(s=>s.tabId===v.activeTabId);ce&&ce.tabId!==$&&!he.some(s=>s.key===ce.key)&&he.push(ce);for(const s of he)for(const O of[...re.get(s.key)??[]]){let N;const A=t.signal;let L=()=>{};try{const T=Promise.resolve(O({operation:t,tab:s})),ne=new Promise(Se=>{if(!A)return;const je=()=>Se();A.addEventListener("abort",je,{once:!0}),L=()=>A.removeEventListener("abort",je),A.aborted&&Se()});N=await Promise.race([T,ne])}catch{if(A!=null&&A.aborted)return n("stale");throw new u.NavigationError("GUARD_FAILED","离页守卫执行失败")}finally{L()}if(G!==F||P||(Pe=t.signal)!=null&&Pe.aborted)return n("stale");if(N===!1)return n("cancelled")}if(G!==F||P||(Me=t.signal)!=null&&Me.aborted)return n("stale");if(b!=null&&b())throw new u.NavigationError("PAGE_RELEASED","页面上下文已释放");if(j(),l&&q(l),b!=null&&b())throw new u.NavigationError("PAGE_RELEASED","页面上下文已释放");if(G!==F||P||(De=t.signal)!=null&&De.aborted)return n("stale");if(d={tabs:p,activeTabId:$},t.type==="open"&&l&&c&&!x&&l.key!==c&&!W.has(l.key)&&W.set(l.key,c),t.type==="refresh"&&l){const s=W.get(g.key);s&&W.set(l.key,s)}for(const s of M)me(s.key);for(const s of new Set(M.map(O=>O.appId)))if(!p.some(O=>O.appId===s)){for(const O of[...B.get(s)??[]])U(O);B.delete(s),te.delete(s)}const Ie=[...M,...p.filter(s=>!v.tabs.some(O=>O.key===s.key))];return!Ie.length&&l&&Ie.push(l),oe(),{status:"committed",affected:Ie.map(s=>({tabId:s.tabId,key:s.key})),...l?{tab:l}:{}}},Q=Ee.then(C);return Ee=Q.catch(()=>{}),Q}catch(t){return Promise.reject(t)}}function Ve(e){if(D(),!X.has(e))throw new Error(`未知子应用: ${e}`);const a=te.get(e);if(a)return a;let c=!0;const I=new Set([()=>{c=!1}]);B.set(e,I);async function b(o,t,r,i={}){const w=()=>{if(P)throw new u.HostCallError("HOST_DISPOSED","MicroHost 已销毁");if(r&&(r.released()||!d.tabs.some(j=>j.key===r.key)))throw new u.HostCallError("PAGE_RELEASED","页面上下文已释放");if(!c)throw new u.HostCallError("BRIDGE_RELEASED","应用 bridge 已释放")};w();const E=ve.get(o);if(!E)throw new u.HostCallError("UNKNOWN_CAPABILITY",`未注册宿主能力: ${o}`);if(E.apps&&!E.apps.has(e))throw new u.HostCallError("FORBIDDEN",`应用无权调用宿主能力: ${o}`);const S=i.timeoutMs??se;if(!Number.isFinite(S)||S<=0||S>2147483647)throw new TypeError("timeoutMs 必须为不超过2147483647的正有限数");const f=i.signal;if(f!=null&&f.aborted)throw new u.HostCallError("CANCELLED","宿主能力调用已取消");const y=new AbortController,k=r?d.tabs.find(j=>j.key===r.key):void 0,x=Object.freeze({appId:e,signal:y.signal,...k?{tab:Object.freeze({...k})}:{},isActive:()=>!y.signal.aborted&&!P&&c&&(!r||!r.released()&&d.tabs.some(j=>j.key===r.key))});return new Promise((j,H)=>{let _=!1,J;const g=()=>{J!==void 0&&clearTimeout(J),f==null||f.removeEventListener("abort",Q),I.delete(n),r==null||r.cleanups.delete(n)},C=v=>{_||(_=!0,g(),H(v),y.abort())},Q=()=>C(new u.HostCallError("CANCELLED","宿主能力调用已取消")),n=()=>{try{w()}catch(v){C(v)}};if(I.add(n),r==null||r.cleanups.add(n),f==null||f.addEventListener("abort",Q,{once:!0}),J=setTimeout(()=>C(new u.HostCallError("TIMEOUT","宿主能力调用超时")),S),f!=null&&f.aborted){Q();return}let m;try{m=E.handler(x,...t)}catch{C(new u.HostCallError("HANDLER_FAILED",`宿主能力执行失败: ${o}`));return}Promise.resolve(m).then(v=>{if(!_){try{w()}catch(F){C(F);return}_=!0,g(),j(v)}},()=>C(new u.HostCallError("HANDLER_FAILED",`宿主能力执行失败: ${o}`)))})}const h={appId:e,getState:ie,callHost(o,...t){return b(o,t)},callHostWithOptions(o,t,...r){return b(o,r,void 0,t)},protocol(){if(D(),!c)throw new Error("应用 bridge 已释放");return Object.freeze({version:"1.0",capabilities:Object.freeze([...ve].filter(([,o])=>!o.apps||o.apps.has(e)).map(([o])=>o)),features:Object.freeze(["call-options","session","event-metadata","navigate-to"])})},negotiate(o={}){const t=h.protocol(),r=Object.freeze((o.capabilities??[]).filter(i=>!t.capabilities.includes(i)));return Object.freeze({compatible:(!o.version||o.version===t.version)&&!r.length,protocol:t,missingCapabilities:r})},getSession(){if(D(),!c)throw new Error("应用 bridge 已释放");return K},subscribeSession(o){const t=h.getSession(),r=w=>o(w),i=()=>{Z.delete(r),I.delete(i)};return Z.add(r),I.add(i),U(()=>o(t)),i},subscribe(o){if(!c)throw new Error("应用 bridge 已释放");const t=ke(o),r=()=>{t(),I.delete(r)};return I.add(r),r},ready(o){const t=i=>{const w=i.tabs.find(E=>E.tabId===i.activeTabId&&E.appId===e);w&&o(w)},r=h.subscribe(t);try{t(ie())}catch(i){throw r(),i}return r},bindPage(o){const t=d.tabs.find(r=>r.tabId===o&&r.appId===e);if(!t)throw new Error("页面不属于该应用");return h.page(t.key)},page(o){if(D(),!c)throw new Error("应用 bridge 已释放");const t=d.tabs.find(f=>f.key===o&&f.appId===e);if(!t)throw new Error("页面上下文不存在或不属于该应用");let r=!1;const i=new Set,w=()=>{if(P)throw new u.NavigationError("HOST_DISPOSED","MicroHost 已销毁");if(r||!d.tabs.some(f=>f.key===o))throw new u.NavigationError("PAGE_RELEASED","页面上下文已释放")},E=()=>{var f;r=!0,i.forEach(y=>y()),i.clear(),(f=V.get(o))==null||f.delete(E)};let S=V.get(o);return S||V.set(o,S=new Set),S.add(E),{get tab(){return d.tabs.find(f=>f.key===o)??t},callHost(f,...y){return b(f,y,{key:o,released:()=>r,cleanups:i})},callHostWithOptions(f,y,...k){return b(f,k,{key:o,released:()=>r,cleanups:i},y)},async openTab(f){w();const y=await Y({type:"open",input:f},e,o,!1,()=>r);if(y.tab)return y.tab;const k=d.tabs.find(x=>x.tabId===f.tabId&&x.appId===e);if(y.status==="noop"&&k)return k;throw new u.NavigationError(y.status==="cancelled"?"CANCELLED":"STALE","导航未提交")},async navigateTo(f,y={}){return w(),Y({type:"open",input:f,signal:y.signal},e,o,!1,()=>r,!0)},async closeCurrent(){return P?!1:(await Y({type:"close",target:{tabId:t.tabId,key:o}},e,void 0,!0)).status==="committed"},beforeLeave(f){if(w(),typeof f!="function")throw new u.NavigationError("INVALID_OPERATION","离页守卫必须为函数");let y=re.get(o);y||re.set(o,y=new Set),y.add(f);const k=()=>{y.delete(f),i.delete(k)};return i.add(k),k},async refreshCurrent(){return w(),Y({type:"refresh",target:{tabId:t.tabId,key:o}},e,o,!1,()=>r)},async back(f){return w(),Y({type:"back",target:{tabId:t.tabId,key:o},fallback:f},e,o,!1,()=>r)},async operate(f){return w(),Y(f,e,o,!1,()=>r)},emit(f,y){w(),ae.emit(f,{payload:y,metadata:Object.freeze({source:"app",appId:e,tabKey:o})})},on(f,y){w();const k=de.on(f,y);return i.add(k),()=>{k(),i.delete(k)}},dispose:E}}};return te.set(e,h),h}function Be(){if(!P){P=!0;for(const e of V.values())[...e].forEach(a=>a());V.clear(),re.clear(),W.clear();for(const e of B.values())[...e].forEach(a=>a());B.clear(),ee.clear(),Z.clear(),te.clear(),de.clear(),d={tabs:[],activeTabId:null}}}return{apps:[...X.values()],getState:ie,subscribe:ke,openTab:He,activateTab:Ce,closeTab:Fe,updateTab:_e,updateSession:ze,operate:$e,bridge:Ve,bus:de,dispose:Be}}exports.createMicroHost=Ge;
|