@axewo/fin-micro 1.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/README.md +181 -4
  2. package/cli/index.mjs +22 -6
  3. package/dist/browser.cjs +1 -1
  4. package/dist/browser.mjs +68 -66
  5. package/dist/core.cjs +1 -1
  6. package/dist/core.mjs +459 -441
  7. package/dist/index.cjs +1 -1
  8. package/dist/index.mjs +31 -24
  9. package/dist/layout.cjs +1 -0
  10. package/dist/layout.css +1 -0
  11. package/dist/layout.mjs +719 -0
  12. package/dist/{router-view-impl-C1KG6mxu.mjs → router-view-impl-B1NzCGKV.mjs} +18 -18
  13. package/dist/router-view-impl-BvOToV_Z.cjs +1 -0
  14. package/dist/runtime.cjs +1 -1
  15. package/dist/runtime.mjs +40 -39
  16. package/dist/sdk.cjs +1 -1
  17. package/dist/sdk.mjs +92 -56
  18. package/dist/types/browser/index.d.ts +4 -0
  19. package/dist/types/core/index.d.ts +1 -1
  20. package/dist/types/index.d.ts +2 -0
  21. package/dist/types/layout/HostShell.vue.d.ts +528 -0
  22. package/dist/types/layout/ShellHeader.vue.d.ts +46 -0
  23. package/dist/types/layout/ShellMenu.vue.d.ts +12 -0
  24. package/dist/types/layout/ShellTabs.vue.d.ts +23 -0
  25. package/dist/types/layout/StandaloneMenuItem.vue.d.ts +11 -0
  26. package/dist/types/layout/StandaloneShell.vue.d.ts +40 -0
  27. package/dist/types/layout/index.d.ts +5 -0
  28. package/dist/types/layout/types.d.ts +65 -0
  29. package/dist/types/runtime/lifecycle.d.ts +1 -1
  30. package/dist/types/sdk/index.d.ts +65 -4
  31. package/dist/types/vue/index.d.ts +13 -2
  32. package/dist/types/vue/router-view-impl.d.ts +1 -1
  33. package/dist/types/watermark/FinWatermark.vue.d.ts +16 -0
  34. package/dist/types/watermark/config.d.ts +36 -0
  35. package/dist/types/watermark/index.d.ts +4 -0
  36. package/dist/vue.cjs +1 -1
  37. package/dist/vue.mjs +49 -35
  38. package/dist/watermark.cjs +2 -0
  39. package/dist/watermark.mjs +119 -0
  40. package/package.json +29 -4
  41. package/scaffold/base/index.html +2 -1
  42. package/scaffold/base/package.json.template +5 -1
  43. package/scaffold/base/pnpm-workspace.yaml +6 -0
  44. package/scaffold/base/scripts/check-runtime.mjs +43 -0
  45. package/scaffold/base/src/locales/en.ts +12 -0
  46. package/scaffold/base/src/locales/index.ts +7 -1
  47. package/scaffold/base/src/locales/zh-CN.ts +12 -0
  48. package/scaffold/base/src/main.ts +13 -5
  49. package/scaffold/base/src/router/demo.ts +6 -0
  50. package/scaffold/base/src/router/index.ts +13 -0
  51. package/scaffold/base/src/stores/demo.ts +12 -0
  52. package/scaffold/base/src/stores/index.ts +11 -0
  53. package/scaffold/base/src/utils/appConfig.ts +49 -0
  54. package/scaffold/base/src/utils/errorUtils.ts +109 -0
  55. package/scaffold/base/src/utils/requestUtils.ts +32 -11
  56. package/scaffold/base/src/utils/watermarkConfig.ts +18 -0
  57. package/scaffold/base/src/views/demo/index.vue +26 -0
  58. package/scaffold/host/.env.example +11 -3
  59. package/scaffold/host/src/App.vue +1 -13
  60. package/scaffold/host/src/bootstrap.ts +40 -8
  61. package/scaffold/host/src/hooks/usePlatformSession.ts +40 -8
  62. package/scaffold/host/src/layout/HostHeader.vue +65 -5
  63. package/scaffold/host/src/layout/HostLayout.vue +65 -21
  64. package/scaffold/host/src/layout/HostMenu.vue +11 -2
  65. package/scaffold/host/src/layout/HostTabs.vue +91 -8
  66. package/scaffold/host/src/layout/HostWorkspace.vue +29 -7
  67. package/scaffold/host/src/localPages.ts +45 -0
  68. package/scaffold/host/src/platform.ts +51 -14
  69. package/scaffold/host/src/router/guards.ts +128 -0
  70. package/scaffold/host/src/router/index.ts +25 -0
  71. package/scaffold/host/src/router/targets.ts +30 -0
  72. package/scaffold/host/src/router/workspace.ts +63 -0
  73. package/scaffold/host/src/styles/shell.css +776 -0
  74. package/scaffold/host/src/utils/request.ts +30 -3
  75. package/scaffold/host/src/views/home/index.vue +19 -0
  76. package/scaffold/host/src/views/login/components/LoginForm.vue +83 -0
  77. package/scaffold/host/src/views/login/index.vue +16 -0
  78. package/scaffold/host/src/views/not-found/index.vue +1 -0
  79. package/scaffold/host/src/views/profile/index.vue +24 -0
  80. package/scaffold/host/src/views/workspace/index.vue +11 -0
  81. package/scaffold/layouts/standard/host/src/layout/HostLayout.vue +116 -0
  82. package/scaffold/layouts/standard/host/src/styles/shell.css +168 -0
  83. package/scaffold/layouts/standard/vue/src/layout/StandaloneLayout.vue +26 -0
  84. package/scaffold/vue/src/App.vue +13 -7
  85. package/scaffold/vue/src/bootstrap.ts +25 -24
  86. package/scaffold/vue/src/bridge/auth.ts +23 -0
  87. package/scaffold/vue/src/bridge/session.ts +2 -2
  88. package/scaffold/vue/src/layout/Header.vue +15 -0
  89. package/scaffold/vue/src/layout/Sidebar.vue +21 -0
  90. package/scaffold/vue/src/layout/SidebarItem.vue +21 -0
  91. package/scaffold/vue/src/layout/StandaloneLayout.vue +34 -0
  92. package/scaffold/vue/src/main.ts +5 -2
  93. package/scaffold/vue/src/router/index.ts +9 -4
  94. package/scaffold/vue/src/router/menu.ts +38 -0
  95. package/scaffold/vue/src/views/demo/configs/table.ts +24 -0
  96. package/scaffold/vue/src/views/demo/hooks/useRecordNavigation.ts +32 -0
  97. package/scaffold/vue/src/views/demo/index.vue +17 -0
  98. package/scaffold/vue/src/views/{DetailView.vue → demo/modules/detail/index.vue} +15 -6
  99. package/scaffold/vue/src/views/demo/types.ts +5 -0
  100. package/dist/router-view-impl-CgLEalx4.cjs +0 -1
  101. package/scaffold/host/src/views/LoginView.vue +0 -38
  102. package/scaffold/vue/src/views/ListView.vue +0 -51
  103. /package/scaffold/vue/src/views/{NotFoundView.vue → not-found/index.vue} +0 -0
package/dist/core.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const y=require("./sdk.cjs");function Ge(je,z={}){var me;const q=new Map,ye=new Map,R=t=>typeof t=="string"&&!!t.trim()&&!/[\u0000-\u001f\u007f]/.test(t),ce=t=>R(t)&&t.startsWith("/")&&!t.startsWith("//")&&!t.includes("\\"),pe=t=>t.split(/[?#]/,1)[0];for(const t of je){let r=function(c){if(!Array.isArray(c))throw new Error("menus 必须为数组");return Object.freeze(c.map(i=>{if(!i||!R(i.menuId)||!R(i.name)||p.has(i.menuId))throw new Error("菜单标识/名称无效或菜单标识重复");if(p.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:r(i.children)})}if(!i.target||!R(i.target.tabId)||!ce(i.target.route))throw new Error("菜单叶节点需要有效 target");return h.add(i.menuId),u.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(q.has(t.appId))throw new Error(`重复的 appId: ${t.appId}`);const s=new URL(t.entry);if(!["http:","https:"].includes(s.protocol))throw new Error("子应用地址必须使用 HTTP(S)");const p=new Set,h=new Set,u=[],e=t.menus===void 0?void 0:r(t.menus);if(t.menuRoutes!==void 0&&!Array.isArray(t.menuRoutes))throw new Error("menuRoutes 必须为数组");const l=(me=t.menuRoutes)==null?void 0:me.map(c=>{if(!c||!ce(c.pathPrefix)||/[?#]/.test(c.pathPrefix)||!h.has(c.menuId))throw new Error("菜单归属规则需要有效路径与本应用可导航叶节点");return Object.freeze({pathPrefix:c.pathPrefix,menuId:c.menuId})});u.push(...(l??[]).map(c=>({...c})));const a=new Map;for(const c of u){if(c.pathPrefix=c.pathPrefix.replace(/\/+$/,"")||"/",a.has(c.pathPrefix)&&a.get(c.pathPrefix)!==c.menuId)throw new Error("菜单归属前缀冲突");a.set(c.pathPrefix,c.menuId)}ye.set(t.appId,{leaves:h,routes:u.sort((c,i)=>i.pathPrefix.length-c.pathPrefix.length)}),q.set(t.appId,Object.freeze({appId:t.appId,name:t.name,entry:t.entry,...e?{menus:e}:{},...l?{menuRoutes:Object.freeze(l)}:{}}))}const oe=y.createNotificationBus(),Le=Object.freeze({source:"host"}),fe={on(t,r){return oe.on(t,({payload:s,metadata:p})=>B(()=>r(s,p)))},emit(t,r){oe.emit(t,{payload:r,metadata:Le})},clear:()=>oe.clear()},ne=z.callTimeoutMs??3e4;if(!Number.isFinite(ne)||ne<=0||ne>2147483647)throw new Error("callTimeoutMs 必须为不超过2147483647的正有限数");const Y=new Set;function Ie(t,r){if(!t||typeof t.authenticated!="boolean"||!Array.isArray(t.permissions)||!t.permissions.every(s=>typeof s=="string")||t.user!==void 0&&(!t.user||typeof t.user.id!="string"||t.user.name!==void 0&&typeof t.user.name!="string"))throw new Error("会话快照无效");return Object.freeze({authenticated:t.authenticated,permissions:Object.freeze([...t.permissions]),...t.user?{user:Object.freeze({id:t.user.id,...t.user.name!==void 0?{name:t.user.name}:{}})}:{},revision:r})}let J=Ie(z.session??{authenticated:!1,permissions:[]},0);function xe(t){L(),J=Ie(t,J.revision+1);const r=J;for(const s of[...Y]){if(r!==J)break;Y.has(s)&&B(()=>s(r))}return r}const Q=new Set,X=new Map,we=new Map(Object.entries(z.capabilities??{}).filter(([,t])=>t!==void 0).map(([t,r])=>{const s=r;return[t,{handler:s.handler,apps:s.apps?new Set(s.apps):void 0}]})),C=new Map,_=new Map;let f={tabs:[],activeTabId:null},de=0,N=!1,$=0,ve=Promise.resolve();const Z=new Map,F=new Map,ze=t=>{var r;try{(r=z.onError)==null||r.call(z,t)}catch{}},B=t=>{try{t()}catch(r){ze(r)}},L=()=>{if(N)throw new Error("MicroHost 已销毁")},ae=()=>({tabs:[...f.tabs],activeTabId:f.activeTabId}),K=()=>{$++;for(const t of[...Q])Q.has(t)&&B(()=>t(ae()))};function Ee(t){return L(),Q.add(t),()=>{Q.delete(t)}}const le=z.canNavigate,ge=z.canOpen;function U(t){if(!ge)return;const r=Object.freeze({appId:t.appId,tabId:t.tabId,title:t.title,route:t.route,...t.activeMenuId!==void 0?{activeMenuId:t.activeMenuId}:{}});try{const s=ge(r);if(s===!0)return;s&&(typeof s=="object"||typeof s=="function")&&Promise.resolve(s).catch(()=>{})}catch{}throw new y.NavigationError("FORBIDDEN","无权访问该页面(权限策略必须同步返回 true)")}function be(t,r=!1){var e;if(L(),!t||!R(t.title)||t.title.length>500)throw new Error("页签标题无效");if(!q.has(t.appId))throw new Error(`未知子应用: ${t.appId}`);if(!R(t.tabId)||!ce(t.route))throw new Error("需要 tabId 与应用内绝对路由");const s=ye.get(t.appId);if(t.activeMenuId!==void 0&&!s.leaves.has(t.activeMenuId))throw new Error("activeMenuId 必须指向本应用可导航叶节点");const p=pe(t.route),h=t.activeMenuId??((e=s.routes.find(l=>l.pathPrefix==="/"||p===l.pathPrefix||p.startsWith(`${l.pathPrefix}/`)))==null?void 0:e.menuId);let u=f.tabs.find(l=>l.tabId===t.tabId);if(u&&(u.appId!==t.appId||u.route!==t.route&&!r))throw new Error("tabId 已绑定到其他页面");return u&&u.route!==t.route&&(u=void 0),U(u??{...t,activeMenuId:h}),u||(u=Object.freeze({appId:t.appId,tabId:t.tabId,title:t.title,route:t.route,...h!==void 0?{activeMenuId:h}:{},key:`${t.tabId}:${++de}`})),u}function Re(t){const r=be(t);return f={tabs:f.tabs.includes(r)?f.tabs:[...f.tabs,r],activeTabId:r.tabId},K(),r}function He(t,r,s){if(L(),typeof r.title!="string"||!r.title.trim()||r.title.length>500||/[\u0000-\u001f\u007f]/.test(r.title))throw new Error("页签标题无效");const p=f.tabs.find(u=>u.tabId===t&&(!s||u.key===s));if(!p)return;const h=Object.freeze({...p,title:r.title});return f={...f,tabs:f.tabs.map(u=>u===p?h:u)},K(),h}function Ce(t){L();const r=f.tabs.find(s=>s.tabId===t);return r?(U(r),f={...f,activeTabId:t},K(),!0):!1}function _e(t,r){var u;L();const s=f.tabs.findIndex(e=>e.tabId===t&&(!r||e.key===r));if(s<0)return!1;const p=f.tabs[s],h=f.tabs.filter(e=>e!==p);if(f={tabs:h,activeTabId:f.activeTabId===t?((u=h[Math.min(s,h.length-1)])==null?void 0:u.tabId)??null:f.activeTabId},ke(p.key),!h.some(e=>e.appId===p.appId)){for(const e of[..._.get(p.appId)??[]])B(e);_.delete(p.appId),X.delete(p.appId)}return K(),!0}function ke(t){Z.delete(t),F.delete(t);for(const r of[...C.get(t)??[]])B(r);C.delete(t)}function $e(t){return G(t)}function G(t,r,s,p=!1,h,u=!1){try{if(N)throw new y.NavigationError("HOST_DISPOSED","MicroHost 已销毁");if(!t||!["open","activate","refresh","close","closeMany","back"].includes(t.type))throw new y.NavigationError("INVALID_OPERATION","导航命令无效");const e={...t,..."target"in t&&t.target?{target:{...t.target}}:{},..."anchor"in t&&t.anchor?{anchor:{...t.anchor}}:{}},l=o=>!!o&&R(o.tabId)&&(o.key===void 0||R(o.key));if(["activate","refresh","close"].includes(e.type)&&(!("target"in e)||!l(e.target)))throw new y.NavigationError("INVALID_OPERATION","导航需要合法目标引用");if("target"in e&&e.target!==void 0&&!l(e.target)||"anchor"in e&&e.anchor!==void 0&&!l(e.anchor))throw new y.NavigationError("INVALID_OPERATION","导航引用无效");if(e.type==="closeMany"&&e.exclude!==void 0){if(!Array.isArray(e.exclude)||!e.exclude.every(l))throw new y.NavigationError("INVALID_OPERATION","批量排除引用无效");e.exclude=Object.freeze(e.exclude.map(o=>{var k;return Object.freeze({tabId:o.tabId,...o.key!==void 0?{key:o.key}:{key:(k=f.tabs.find(g=>g.tabId===o.tabId))==null?void 0:k.key}})}))}if(e.type==="open"){const o=e.input;if(!o)throw new y.NavigationError("INVALID_OPERATION","导航目标无效");e.input=Object.freeze({appId:o.appId,tabId:o.tabId,title:o.title,route:o.route,...o.activeMenuId!==void 0?{activeMenuId:o.activeMenuId}:{}})}"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);const a=o=>f.tabs.find(k=>o?k.tabId===o.tabId&&(!o.key||k.key===o.key):k.tabId===f.activeTabId),c="target"in e?a(e.target):void 0,i=e.type==="closeMany"&&e.scope!=="all"?e.anchor?a(e.anchor):s?f.tabs.find(o=>o.key===s):a():void 0,T=a(),O=e.type==="open"?f.tabs.find(o=>o.tabId===e.input.tabId):void 0,d=u&&s?f.tabs.find(o=>o.key===s):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,V=()=>{if(!(!v||e.type!=="open")){try{const o=I&&(le==null?void 0:le(I,e.input));if(o===!0)return;o&&(typeof o=="object"||typeof o=="function")&&Promise.resolve(o).catch(()=>{})}catch{}throw new y.NavigationError("FORBIDDEN","无权跨应用导航(权限策略必须同步返回 true)")}},P=o=>!r||o.appId===r;if(c&&!P(c)||i&&!P(i)||e.type==="open"&&r&&e.input.appId!==r&&!v||e.type==="back"&&e.fallback&&r&&e.fallback.appId!==r)throw new y.NavigationError("FORBIDDEN","页面只能操作本应用");if(e.type==="closeMany"&&!["all","others","left","right"].includes(e.scope))throw new y.NavigationError("INVALID_OPERATION","批量范围无效");const ee=i?f.tabs.indexOf(i):-1,W=e.type==="closeMany"?f.tabs.filter((o,k)=>{var g;return P(o)&&!((g=e.exclude)!=null&&g.some(M=>M.tabId===o.tabId&&M.key===o.key))&&(e.scope==="all"||ee>=0&&(e.scope==="others"?o.key!==i.key:e.scope==="left"?k<ee:k>ee))}):[],E=e.type==="back"?c??(!("target"in e)||!e.target?T:void 0):c,se=async()=>{var Oe,Ae,Ne,Te,Pe,Me;const o=n=>({status:n,affected:[]});if(N)throw new y.NavigationError("HOST_DISPOSED","MicroHost 已销毁");if((Oe=e.signal)!=null&&Oe.aborted)return o("stale");if(s&&!p&&(h!=null&&h()||!f.tabs.some(n=>n.key===s)))throw new y.NavigationError("PAGE_RELEASED","页面上下文已释放");if(e.type==="open"&&(O?!f.tabs.some(n=>n.key===O.key):f.tabs.some(n=>n.tabId===e.input.tabId))||E&&!f.tabs.some(n=>n.key===E.key)||i&&!f.tabs.some(n=>n.key===i.key)||W.some(n=>!f.tabs.some(m=>m.key===n.key)))return o("stale");if(e.type!=="open"&&e.type!=="closeMany"&&!E)return o("noop");const k=$;if(V(),s&&!p&&(h!=null&&h()||!f.tabs.some(n=>n.key===s)))throw new y.NavigationError("PAGE_RELEASED","页面上下文已释放");if($!==k||N||(Ae=e.signal)!=null&&Ae.aborted)return o("stale");const g=f,M=$;let w=[...f.tabs],b,S=[],H=f.activeTabId;try{if(e.type==="open"&&(b=be(e.input,!0),O&&O.key!==b.key?(S=[O],w=w.map(n=>n.key===O.key?b:n)):w.some(n=>n.key===b.key)||w.push(b),H=b.tabId),e.type==="activate"&&(b=E,U(b),H=b.tabId),e.type==="refresh"&&(U(E),S=[E],b=Object.freeze({...E,key:`${E.tabId}:${++de}`}),w=w.map(n=>n.key===E.key?b:n)),(e.type==="close"||e.type==="back")&&(S=[E]),e.type==="closeMany"&&(S=W),S.length&&e.type!=="refresh"&&e.type!=="open"){const n=new Set(S.map(D=>D.key)),m=g.tabs.findIndex(D=>D.tabId===g.activeTabId);if(w=w.filter(D=>!n.has(D.key)),e.type==="back"){const D=F.get(E.key);b=w.find(A=>A.key===D&&P(A)),!b&&e.fallback&&(b=be(e.fallback),n.has(b.key)&&(b=Object.freeze({...b,key:`${b.tabId}:${++de}`})),w.some(A=>A.key===b.key)||w.push(b));const j=g.tabs.filter(A=>P(A)),te=j.findIndex(A=>A.key===E.key);b??(b=j.slice(0,te).reverse().find(A=>w.some(re=>re.key===A.key))??j.slice(te+1).find(A=>w.some(re=>re.key===A.key))),b??(b=w[Math.min(m,w.length-1)]),b&&U(b),H=(b==null?void 0:b.tabId)??null}else S.some(D=>D.tabId===H)&&(H=((Ne=w[Math.min(m,w.length-1)])==null?void 0:Ne.tabId)??null)}}catch(n){throw n instanceof y.NavigationError?n:new y.NavigationError("INVALID_OPERATION","导航参数无效")}if(w.length===g.tabs.length&&w.every((n,m)=>n.key===g.tabs[m].key)&&H===g.activeTabId)return o("noop");const ue=[...S],ie=g.tabs.find(n=>n.tabId===g.activeTabId);ie&&ie.tabId!==H&&!ue.some(n=>n.key===ie.key)&&ue.push(ie);for(const n of ue)for(const m of[...Z.get(n.key)??[]]){let D;const j=e.signal;let te=()=>{};try{const A=Promise.resolve(m({operation:e,tab:n})),re=new Promise(De=>{if(!j)return;const Se=()=>De();j.addEventListener("abort",Se,{once:!0}),te=()=>j.removeEventListener("abort",Se),j.aborted&&De()});D=await Promise.race([A,re])}catch{if(j!=null&&j.aborted)return o("stale");throw new y.NavigationError("GUARD_FAILED","离页守卫执行失败")}finally{te()}if($!==M||N||(Te=e.signal)!=null&&Te.aborted)return o("stale");if(D===!1)return o("cancelled")}if($!==M||N||(Pe=e.signal)!=null&&Pe.aborted)return o("stale");if(h!=null&&h())throw new y.NavigationError("PAGE_RELEASED","页面上下文已释放");if(V(),b&&U(b),h!=null&&h())throw new y.NavigationError("PAGE_RELEASED","页面上下文已释放");if($!==M||N||(Me=e.signal)!=null&&Me.aborted)return o("stale");if(f={tabs:w,activeTabId:H},e.type==="open"&&b&&s&&!v&&b.key!==s&&!F.has(b.key)&&F.set(b.key,s),e.type==="refresh"&&b){const n=F.get(E.key);n&&F.set(b.key,n)}for(const n of S)ke(n.key);for(const n of new Set(S.map(m=>m.appId)))if(!w.some(m=>m.appId===n)){for(const m of[..._.get(n)??[]])B(m);_.delete(n),X.delete(n)}const he=[...S,...w.filter(n=>!g.tabs.some(m=>m.key===n.key))];return!he.length&&b&&he.push(b),K(),{status:"committed",affected:he.map(n=>({tabId:n.tabId,key:n.key})),...b?{tab:b}:{}}},x=ve.then(se);return ve=x.catch(()=>{}),x}catch(e){return Promise.reject(e)}}function Fe(t){if(L(),!q.has(t))throw new Error(`未知子应用: ${t}`);const r=X.get(t);if(r)return r;let s=!0;const p=new Set([()=>{s=!1}]);_.set(t,p);async function h(e,l,a,c={}){const i=()=>{if(N)throw new y.HostCallError("HOST_DISPOSED","MicroHost 已销毁");if(a&&(a.released()||!f.tabs.some(P=>P.key===a.key)))throw new y.HostCallError("PAGE_RELEASED","页面上下文已释放");if(!s)throw new y.HostCallError("BRIDGE_RELEASED","应用 bridge 已释放")};i();const T=we.get(e);if(!T)throw new y.HostCallError("UNKNOWN_CAPABILITY",`未注册宿主能力: ${e}`);if(T.apps&&!T.apps.has(t))throw new y.HostCallError("FORBIDDEN",`应用无权调用宿主能力: ${e}`);const O=c.timeoutMs??ne;if(!Number.isFinite(O)||O<=0||O>2147483647)throw new TypeError("timeoutMs 必须为不超过2147483647的正有限数");const d=c.signal;if(d!=null&&d.aborted)throw new y.HostCallError("CANCELLED","宿主能力调用已取消");const I=new AbortController,v=a?f.tabs.find(P=>P.key===a.key):void 0,V=Object.freeze({appId:t,signal:I.signal,...v?{tab:Object.freeze({...v})}:{},isActive:()=>!I.signal.aborted&&!N&&s&&(!a||!a.released()&&f.tabs.some(P=>P.key===a.key))});return new Promise((P,ee)=>{let W=!1,E;const se=()=>{E!==void 0&&clearTimeout(E),d==null||d.removeEventListener("abort",o),p.delete(k),a==null||a.cleanups.delete(k)},x=M=>{W||(W=!0,se(),ee(M),I.abort())},o=()=>x(new y.HostCallError("CANCELLED","宿主能力调用已取消")),k=()=>{try{i()}catch(M){x(M)}};if(p.add(k),a==null||a.cleanups.add(k),d==null||d.addEventListener("abort",o,{once:!0}),E=setTimeout(()=>x(new y.HostCallError("TIMEOUT","宿主能力调用超时")),O),d!=null&&d.aborted){o();return}let g;try{g=T.handler(V,...l)}catch{x(new y.HostCallError("HANDLER_FAILED",`宿主能力执行失败: ${e}`));return}Promise.resolve(g).then(M=>{if(!W){try{i()}catch(w){x(w);return}W=!0,se(),P(M)}},()=>x(new y.HostCallError("HANDLER_FAILED",`宿主能力执行失败: ${e}`)))})}const u={appId:t,getState:ae,callHost(e,...l){return h(e,l)},callHostWithOptions(e,l,...a){return h(e,a,void 0,l)},protocol(){if(L(),!s)throw new Error("应用 bridge 已释放");return Object.freeze({version:"1.0",capabilities:Object.freeze([...we].filter(([,e])=>!e.apps||e.apps.has(t)).map(([e])=>e)),features:Object.freeze(["call-options","session","event-metadata","navigate-to"])})},negotiate(e={}){const l=u.protocol(),a=Object.freeze((e.capabilities??[]).filter(c=>!l.capabilities.includes(c)));return Object.freeze({compatible:(!e.version||e.version===l.version)&&!a.length,protocol:l,missingCapabilities:a})},getSession(){if(L(),!s)throw new Error("应用 bridge 已释放");return J},subscribeSession(e){const l=u.getSession(),a=i=>e(i),c=()=>{Y.delete(a),p.delete(c)};return Y.add(a),p.add(c),B(()=>e(l)),c},subscribe(e){if(!s)throw new Error("应用 bridge 已释放");const l=Ee(e),a=()=>{l(),p.delete(a)};return p.add(a),a},ready(e){const l=c=>{const i=c.tabs.find(T=>T.tabId===c.activeTabId&&T.appId===t);i&&e(i)},a=u.subscribe(l);try{l(ae())}catch(c){throw a(),c}return a},bindPage(e){const l=f.tabs.find(a=>a.tabId===e&&a.appId===t);if(!l)throw new Error("页面不属于该应用");return u.page(l.key)},page(e){if(L(),!s)throw new Error("应用 bridge 已释放");const l=f.tabs.find(d=>d.key===e&&d.appId===t);if(!l)throw new Error("页面上下文不存在或不属于该应用");let a=!1;const c=new Set,i=()=>{if(N)throw new y.NavigationError("HOST_DISPOSED","MicroHost 已销毁");if(a||!f.tabs.some(d=>d.key===e))throw new y.NavigationError("PAGE_RELEASED","页面上下文已释放")},T=()=>{var d;a=!0,c.forEach(I=>I()),c.clear(),(d=C.get(e))==null||d.delete(T)};let O=C.get(e);return O||C.set(e,O=new Set),O.add(T),{get tab(){return f.tabs.find(d=>d.key===e)??l},callHost(d,...I){return h(d,I,{key:e,released:()=>a,cleanups:c})},callHostWithOptions(d,I,...v){return h(d,v,{key:e,released:()=>a,cleanups:c},I)},async openTab(d){i();const I=await G({type:"open",input:d},t,e,!1,()=>a);if(I.tab)return I.tab;const v=f.tabs.find(V=>V.tabId===d.tabId&&V.appId===t);if(I.status==="noop"&&v)return v;throw new y.NavigationError(I.status==="cancelled"?"CANCELLED":"STALE","导航未提交")},async navigateTo(d,I={}){return i(),G({type:"open",input:d,signal:I.signal},t,e,!1,()=>a,!0)},async closeCurrent(){return N?!1:(await G({type:"close",target:{tabId:l.tabId,key:e}},t,void 0,!0)).status==="committed"},beforeLeave(d){if(i(),typeof d!="function")throw new y.NavigationError("INVALID_OPERATION","离页守卫必须为函数");let I=Z.get(e);I||Z.set(e,I=new Set),I.add(d);const v=()=>{I.delete(d),c.delete(v)};return c.add(v),v},async refreshCurrent(){return i(),G({type:"refresh",target:{tabId:l.tabId,key:e}},t,e,!1,()=>a)},async back(d){return i(),G({type:"back",target:{tabId:l.tabId,key:e},fallback:d},t,e,!1,()=>a)},async operate(d){return i(),G(d,t,e,!1,()=>a)},emit(d,I){i(),oe.emit(d,{payload:I,metadata:Object.freeze({source:"app",appId:t,tabKey:e})})},on(d,I){i();const v=fe.on(d,I);return c.add(v),()=>{v(),c.delete(v)}},dispose:T}}};return X.set(t,u),u}function Be(){if(!N){N=!0;for(const t of C.values())[...t].forEach(r=>r());C.clear(),Z.clear(),F.clear();for(const t of _.values())[...t].forEach(r=>r());_.clear(),Q.clear(),Y.clear(),X.clear(),fe.clear(),f={tabs:[],activeTabId:null}}}return{apps:[...q.values()],getState:ae,subscribe:Ee,openTab:Re,activateTab:Ce,closeTab:_e,updateTab:He,updateSession:xe,operate:$e,bridge:Fe,bus:fe,dispose:Be}}exports.createMicroHost=Ge;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("./sdk.cjs");function Ge(Le,z={}){var Oe;const Q=new Map,Ie=new Map,R=e=>typeof e=="string"&&!!e.trim()&&!/[\u0000-\u001f\u007f]/.test(e),fe=e=>R(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(s=>{if(!s||!R(s.menuId)||!R(s.name)||c.has(s.menuId))throw new Error("菜单标识/名称无效或菜单标识重复");if(c.add(s.menuId),s.icon!==void 0&&(typeof s.icon!="string"||!/^[A-Za-z][A-Za-z0-9-]{0,63}$/.test(s.icon)))throw new Error("菜单图标标识无效");if(s.children!==void 0){if(s.target!==void 0||!Array.isArray(s.children)||!s.children.length)throw new Error("菜单分组需要非空 children 且不能配置 target");return Object.freeze({menuId:s.menuId,name:s.name,...s.icon===void 0?{}:{icon:s.icon},children:a(s.children)})}if(!s.target||!R(s.target.tabId)||!fe(s.target.route))throw new Error("菜单叶节点需要有效 target");return y.add(s.menuId),b.push({pathPrefix:pe(s.target.route),menuId:s.menuId}),Object.freeze({menuId:s.menuId,name:s.name,...s.icon===void 0?{}:{icon:s.icon},target:Object.freeze({tabId:s.target.tabId,route:s.target.route})})}))};if(Q.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,y=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)||!y.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)}Ie.set(e.appId,{leaves:y,routes:b.sort((r,s)=>s.pathPrefix.length-r.pathPrefix.length)}),Q.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(),ze=Object.freeze({source:"host"}),de={on(e,a){return ae.on(e,({payload:c,metadata:y})=>U(()=>a(c,y)))},emit(e,a){ae.emit(e,{payload:a,metadata:ze})},clear:()=>ae.clear()},se=z.callTimeoutMs??3e4;if(!Number.isFinite(se)||se<=0||se>2147483647)throw new Error("callTimeoutMs 必须为不超过2147483647的正有限数");const X=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(z.session??{authenticated:!1,permissions:[]},0);function Re(e){D(),K=we(e,K.revision+1);const a=K;for(const c of[...X]){if(a!==K)break;X.has(c)&&U(()=>c(a))}return a}const ee=new Set,te=new Map,ve=new Map(Object.entries(z.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=z.onError)==null||a.call(z,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=z.canNavigate,ge=z.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||!R(e.title)||e.title.length>500)throw new Error("页签标题无效");if(!Q.has(e.appId))throw new Error(`未知子应用: ${e.appId}`);if(!R(e.tabId)||!fe(e.route))throw new Error("需要 tabId 与应用内绝对路由");const c=Ie.get(e.appId);if(e.activeMenuId!==void 0&&!c.leaves.has(e.activeMenuId))throw new Error("activeMenuId 必须指向本应用可导航叶节点");const y=pe(e.route),b=e.activeMenuId??((o=c.routes.find(t=>t.pathPrefix==="/"||y===t.pathPrefix||y.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 y=d.tabs.find(h=>h.tabId===e&&(!c||h.key===c));if(!y)return;const b=Object.freeze({...y,title:a.title});return d={...d,tabs:d.tabs.map(h=>h===y?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 y=d.tabs[c],b=d.tabs.filter(o=>o!==y);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(y.key),!b.some(o=>o.appId===y.appId)){for(const o of[...B.get(y.appId)??[]])U(o);B.delete(y.appId),te.delete(y.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 Z(e)}function Z(e,a,c,y=!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&&R(n.tabId)&&(n.key===void 0||R(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 s=n=>d.tabs.find(m=>n?m.tabId===n.tabId&&(!n.key||m.key===n.key):m.tabId===d.activeTabId),w="target"in t?s(t.target):void 0,E=t.type==="closeMany"&&t.scope!=="all"?t.anchor?s(t.anchor):c?d.tabs.find(n=>n.key===c):s():void 0,S=s(),f=t.type==="open"?d.tabs.find(n=>n.tabId===t.input.tabId):void 0,I=h&&c?d.tabs.find(n=>n.key===c):void 0,k=I&&a?Object.freeze({appId:a,tab:Object.freeze({...I})}):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,Y=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 Ae,Ne,Te,Pe,Me,De;const n=i=>({status:i,affected:[]});if(P)throw new u.NavigationError("HOST_DISPOSED","MicroHost 已销毁");if((Ae=t.signal)!=null&&Ae.aborted)return n("stale");if(c&&!y&&(b!=null&&b()||!d.tabs.some(i=>i.key===c)))throw new u.NavigationError("PAGE_RELEASED","页面上下文已释放");if(t.type==="open"&&(f?!d.tabs.some(i=>i.key===f.key):d.tabs.some(i=>i.tabId===t.input.tabId))||g&&!d.tabs.some(i=>i.key===g.key)||E&&!d.tabs.some(i=>i.key===E.key)||Y.some(i=>!d.tabs.some(O=>O.key===i.key)))return n("stale");if(t.type!=="open"&&t.type!=="closeMany"&&!g)return n("noop");const m=G;if(j(),c&&!y&&(b!=null&&b()||!d.tabs.some(i=>i.key===c)))throw new u.NavigationError("PAGE_RELEASED","页面上下文已释放");if(G!==m||P||(Ne=t.signal)!=null&&Ne.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(i=>i.key===f.key?l:i)):p.some(i=>i.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(i=>i.key===g.key?l:i)),(t.type==="close"||t.type==="back")&&(M=[g]),t.type==="closeMany"&&(M=Y),M.length&&t.type!=="refresh"&&t.type!=="open"){const i=new Set(M.map(A=>A.key)),O=v.tabs.findIndex(A=>A.tabId===v.activeTabId);if(p=p.filter(A=>!i.has(A.key)),t.type==="back"){const A=W.get(g.key);l=p.find(T=>T.key===A&&H(T)),!l&&t.fallback&&(l=ue(t.fallback),i.has(l.key)&&(l=Object.freeze({...l,key:`${l.tabId}:${++le}`})),p.some(T=>T.key===l.key)||p.push(l));const N=v.tabs.filter(T=>H(T)),L=N.findIndex(T=>T.key===g.key);l??(l=N.slice(0,L).reverse().find(T=>p.some(ne=>ne.key===T.key))??N.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(A=>A.tabId===$)){const A="closeFallback"in t?t.closeFallback:void 0,N=A?p.find(L=>L.tabId===A.tabId&&L.key===A.key&&H(L)):void 0;N&&(q(N),l=N),$=((Te=N??p[Math.min(O,p.length-1)])==null?void 0:Te.tabId)??null}}}catch(i){throw i instanceof u.NavigationError?i:new u.NavigationError("INVALID_OPERATION","导航参数无效")}if(p.length===v.tabs.length&&p.every((i,O)=>i.key===v.tabs[O].key)&&$===v.activeTabId)return n("noop");const he=[...M],ce=v.tabs.find(i=>i.tabId===v.activeTabId);ce&&ce.tabId!==$&&!he.some(i=>i.key===ce.key)&&he.push(ce);for(const i of he)for(const O of[...re.get(i.key)??[]]){let A;const N=t.signal;let L=()=>{};try{const T=Promise.resolve(O({operation:t,tab:i})),ne=new Promise(Se=>{if(!N)return;const je=()=>Se();N.addEventListener("abort",je,{once:!0}),L=()=>N.removeEventListener("abort",je),N.aborted&&Se()});A=await Promise.race([T,ne])}catch{if(N!=null&&N.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(A===!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 i=W.get(g.key);i&&W.set(l.key,i)}for(const i of M)me(i.key);for(const i of new Set(M.map(O=>O.appId)))if(!p.some(O=>O.appId===i)){for(const O of[...B.get(i)??[]])U(O);B.delete(i),te.delete(i)}const ye=[...M,...p.filter(i=>!v.tabs.some(O=>O.key===i.key))];return!ye.length&&l&&ye.push(l),oe(),{status:"committed",affected:ye.map(i=>({tabId:i.tabId,key:i.key})),...l?{tab:l}:{}}},J=Ee.then(C);return Ee=J.catch(()=>{}),J}catch(t){return Promise.reject(t)}}function Ve(e){if(D(),!Q.has(e))throw new Error(`未知子应用: ${e}`);const a=te.get(e);if(a)return a;let c=!0;const y=new Set([()=>{c=!1}]);B.set(e,y);async function b(o,t,r,s={}){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=s.timeoutMs??se;if(!Number.isFinite(S)||S<=0||S>2147483647)throw new TypeError("timeoutMs 必须为不超过2147483647的正有限数");const f=s.signal;if(f!=null&&f.aborted)throw new u.HostCallError("CANCELLED","宿主能力调用已取消");const I=new AbortController,k=r?d.tabs.find(j=>j.key===r.key):void 0,x=Object.freeze({appId:e,signal:I.signal,...k?{tab:Object.freeze({...k})}:{},isActive:()=>!I.signal.aborted&&!P&&c&&(!r||!r.released()&&d.tabs.some(j=>j.key===r.key))});return new Promise((j,H)=>{let _=!1,Y;const g=()=>{Y!==void 0&&clearTimeout(Y),f==null||f.removeEventListener("abort",J),y.delete(n),r==null||r.cleanups.delete(n)},C=v=>{_||(_=!0,g(),H(v),I.abort())},J=()=>C(new u.HostCallError("CANCELLED","宿主能力调用已取消")),n=()=>{try{w()}catch(v){C(v)}};if(y.add(n),r==null||r.cleanups.add(n),f==null||f.addEventListener("abort",J,{once:!0}),Y=setTimeout(()=>C(new u.HostCallError("TIMEOUT","宿主能力调用超时")),S),f!=null&&f.aborted){J();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(s=>!t.capabilities.includes(s)));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),s=()=>{X.delete(r),y.delete(s)};return X.add(r),y.add(s),U(()=>o(t)),s},subscribe(o){if(!c)throw new Error("应用 bridge 已释放");const t=ke(o),r=()=>{t(),y.delete(r)};return y.add(r),r},ready(o){const t=s=>{const w=s.tabs.find(E=>E.tabId===s.activeTabId&&E.appId===e);w&&o(w)},r=h.subscribe(t);try{t(ie())}catch(s){throw r(),s}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 s=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,s.forEach(I=>I()),s.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,...I){return b(f,I,{key:o,released:()=>r,cleanups:s})},callHostWithOptions(f,I,...k){return b(f,k,{key:o,released:()=>r,cleanups:s},I)},async openTab(f){w();const I=await Z({type:"open",input:f},e,o,!1,()=>r);if(I.tab)return I.tab;const k=d.tabs.find(x=>x.tabId===f.tabId&&x.appId===e);if(I.status==="noop"&&k)return k;throw new u.NavigationError(I.status==="cancelled"?"CANCELLED":"STALE","导航未提交")},async navigateTo(f,I={}){return w(),Z({type:"open",input:f,signal:I.signal},e,o,!1,()=>r,!0)},async closeCurrent(){return P?!1:(await Z({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 I=re.get(o);I||re.set(o,I=new Set),I.add(f);const k=()=>{I.delete(f),s.delete(k)};return s.add(k),k},async refreshCurrent(){return w(),Z({type:"refresh",target:{tabId:t.tabId,key:o}},e,o,!1,()=>r)},async back(f){return w(),Z({type:"back",target:{tabId:t.tabId,key:o},fallback:f},e,o,!1,()=>r)},async operate(f){return w(),Z(f,e,o,!1,()=>r)},emit(f,I){w(),ae.emit(f,{payload:I,metadata:Object.freeze({source:"app",appId:e,tabKey:o})})},on(f,I){w();const k=de.on(f,I);return s.add(k),()=>{k(),s.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(),X.clear(),te.clear(),de.clear(),d={tabs:[],activeTabId:null}}}return{apps:[...Q.values()],getState:ie,subscribe:ke,openTab:He,activateTab:Ce,closeTab:Fe,updateTab:_e,updateSession:Re,operate:$e,bridge:Ve,bus:de,dispose:Be}}exports.createMicroHost=Ge;