@fulgurjs/federation 2.0.2 → 2.1.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/dist/runtime.js CHANGED
@@ -1,3 +1,3 @@
1
- var X=/^v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-((?:[0-9A-Za-z-]+)(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z.-]+)?$/;function T(t){let n=X.exec(String(t).trim());return n?{major:Number(n[1]),minor:Number(n[2]??0),patch:Number(n[3]??0),pre:n[4]?n[4].split("."):[]}:null}function Y(t,n){if(t.length===0&&n.length===0)return 0;if(t.length===0)return 1;if(n.length===0)return-1;let o=Math.max(t.length,n.length);for(let r=0;r<o;r++){let l=t[r],m=n[r];if(l===void 0)return-1;if(m===void 0)return 1;let y=/^\d+$/.test(l),E=/^\d+$/.test(m);if(y&&E){let w=Number(l)-Number(m);if(w!==0)return w}else{if(y!==E)return y?-1:1;if(l!==m)return l<m?-1:1}}return 0}function v(t,n){let o=T(t),r=T(n);return!o||!r?t<n?-1:t>n?1:0:o.major!==r.major?o.major-r.major:o.minor!==r.minor?o.minor-r.minor:o.patch!==r.patch?o.patch-r.patch:Y(o.pre,r.pre)}var Z=/^[a-z]+[+:-]/i;function O(t,n){if(n===!1||n==null||n==="")return!0;let o=String(n).trim();return!o||o==="*"||o==="latest"||Z.test(o)?!0:o.split("||").some(r=>Q(t,r.trim()))}function Q(t,n){if(!n||n==="*")return!T(t)?.pre.length;let o=n.split(/\s+-\s+/);if(o.length===2){let m=o[0].trim(),y=o[1].trim();return!(!O(t,`>=${m}`)||!O(t,`<=${y}`))}let r=T(t);if(!r)return!1;let l=ee(n);if(l===null)return!0;for(let m of l)if(!ie(r,t,m))return!1;return!(r.pre.length>0&&!l.some(m=>m.ver.pre.length>0)&&!l.some(y=>y.ver.major===r.major&&y.ver.minor===r.minor&&y.ver.patch===r.patch))}function ee(t){let n=[],o=t.trim().split(/\s+/).filter(Boolean);if(o.length===0)return null;for(let r of o){let l=/^(>=|<=|>|<|=|\^|~)?(.*)$/.exec(r);if(!l)return null;let m=l[1],y=l[2],E=re(m,y);if(E===null)return null;n.push(...E)}return n}function re(t,n){let o=ne(n);if(!o)return null;let{ver:r,partial:l}=o;switch(t){case"^":{let m=te(r);return m?[{op:">=",ver:r},{op:"<",ver:m}]:null}case"~":{let m=l>=2?{major:r.major,minor:r.minor+1,patch:0,pre:[]}:{major:r.major+1,minor:0,patch:0,pre:[]};return[{op:">=",ver:r},{op:"<",ver:m}]}case">=":return[{op:">=",ver:r}];case">":return l===1?[{op:">=",ver:{major:r.major+1,minor:0,patch:0,pre:[]}}]:l===2?[{op:">=",ver:{major:r.major,minor:r.minor+1,patch:0,pre:[]}}]:[{op:">",ver:r}];case"<":return l===1?[{op:"<",ver:{major:r.major,minor:0,patch:0,pre:[]}}]:l===2?[{op:"<",ver:{major:r.major,minor:r.minor,patch:0,pre:[]}}]:[{op:"<",ver:r}];case"<=":return[{op:"<=",ver:r}];case"=":case void 0:return l===1?[{op:">=",ver:r},{op:"<",ver:{major:r.major+1,minor:0,patch:0,pre:[]}}]:l===2?[{op:">=",ver:r},{op:"<",ver:{major:r.major,minor:r.minor+1,patch:0,pre:[]}}]:[{op:"=",ver:r}];default:return null}}function te(t){return t.major>0?{major:t.major+1,minor:0,patch:0,pre:[]}:t.minor>0?{major:0,minor:t.minor+1,patch:0,pre:[]}:{major:0,minor:0,patch:t.patch+1,pre:[]}}function ne(t){let n=t.replace(/^v/,"").trim();if(!n||n==="*"||n==="x"||n==="X")return{ver:{major:0,minor:0,patch:0,pre:[]},partial:0};let o=n.split("."),r=[],l=0;for(let E of o.slice(0,3))if(/^\d+$/.test(E))r.push(Number(E)),l++;else{if(E==="x"||E==="X"||E==="*")break;break}let m=n.split("-"),y=m.length>1?m.slice(1).join("-").split("+")[0].split("."):[];return l===0&&y.length===0?{ver:{major:0,minor:0,patch:0,pre:[]},partial:0}:{ver:{major:r[0]??0,minor:r[1]??0,patch:r[2]??0,pre:y},partial:l}}function oe(t,n){switch(n){case">=":return t>=0;case">":return t>0;case"<":return t<0;case"<=":return t<=0;case"=":return t===0}}function ie(t,n,o){let r=`${t.major}.${t.minor}.${t.patch}`+(t.pre.length?`-${t.pre.join(".")}`:"");if(o.upper)return!(!(v(r,`${o.ver.major}.${o.ver.minor}.${o.ver.patch}`+(o.ver.pre.length?`-${o.ver.pre.join(".")}`:""))>=0)||!(v(r,`${o.upper.major}.${o.upper.minor}.${o.upper.patch}`)<0));let l=v(r,`${o.ver.major}.${o.ver.minor}.${o.ver.patch}`+(o.ver.pre.length?`-${o.ver.pre.join(".")}`:"")),m=oe(l,o.op);return o.ver.pre.length>0&&t.pre.length===0&&(o.op===">"||o.op==="<")?!1:m}var g=class extends Error{code;details;constructor(n,o,r){super(`[fulgurjs:${n}] ${o}`),this.name="FgError",this.code=n,this.details=r}},S={REMOTE_LOAD_FAILED:"MFU-001",REMOTE_NAME_MISMATCH:"MFU-002",SHARE_STRICT_VERSION:"MFU-003",SHARE_NOT_AVAILABLE:"MFU-004",CONTAINER_REINIT_CONFLICT:"MFU-005",MODULE_NOT_EXPOSED:"MFU-006",PRELOAD_FAILED:"MFU-007",REMOTE_UNKNOWN:"MFU-008",EMPTY_EXPORTS:"MFU-009",SINGLETON_SKEW:"MFU-010"};var U="2.0.2";var A=15e3,P=2,ae=5,se=3e4;function ce(){let t={},n=new Map,o=[],r=new Map,l=new WeakMap,m={},y=()=>{for(let e of o)try{e.init?.(m)}catch(i){console.warn("[fulgurjs] runtimePlugin init failed:",e.name,i)}},E=e=>t[e]??={};function w(e){try{m.onRemoteError?.(e)}catch{}typeof window<"u"&&window.dispatchEvent(new CustomEvent("fulgurjs:error",{detail:e}))}function F(e="default"){return E(e),t}function V(e,i,a,c,R={}){let s=E(e||"default"),u=s[i]??={};u[a]||(u[a]={version:a,get:c,from:R.from??"unknown",eager:!!R.eager,loaded:!!R.loaded})}function I(e){for(let i of e){let a=n.get(i.name);n.set(i.name,{timeout:A,retries:P,...a,...i,state:a?.state??"idle",breakerState:a?.breakerState??{fails:0,openUntil:0},debug:a?.debug??{entry:i.entry,status:"idle"}})}}function D(e){I([e])}function H(e){o.push(...e),y()}async function K(e,i={}){let a=i.shareScope||"default",c=i.shareKey||e,s=E(a)[c]||{},u=Object.keys(s),d=i.requiredVersion,f=u.filter(p=>d===void 0||d===!1||O(p,d)),_;if(i.singleton&&u.length>0){let p=u.filter(h=>s[h].loaded);if(_=[...p.length?p:u].sort(v).pop(),u.length>1||!f.includes(_)){let h=s[_],j=`singleton skew "${c}": req "${d??"any"}" use ${_} from ${h.from}`;if(i.strictVersion){let M=new g(S.SHARE_STRICT_VERSION,j,{shareKey:c,requiredVersion:d});throw w({remote:h.from,error:M}),M}console.warn(`[fulgurjs:MFU-010] ${j}`)}}else if(_=[...f].sort(v).pop(),!_){if(i.strictVersion){let h=new g(S.SHARE_STRICT_VERSION,`no satisfying version for "${c}" (required "${d}") in share scope "${a}"`);throw w({remote:c,error:h}),h}if(i.fallback)return i.fallback();let p=new g(S.SHARE_NOT_AVAILABLE,`shared "${c}" (${d??"any"}) unavailable in scope "${a}" (no local fallback)`,{shareKey:c,requiredVersion:d});throw w({remote:c,error:p}),p}let $=s[_];if(m.resolveShare){let p=await m.resolveShare({shareKey:c,shareScope:a,requiredVersion:d,picked:$,available:Object.values(s)});p&&($=p)}return $.loaded=!0,$.get()}function q(e,i,a){return new Promise((c,R)=>{let s=setTimeout(()=>R(new Error(`timeout after ${i}ms: ${a}`)),i);e.then(u=>{clearTimeout(s),c(u)},u=>{clearTimeout(s),R(u)})})}let z=e=>new Promise(i=>setTimeout(i,e));function k(e,i){if(!e||typeof e.get!="function")throw new Error(`remoteEntry of "${i.name}" does not export a container interface (get/init)`);if(e.name&&i.name&&e.name!==i.name){let a=`remoteEntry self-reported name "${e.name}" mismatches configured name "${i.name}"`;if(i.promise)console.warn(`[fulgurjs] ${a} (promise-based remote, continuing)`);else throw Object.assign(new Error(a),{code:S.REMOTE_NAME_MISMATCH})}}async function L(e,i){return q(import(e),i.timeout??A,`load remoteEntry ${e}`)}async function x(e,i){let a=e.shareScope||"default";if(e.container){let s=l.get(e.container);if(s&&s!==a)throw new g(S.CONTAINER_REINIT_CONFLICT,`container "${e.name}" already initialized with share scope "${s}", cannot re-init with "${a}"`);return e.container}if(e.containerPromise)return e.containerPromise;let c=e.breakerState;if(c.openUntil>Date.now()){let s=new g(S.REMOTE_LOAD_FAILED,`circuit breaker open for remote "${e.name}" until ${new Date(c.openUntil).toISOString()}`,{remote:e.name});throw w({remote:e.name,error:s}),s}e.state="loading",e.debug.status="loading";let R=(async()=>{let s=[];if(e.promise)s.push(async()=>{let p=await e.promise(),h=typeof p=="string"?await L(p,e):p;return k(h,e),h});else for(let p of[e.entry,...e.fallback??[]])s.push(async()=>{let h=await L(p,e);return k(h,e),h});let u,d,f=!1,_=i?.retries??e.retries??P;for(let p of s){for(let h=0;h<=_;h++){let j=performance.now();try{u=await p(),e.lastLoadMs=Math.round(performance.now()-j),e.debug.lastLoadMs=e.lastLoadMs,f=!0;break}catch(M){if(d=M,M?.code)throw M;h<_&&await z(200*2**h)}}if(f)break}if(!f||!u){let p=[];throw/^https?:\/\//.test(e.entry)&&typeof location<"u"?(p.push(`1. is the remote dev server running? try opening ${e.entry} in the browser \u2014 it must return JS, not HTML/an error`),p.push("2. is the URL correct in federation({ remotes })? dev and prod entries can differ"),p.push("3. CORS: the remote dev server must allow cross-origin requests (fulgurjs-federation enables server.cors automatically; a custom server config may have disabled it)")):(p.push(`1. is the remote deployed and reachable? try opening ${e.entry} in the browser \u2014 it must return JS`),p.push("2. NGINX/CDN routing: the entry path must serve the remoteEntry JS file (check try_files / fallback rules)")),new g(S.REMOTE_LOAD_FAILED,`failed to load remote "${e.name}" from ${e.entry}: ${String(d?.message??d)}
2
- `+p.join(`
3
- `),{remote:e.name})}k(u,e);let $=l.get(u);if($&&$!==a)throw new g(S.CONTAINER_REINIT_CONFLICT,`container "${e.name}" already initialized with share scope "${$}", cannot re-init with "${a}"`);return typeof u.init=="function"&&await u.init(t),l.set(u,a),e.container=u,e.state="loaded",e.debug.status="loaded",c.fails=0,u})();e.containerPromise=R;try{return await R}catch(s){if(e.containerPromise=void 0,e.state="failed",e.debug.status="failed",e.debug.error=String(s?.message??s),c.fails+=1,c.fails>=ae&&(c.openUntil=Date.now()+se,c.fails=0),s instanceof g)throw w({remote:e.name,error:s}),s;if(s?.code)throw w({remote:e.name,error:s}),s;let u=new g(S.REMOTE_LOAD_FAILED,String(s?.message??s),{remote:e.name});throw w({remote:e.name,error:u}),u}}function N(e){let i=e.indexOf("/");return i===-1?{remote:e,module:""}:{remote:e.slice(0,i),module:G(e.slice(i+1))}}function G(e){return e?e.startsWith(".")?e:`./${e}`:""}async function J(e,i){let{remote:a,module:c}=N(e);m.beforeLoadRemote?.({remote:a,module:c});let R=n.get(a);if(!R)throw new g(S.REMOTE_UNKNOWN,`unknown remote "${a}". Register it via federation({ remotes }) or registerRemote().`,{remote:a});let s;try{s=await x(R,{retries:i?.retries})}catch(f){if(i?.fallbackModule)return console.error(`[fulgurjs] loadRemote("${e}") failed; returning fallbackModule (\u663E\u5F0F\u964D\u7EA7\uFF0C\u9519\u8BEF\u5DF2\u900F\u51FA)`,f),w({remote:a,error:f}),await i.fallbackModule();throw f}if(!c)return s;let u=`${a}@${R.shareScope||"default"}#${c}`;r.has(u)||r.set(u,s.get(c).catch(f=>{throw r.delete(u),f instanceof g||f?.code?f:new g(S.REMOTE_LOAD_FAILED,`failed to load module "${c}" from remote "${a}": ${String(f?.message??f)}`,{remote:a,module:c})}));let d;try{d=await r.get(u)}catch(f){if(i?.fallbackModule)return console.error(`[fulgurjs] loadRemote("${e}") failed; returning fallbackModule (\u663E\u5F0F\u964D\u7EA7\uFF0C\u9519\u8BEF\u5DF2\u900F\u51FA)`,f),w({remote:a,error:f}),await i.fallbackModule();throw f}if(m.afterLoadRemote?.({remote:a,module:c,module_ns:d}),d&&typeof d=="object"&&Object.keys(d).length===0){let f=new g(S.EMPTY_EXPORTS,`no exports: ${c} @ ${a}`,{remote:a,module:c});w({remote:a,error:f}),console.error(`[fulgurjs:MFU-009] ${f.message}`)}return d}function W(e){let i=n.get(e);if(!i)throw new g(S.REMOTE_UNKNOWN,`unknown remote "${e}"`,{remote:e});return x(i)}async function B(e,i={}){let{remote:a}=N(e),c=n.get(a);if(!c)throw new g(S.REMOTE_UNKNOWN,`unknown remote "${a}"`,{remote:a});let R=i.mode??"preload",s=(u,d)=>{if(typeof document>"u"||document.querySelector(`link[href="${u}"]`))return;let f=document.createElement("link");f.rel=d==="style"?"stylesheet":"modulepreload",f.href=u,R==="prefetch"&&(f.fetchPriority="low"),document.head.appendChild(f)};try{if(c.manifestUrl){let u=await fetch(c.manifestUrl,{cache:"no-cache"});if(u.ok){let d=await u.json();s(c.entry,"modulepreload");let f=d.exposes??{};for(let _ of Object.keys(f)){let $=f[_];$?.file&&s(new URL($.file,c.entry).href,"modulepreload");for(let p of $?.css??[])s(new URL(p,c.entry).href,"style")}return}}s(c.entry,"modulepreload")}catch(u){w({remote:a,error:new g(S.PRELOAD_FAILED,String(u?.message??u),{remote:a})})}}return typeof window>"u"||(window.__FULGURJS_SCOPE__=t,window.__FULGURJS_INFO__={get remotes(){let e={};for(let[i,a]of n)e[i]={...a.debug,entry:a.entry};return e},errors:[]}),{shareScopeMap:t,initSharing:F,registerShare:V,registerRemotes:I,registerRemote:D,registerPlugins:H,loadShare:K,loadRemote:J,getContainer:W,preloadRemote:B,parseSpec:N}}var C=globalThis,b=C.__FULGURJS_RUNTIME__??ce();C.__FULGURJS_RUNTIME__=b;try{Object.freeze(b)}catch{}var he=U;function ye(){return b}var Ee=b.initSharing,Re=b.registerShare,Se=b.registerRemotes,we=b.registerRemote,be=b.registerPlugins,_e=b.loadShare,$e=b.loadRemote,Me=b.getContainer,ve=b.preloadRemote,je=b.shareScopeMap;function Te(t){if(t&&typeof t=="object"&&"default"in t){let n=t.default;return n!==void 0?n:t}return t}var Oe=b;export{Oe as default,Me as getContainer,ye as getRuntime,Ee as initSharing,$e as loadRemote,_e as loadShare,ve as preloadRemote,be as registerPlugins,we as registerRemote,Se as registerRemotes,Re as registerShare,b as runtime,je as shareScopeMap,Te as unwrapDefault,he as version};
1
+ var oe=/^v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-((?:[0-9A-Za-z-]+)(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z.-]+)?$/;function I(o){let i=oe.exec(String(o).trim());return i?{major:Number(i[1]),minor:Number(i[2]??0),patch:Number(i[3]??0),pre:i[4]?i[4].split("."):[]}:null}function ie(o,i){if(o.length===0&&i.length===0)return 0;if(o.length===0)return 1;if(i.length===0)return-1;let s=Math.max(o.length,i.length);for(let n=0;n<s;n++){let f=o[n],y=i[n];if(f===void 0)return-1;if(y===void 0)return 1;let R=/^\d+$/.test(f),w=/^\d+$/.test(y);if(R&&w){let T=Number(f)-Number(y);if(T!==0)return T}else{if(R!==w)return R?-1:1;if(f!==y)return f<y?-1:1}}return 0}function j(o,i){let s=I(o),n=I(i);return!s||!n?o<i?-1:o>i?1:0:s.major!==n.major?s.major-n.major:s.minor!==n.minor?s.minor-n.minor:s.patch!==n.patch?s.patch-n.patch:ie(s.pre,n.pre)}var se=/^[a-z]+[+:-]/i;function P(o,i){if(i===!1||i==null||i==="")return!0;let s=String(i).trim();return!s||s==="*"||s==="latest"||se.test(s)?!0:s.split("||").some(n=>ae(o,n.trim()))}function ae(o,i){if(!i||i==="*")return!I(o)?.pre.length;let s=i.split(/\s+-\s+/);if(s.length===2){let y=s[0].trim(),R=s[1].trim();return!(!P(o,`>=${y}`)||!P(o,`<=${R}`))}let n=I(o);if(!n)return!1;let f=ce(i);if(f===null)return!0;for(let y of f)if(!pe(n,o,y))return!1;return!(n.pre.length>0&&!f.some(y=>y.ver.pre.length>0)&&!f.some(R=>R.ver.major===n.major&&R.ver.minor===n.minor&&R.ver.patch===n.patch))}function ce(o){let i=[],s=o.trim().split(/\s+/).filter(Boolean);if(s.length===0)return null;for(let n of s){let f=/^(>=|<=|>|<|=|\^|~)?(.*)$/.exec(n);if(!f)return null;let y=f[1],R=f[2],w=le(y,R);if(w===null)return null;i.push(...w)}return i}function le(o,i){let s=me(i);if(!s)return null;let{ver:n,partial:f}=s;switch(o){case"^":{let y=ue(n);return y?[{op:">=",ver:n},{op:"<",ver:y}]:null}case"~":{let y=f>=2?{major:n.major,minor:n.minor+1,patch:0,pre:[]}:{major:n.major+1,minor:0,patch:0,pre:[]};return[{op:">=",ver:n},{op:"<",ver:y}]}case">=":return[{op:">=",ver:n}];case">":return f===1?[{op:">=",ver:{major:n.major+1,minor:0,patch:0,pre:[]}}]:f===2?[{op:">=",ver:{major:n.major,minor:n.minor+1,patch:0,pre:[]}}]:[{op:">",ver:n}];case"<":return f===1?[{op:"<",ver:{major:n.major,minor:0,patch:0,pre:[]}}]:f===2?[{op:"<",ver:{major:n.major,minor:n.minor,patch:0,pre:[]}}]:[{op:"<",ver:n}];case"<=":return[{op:"<=",ver:n}];case"=":case void 0:return f===1?[{op:">=",ver:n},{op:"<",ver:{major:n.major+1,minor:0,patch:0,pre:[]}}]:f===2?[{op:">=",ver:n},{op:"<",ver:{major:n.major,minor:n.minor+1,patch:0,pre:[]}}]:[{op:"=",ver:n}];default:return null}}function ue(o){return o.major>0?{major:o.major+1,minor:0,patch:0,pre:[]}:o.minor>0?{major:0,minor:o.minor+1,patch:0,pre:[]}:{major:0,minor:0,patch:o.patch+1,pre:[]}}function me(o){let i=o.replace(/^v/,"").trim();if(!i||i==="*"||i==="x"||i==="X")return{ver:{major:0,minor:0,patch:0,pre:[]},partial:0};let s=i.split("."),n=[],f=0;for(let w of s.slice(0,3))if(/^\d+$/.test(w))n.push(Number(w)),f++;else{if(w==="x"||w==="X"||w==="*")break;break}let y=i.split("-"),R=y.length>1?y.slice(1).join("-").split("+")[0].split("."):[];return f===0&&R.length===0?{ver:{major:0,minor:0,patch:0,pre:[]},partial:0}:{ver:{major:n[0]??0,minor:n[1]??0,patch:n[2]??0,pre:R},partial:f}}function fe(o,i){switch(i){case">=":return o>=0;case">":return o>0;case"<":return o<0;case"<=":return o<=0;case"=":return o===0}}function pe(o,i,s){let n=`${o.major}.${o.minor}.${o.patch}`+(o.pre.length?`-${o.pre.join(".")}`:"");if(s.upper)return!(!(j(n,`${s.ver.major}.${s.ver.minor}.${s.ver.patch}`+(s.ver.pre.length?`-${s.ver.pre.join(".")}`:""))>=0)||!(j(n,`${s.upper.major}.${s.upper.minor}.${s.upper.patch}`)<0));let f=j(n,`${s.ver.major}.${s.ver.minor}.${s.ver.patch}`+(s.ver.pre.length?`-${s.ver.pre.join(".")}`:"")),y=fe(f,s.op);return s.ver.pre.length>0&&o.pre.length===0&&(s.op===">"||s.op==="<")?!1:y}var b=class extends Error{code;details;constructor(i,s,n){super(`[fulgurjs:${i}] ${s}`),this.name="FgError",this.code=i,this.details=n}},S={REMOTE_LOAD_FAILED:"MFU-001",REMOTE_NAME_MISMATCH:"MFU-002",SHARE_STRICT_VERSION:"MFU-003",SHARE_NOT_AVAILABLE:"MFU-004",CONTAINER_REINIT_CONFLICT:"MFU-005",MODULE_NOT_EXPOSED:"MFU-006",PRELOAD_FAILED:"MFU-007",REMOTE_UNKNOWN:"MFU-008",EMPTY_EXPORTS:"MFU-009",SINGLETON_SKEW:"MFU-010"};var q="2.1.0";var x=15e3,z=2,de=5,ge=3e4,he=4e3,ye=.2;function Ee(){let o=Object.create(null),i=new Map,s=[],n=new Map,f=new Map,y=new WeakMap,R=new WeakMap,w={},T=()=>{for(let e of s)try{e.init?.(w)}catch(t){console.warn("[fulgurjs] runtimePlugin init failed:",e.name,t)}},N=e=>o[e]??=Object.create(null);function $(e){try{w.onRemoteError?.(e)}catch{}typeof window<"u"&&window.dispatchEvent(new CustomEvent("fulgurjs:error",{detail:e}))}function B(e="default"){return N(e),o}function J(e,t,r,l,h={}){let a=N(e||"default"),p=Object.hasOwn(a,t)?a[t]:void 0,d=p&&typeof p=="object"?p:void 0;d||(d=Object.create(null),a[t]=d),!(Object.hasOwn(d,r)&&d[r])&&(d[r]={version:r,get:l,from:h.from??"unknown",eager:!!h.eager,loaded:!!h.loaded})}function G(e){let t=(r,l,h)=>{if(r===void 0)return;if(!(typeof r=="number"&&(h?Number.isInteger(r)&&r>=0&&r<=10:Number.isFinite(r)&&r>0)))throw new Error(`[fulgurjs] registerRemote("${e.name}"): ${l} must be a ${h?"integer 0..10":"finite positive number"}, got ${String(r)}`)};t(e.timeout,"timeout"),t(e.retries,"retries",!0),e.breaker&&(t(e.breaker.threshold,"breaker.threshold"),t(e.breaker.resetMs,"breaker.resetMs"))}function F(e){for(let t of e){G(t);let r=i.get(t.name);i.set(t.name,{timeout:x,retries:z,...r,...t,breakerThreshold:t.breaker?.threshold??r?.breakerThreshold??de,breakerResetMs:t.breaker?.resetMs??r?.breakerResetMs??ge,state:r?.state??"idle",breakerState:r?.breakerState??{fails:0,openUntil:0},entryInflight:r?.entryInflight??new Map,debug:r?.debug??{entry:t.entry,status:"idle"}})}}function X(e){F([e])}function Y(e){s.push(...e),T()}async function Z(e,t={}){let r=t.shareScope||"default",l=t.shareKey||e,a=N(r)[l]||{},p=Object.keys(a),d=t.requiredVersion,g=p.filter(c=>d===void 0||d===!1||P(c,d)),m;if(t.singleton&&p.length>0){let c=p.filter(u=>a[u].loaded);if(m=[...c.length?c:p].sort(j).pop(),p.length>1||!g.includes(m)){let u=a[m],k=`singleton skew "${l}": req "${d??"any"}" use ${m} from ${u.from}`;if(t.strictVersion){let v=new b(S.SHARE_STRICT_VERSION,k,{shareKey:l,requiredVersion:d});throw $({remote:u.from,error:v}),v}console.warn(`[fulgurjs:MFU-010] ${k}`)}}else if(m=[...g].sort(j).pop(),!m){if(t.strictVersion){let u=new b(S.SHARE_STRICT_VERSION,`no satisfying version for "${l}" (required "${d}") in share scope "${r}"`);throw $({remote:l,error:u}),u}if(t.fallback)return t.fallback();let c=new b(S.SHARE_NOT_AVAILABLE,`shared "${l}" (${d??"any"}) unavailable in scope "${r}" (no local fallback)`,{shareKey:l,requiredVersion:d});throw $({remote:l,error:c}),c}let E=a[m];if(w.resolveShare){let c=await w.resolveShare({shareKey:l,shareScope:r,requiredVersion:d,picked:E,available:Object.values(a)});c&&(E=c)}return E.loaded=!0,E.get()}function Q(e){try{let t=new URL(e,typeof location<"u"?location.href:"http://f.invalid");return t.protocol+"//"+t.host+t.pathname}catch{return String(e).replace(/[?#].*$/,"")}}function C(e,t,r){return new Promise((l,h)=>{let a=setTimeout(()=>h(new Error(`timeout after ${t}ms: ${r}`)),t);e.then(p=>{clearTimeout(a),l(p)},p=>{clearTimeout(a),h(p)})})}let ee=e=>new Promise(t=>setTimeout(t,e));function A(e,t){if(!e||typeof e.get!="function")throw new Error(`remoteEntry of "${t.name}" does not export a container interface (get/init)`);if(e.name&&t.name&&e.name!==t.name){let r=`remoteEntry self-reported name "${e.name}" mismatches configured name "${t.name}"`;if(t.promise)console.warn(`[fulgurjs] ${r} (promise-based remote, continuing)`);else throw Object.assign(new Error(r),{code:S.REMOTE_NAME_MISMATCH})}}async function D(e,t){let r=t.entryInflight.get(e),l=r??import(e);return r||(t.entryInflight.set(e,l),l.catch(()=>t.entryInflight.delete(e))),C(l,t.timeout??x,`load remoteEntry ${e}`)}async function V(e,t){let r=e.shareScope||"default";if(e.container){let a=R.get(e.container);if(a&&a!==r)throw new b(S.CONTAINER_REINIT_CONFLICT,`container "${e.name}" already initialized with share scope "${a}", cannot re-init with "${r}"`);return e.container}if(e.containerPromise)return e.containerPromise;let l=e.breakerState;if(l.openUntil>Date.now()){let a=new b(S.REMOTE_LOAD_FAILED,`circuit breaker open for remote "${e.name}" until ${new Date(l.openUntil).toISOString()}`,{remote:e.name});throw $({remote:e.name,error:a}),a}e.state="loading",e.debug.status="loading";let h=(async()=>{let a=[];if(e.promise)a.push(async()=>{let c=await C(e.promise(),e.timeout??x,`resolve promise remote "${e.name}"`),u=typeof c=="string"?await D(c,e):c;return A(u,e),u});else for(let c of[e.entry,...e.fallback??[]])a.push(async()=>{let u=await D(c,e);return A(u,e),u});let p,d,g=!1,m=t?.retries??e.retries??z;for(let c of a){for(let u=0;u<=m;u++){let k=performance.now();try{p=await c(),e.lastLoadMs=Math.round(performance.now()-k),e.debug.lastLoadMs=e.lastLoadMs,g=!0;break}catch(v){if(d=v,v?.code)throw v;u<m&&await ee(Math.min(he,200*2**u)*(1-ye*(1-2*Math.random())))}}if(g)break}if(!g||!p){let c=Q(e.entry),u=[];throw/^https?:\/\//.test(e.entry)&&typeof location<"u"?(u.push(`1. is the remote dev server running? open ${c} in a browser \u2014 it must return JS, not HTML`),u.push("2. is the URL correct in federation({ remotes })? dev/prod entries can differ"),u.push("3. CORS: the remote dev server must allow cross-origin requests (check its server.cors)")):(u.push(`1. is the remote deployed? open ${c} in a browser \u2014 it must return JS`),u.push("2. NGINX/CDN routing: the entry path must serve the remoteEntry JS (check try_files)")),new b(S.REMOTE_LOAD_FAILED,`failed to load remote "${e.name}" from ${c}: ${String(d?.message??d)}
2
+ `+u.join(`
3
+ `),{remote:e.name})}A(p,e);let E=R.get(p);if(E&&E!==r)throw new b(S.CONTAINER_REINIT_CONFLICT,`container "${e.name}" already initialized with share scope "${E}", cannot re-init with "${r}"`);return typeof p.init=="function"&&await p.init(o),R.set(p,r),e.container=p,e.state="loaded",e.debug.status="loaded",l.fails=0,p})();e.containerPromise=h;try{return await h}catch(a){if(e.containerPromise=void 0,e.state="failed",e.debug.status="failed",e.debug.error=String(a?.message??a),l.fails+=1,l.fails>=e.breakerThreshold&&(l.openUntil=Date.now()+e.breakerResetMs,l.fails=0),a instanceof b)throw $({remote:e.name,error:a}),a;if(a?.code)throw $({remote:e.name,error:a}),a;let p=new b(S.REMOTE_LOAD_FAILED,String(a?.message??a),{remote:e.name});throw $({remote:e.name,error:p}),p}}function U(e){let t=e.indexOf("/");return t===-1?{remote:e,module:""}:{remote:e.slice(0,t),module:H(e.slice(t+1))}}function H(e){return e?e.startsWith(".")?e:`./${e}`:""}async function re(e,t){let{remote:r,module:l}=U(e);try{w.beforeLoadRemote?.({remote:r,module:l})}catch(g){console.warn("[fulgurjs] beforeLoadRemote hook error (ignored):",g)}let h=i.get(r);if(!h)throw new b(S.REMOTE_UNKNOWN,`unknown remote "${r}". Register it via federation({ remotes }) or registerRemote().`,{remote:r});let a;try{let g=l&&h.manifestUrl?K(e):Promise.resolve();[a]=await Promise.all([V(h,{retries:t?.retries}),g])}catch(g){if(t?.fallbackModule)return console.error(`[fulgurjs] loadRemote("${e}") failed; returning fallbackModule (\u663E\u5F0F\u964D\u7EA7\uFF0C\u9519\u8BEF\u5DF2\u900F\u51FA)`,g),$({remote:r,error:g}),await t.fallbackModule();throw g}if(!l)return a;let p=`${r}@${h.shareScope||"default"}#${l}`;n.has(p)||n.set(p,a.get(l).catch(g=>{throw n.delete(p),g instanceof b||g?.code?g:new b(S.REMOTE_LOAD_FAILED,`failed to load module "${l}" from remote "${r}": ${String(g?.message??g)}`,{remote:r,module:l})}));let d;try{d=await n.get(p)}catch(g){if(t?.fallbackModule)return console.error(`[fulgurjs] loadRemote("${e}") failed; returning fallbackModule (\u663E\u5F0F\u964D\u7EA7\uFF0C\u9519\u8BEF\u5DF2\u900F\u51FA)`,g),$({remote:r,error:g}),await t.fallbackModule();throw g}try{w.afterLoadRemote?.({remote:r,module:l,module_ns:d})}catch(g){console.warn("[fulgurjs] afterLoadRemote hook error (ignored):",g)}if(d&&typeof d=="object"&&Object.keys(d).length===0){let g=new b(S.EMPTY_EXPORTS,`no exports: ${l} @ ${r}`,{remote:r,module:l});$({remote:r,error:g}),console.error(`[fulgurjs:MFU-009] ${g.message}`)}return d}function te(e){let t=i.get(e);if(!t)throw new b(S.REMOTE_UNKNOWN,`unknown remote "${e}"`,{remote:e});return V(t)}async function K(e,t={}){let{remote:r,module:l}=U(e),h=i.get(r);if(!h)throw new b(S.REMOTE_UNKNOWN,`unknown remote "${r}"`,{remote:r});let a=t.mode??"preload",p=(m,E)=>{if(m.sheet)return Promise.resolve();let c=y.get(m);if(c)return c;let u=new Promise(k=>{let v=()=>{m.removeEventListener("load",v),m.removeEventListener("error",O),k()},O=()=>{$({remote:r,error:new b(S.PRELOAD_FAILED,`failed to load remote stylesheet ${E}`,{remote:r})}),v()};m.addEventListener("load",v,{once:!0}),m.addEventListener("error",O,{once:!0}),m.sheet&&v()});return y.set(m,u),u},d=(m,E)=>{if(typeof document>"u")return Promise.resolve();let c=document.querySelector(`link[href="${m}"]`);if(c)return E==="style"&&a==="preload"?y.get(c)??Promise.resolve():Promise.resolve();let u=document.createElement("link");u.rel=E==="style"?"stylesheet":"modulepreload",u.href=m,a==="prefetch"&&(u.fetchPriority="low");let k=E==="style"?p(u,m):Promise.resolve();return document.head.appendChild(u),E==="style"&&a==="preload"?k:Promise.resolve()},g=m=>{let E=typeof document<"u"?document.baseURI:void 0,c=new URL(h.entry,E||"http://fulgurjs.invalid/");return!c.pathname.endsWith("/")&&!c.pathname.split("/").pop().includes(".")&&(c.pathname+="/"),new URL(m,c).href};try{if(h.manifestUrl){let m=f.get(h.manifestUrl);m||(m=fetch(h.manifestUrl,{cache:"no-cache",signal:AbortSignal.timeout(8e3)}).then(c=>c.ok?c.json():void 0),f.set(h.manifestUrl,m),m.then(c=>{c||f.delete(h.manifestUrl)},()=>f.delete(h.manifestUrl)));let E=await m;if(E){let c=E.schemaVersion;if(typeof c=="number"&&(!Number.isInteger(c)||c>1)){$({remote:r,error:new b(S.PRELOAD_FAILED,`unsupported manifest schemaVersion ${String(c)} (expected 1)`,{remote:r})}),await d(h.entry,"modulepreload");return}await d(h.entry,"modulepreload");let u=E.exposes,k=Array.isArray(u)?u.filter(_=>_&&typeof _=="object").map(_=>[String(_.name??""),_]):u&&typeof u=="object"?Object.entries(u):[],v=l?k.filter(([_,L])=>H(String(L?.name??_))===l):k,O=[];for(let[,_]of v)if(typeof _?.file=="string"&&await d(g(_.file),"modulepreload"),Array.isArray(_?.css))for(let L of _.css){if(typeof L!="string")continue;let ne=d(g(L),"style");a==="preload"&&O.push(ne)}await Promise.all(O);return}}await d(h.entry,"modulepreload")}catch(m){$({remote:r,error:new b(S.PRELOAD_FAILED,String(m?.message??m),{remote:r})})}}return typeof window>"u"||(window.__FULGURJS_SCOPE__=o,window.__FULGURJS_INFO__={get remotes(){let e={};for(let[t,r]of i)e[t]={...r.debug,entry:r.entry};return e},errors:[]}),{shareScopeMap:o,initSharing:B,registerShare:J,registerRemotes:F,registerRemote:X,registerPlugins:Y,loadShare:Z,loadRemote:re,getContainer:te,preloadRemote:K,parseSpec:U}}var W=globalThis,M=W.__FULGURJS_RUNTIME__??Ee();W.__FULGURJS_RUNTIME__=M;try{Object.freeze(M)}catch{}var _e=q;function ke(){return M}var je=M.initSharing,Oe=M.registerShare,Te=M.registerRemotes,Le=M.registerRemote,Ie=M.registerPlugins,Pe=M.loadShare,Ne=M.loadRemote,Ae=M.getContainer,Ue=M.preloadRemote,xe=M.parseSpec,Fe=M.shareScopeMap;function Ce(o){if(o&&typeof o=="object"&&"default"in o){let i=o.default;return i!==void 0?i:o}return o}var De=M;export{De as default,Ae as getContainer,ke as getRuntime,je as initSharing,Ne as loadRemote,Pe as loadShare,xe as parseSpec,Ue as preloadRemote,Ie as registerPlugins,Le as registerRemote,Te as registerRemotes,Oe as registerShare,M as runtime,Fe as shareScopeMap,Ce as unwrapDefault,_e as version};
package/dist/vue.cjs CHANGED
@@ -262,11 +262,15 @@ var DEFAULT_TIMEOUT = 15e3;
262
262
  var DEFAULT_RETRIES = 2;
263
263
  var BREAKER_THRESHOLD = 5;
264
264
  var BREAKER_RESET_MS = 3e4;
265
+ var BACKOFF_MAX_MS = 4e3;
266
+ var BACKOFF_JITTER = 0.2;
265
267
  function createRuntime() {
266
- const shareScopeMap2 = {};
268
+ const shareScopeMap2 = /* @__PURE__ */ Object.create(null);
267
269
  const remotes = /* @__PURE__ */ new Map();
268
270
  const plugins = [];
269
271
  const loadedModules = /* @__PURE__ */ new Map();
272
+ const remoteManifests = /* @__PURE__ */ new Map();
273
+ const stylesheetLoads = /* @__PURE__ */ new WeakMap();
270
274
  const containerInitScopes = /* @__PURE__ */ new WeakMap();
271
275
  const hooks = {};
272
276
  const applyPlugins = () => {
@@ -278,7 +282,7 @@ function createRuntime() {
278
282
  }
279
283
  }
280
284
  };
281
- const getScope = (name) => shareScopeMap2[name] ??= {};
285
+ const getScope = (name) => shareScopeMap2[name] ??= /* @__PURE__ */ Object.create(null);
282
286
  function emitError(info) {
283
287
  try {
284
288
  hooks.onRemoteError?.(info);
@@ -294,22 +298,49 @@ function createRuntime() {
294
298
  }
295
299
  function registerShare2(scopeName, name, version, get, opts = {}) {
296
300
  const scope = getScope(scopeName || "default");
297
- const byName = scope[name] ??= {};
298
- if (byName[version]) {
301
+ const own = Object.hasOwn(scope, name) ? scope[name] : void 0;
302
+ let byName = own && typeof own === "object" ? own : void 0;
303
+ if (!byName) {
304
+ byName = /* @__PURE__ */ Object.create(null);
305
+ scope[name] = byName;
306
+ }
307
+ if (Object.hasOwn(byName, version) && byName[version]) {
299
308
  return;
300
309
  }
301
310
  byName[version] = { version, get, from: opts.from ?? "unknown", eager: !!opts.eager, loaded: !!opts.loaded };
302
311
  }
312
+ function assertRemoteParams(r) {
313
+ const check = (v, name, int10) => {
314
+ if (v === void 0) return;
315
+ const ok = typeof v === "number" && (int10 ? Number.isInteger(v) && v >= 0 && v <= 10 : Number.isFinite(v) && v > 0);
316
+ if (!ok) {
317
+ throw new Error(
318
+ `[fulgurjs] registerRemote("${r.name}"): ${name} must be a ${int10 ? "integer 0..10" : "finite positive number"}, got ${String(v)}`
319
+ );
320
+ }
321
+ };
322
+ check(r.timeout, "timeout");
323
+ check(r.retries, "retries", true);
324
+ if (r.breaker) {
325
+ check(r.breaker.threshold, "breaker.threshold");
326
+ check(r.breaker.resetMs, "breaker.resetMs");
327
+ }
328
+ }
303
329
  function registerRemotes2(list) {
304
330
  for (const r of list) {
331
+ assertRemoteParams(r);
305
332
  const prev = remotes.get(r.name);
306
333
  remotes.set(r.name, {
307
334
  timeout: DEFAULT_TIMEOUT,
308
335
  retries: DEFAULT_RETRIES,
309
336
  ...prev,
310
337
  ...r,
338
+ // 熔断参数随最新配置刷新;计数状态(fails/openUntil)跨注册保留——半开窗口不被重置
339
+ breakerThreshold: r.breaker?.threshold ?? prev?.breakerThreshold ?? BREAKER_THRESHOLD,
340
+ breakerResetMs: r.breaker?.resetMs ?? prev?.breakerResetMs ?? BREAKER_RESET_MS,
311
341
  state: prev?.state ?? "idle",
312
342
  breakerState: prev?.breakerState ?? { fails: 0, openUntil: 0 },
343
+ entryInflight: prev?.entryInflight ?? /* @__PURE__ */ new Map(),
313
344
  debug: prev?.debug ?? { entry: r.entry, status: "idle" }
314
345
  });
315
346
  }
@@ -383,6 +414,14 @@ function createRuntime() {
383
414
  entry.loaded = true;
384
415
  return entry.get();
385
416
  }
417
+ function sanitizeUrl(u) {
418
+ try {
419
+ const x = new URL(u, typeof location < "u" ? location.href : "http://f.invalid");
420
+ return x.protocol + "//" + x.host + x.pathname;
421
+ } catch {
422
+ return String(u).replace(/[?#].*$/, "");
423
+ }
424
+ }
386
425
  function withTimeout(p, ms, label) {
387
426
  return new Promise((resolve, reject) => {
388
427
  const t = setTimeout(() => reject(new Error(`timeout after ${ms}ms: ${label}`)), ms);
@@ -415,14 +454,16 @@ function createRuntime() {
415
454
  }
416
455
  }
417
456
  async function importEntry(url, remote) {
418
- return withTimeout(
419
- import(
420
- /* @vite-ignore */
421
- url
422
- ),
423
- remote.timeout ?? DEFAULT_TIMEOUT,
424
- `load remoteEntry ${url}`
457
+ const cached = remote.entryInflight.get(url);
458
+ const inflight = cached ?? import(
459
+ /* @vite-ignore */
460
+ url
425
461
  );
462
+ if (!cached) {
463
+ remote.entryInflight.set(url, inflight);
464
+ inflight.catch(() => remote.entryInflight.delete(url));
465
+ }
466
+ return withTimeout(inflight, remote.timeout ?? DEFAULT_TIMEOUT, `load remoteEntry ${url}`);
426
467
  }
427
468
  async function acquireContainer(remote, overrides) {
428
469
  const scopeKey = remote.shareScope || "default";
@@ -453,7 +494,11 @@ function createRuntime() {
453
494
  const loaders = [];
454
495
  if (remote.promise) {
455
496
  loaders.push(async () => {
456
- const resolved = await remote.promise();
497
+ const resolved = await withTimeout(
498
+ remote.promise(),
499
+ remote.timeout ?? DEFAULT_TIMEOUT,
500
+ `resolve promise remote "${remote.name}"`
501
+ );
457
502
  const container2 = typeof resolved === "string" ? await importEntry(resolved, remote) : resolved;
458
503
  validateContainerInterface(container2, remote);
459
504
  return container2;
@@ -484,25 +529,26 @@ function createRuntime() {
484
529
  lastErr = err;
485
530
  if (err?.code) throw err;
486
531
  if (attempt < maxRetries) {
487
- await sleep(200 * 2 ** attempt);
532
+ await sleep(Math.min(BACKOFF_MAX_MS, 200 * 2 ** attempt) * (1 - BACKOFF_JITTER * (1 - 2 * Math.random())));
488
533
  }
489
534
  }
490
535
  }
491
536
  if (succeeded) break;
492
537
  }
493
538
  if (!succeeded || !container) {
539
+ const shown = sanitizeUrl(remote.entry);
494
540
  const hints = [];
495
541
  if (/^https?:\/\//.test(remote.entry) && typeof location !== "undefined") {
496
- hints.push(`1. is the remote dev server running? try opening ${remote.entry} in the browser \u2014 it must return JS, not HTML/an error`);
497
- hints.push(`2. is the URL correct in federation({ remotes })? dev and prod entries can differ`);
498
- hints.push(`3. CORS: the remote dev server must allow cross-origin requests (fulgurjs-federation enables server.cors automatically; a custom server config may have disabled it)`);
542
+ hints.push(`1. is the remote dev server running? open ${shown} in a browser \u2014 it must return JS, not HTML`);
543
+ hints.push(`2. is the URL correct in federation({ remotes })? dev/prod entries can differ`);
544
+ hints.push(`3. CORS: the remote dev server must allow cross-origin requests (check its server.cors)`);
499
545
  } else {
500
- hints.push(`1. is the remote deployed and reachable? try opening ${remote.entry} in the browser \u2014 it must return JS`);
501
- hints.push(`2. NGINX/CDN routing: the entry path must serve the remoteEntry JS file (check try_files / fallback rules)`);
546
+ hints.push(`1. is the remote deployed? open ${shown} in a browser \u2014 it must return JS`);
547
+ hints.push(`2. NGINX/CDN routing: the entry path must serve the remoteEntry JS (check try_files)`);
502
548
  }
503
549
  throw new FgError(
504
550
  ErrorCodes.REMOTE_LOAD_FAILED,
505
- `failed to load remote "${remote.name}" from ${remote.entry}: ${String(lastErr?.message ?? lastErr)}
551
+ `failed to load remote "${remote.name}" from ${shown}: ${String(lastErr?.message ?? lastErr)}
506
552
  ` + hints.join("\n"),
507
553
  { remote: remote.name }
508
554
  );
@@ -534,8 +580,8 @@ function createRuntime() {
534
580
  remote.debug.status = "failed";
535
581
  remote.debug.error = String(err?.message ?? err);
536
582
  b.fails += 1;
537
- if (b.fails >= BREAKER_THRESHOLD) {
538
- b.openUntil = Date.now() + BREAKER_RESET_MS;
583
+ if (b.fails >= remote.breakerThreshold) {
584
+ b.openUntil = Date.now() + remote.breakerResetMs;
539
585
  b.fails = 0;
540
586
  }
541
587
  if (err instanceof FgError) {
@@ -553,7 +599,7 @@ function createRuntime() {
553
599
  throw wrapped;
554
600
  }
555
601
  }
556
- function parseSpec(spec) {
602
+ function parseSpec2(spec) {
557
603
  const idx = spec.indexOf("/");
558
604
  if (idx === -1) return { remote: spec, module: "" };
559
605
  return { remote: spec.slice(0, idx), module: normalizeModuleName(spec.slice(idx + 1)) };
@@ -563,8 +609,12 @@ function createRuntime() {
563
609
  return m.startsWith(".") ? m : `./${m}`;
564
610
  }
565
611
  async function loadRemote2(spec, opts) {
566
- const { remote: name, module: module2 } = parseSpec(spec);
567
- hooks.beforeLoadRemote?.({ remote: name, module: module2 });
612
+ const { remote: name, module: module2 } = parseSpec2(spec);
613
+ try {
614
+ hooks.beforeLoadRemote?.({ remote: name, module: module2 });
615
+ } catch (hookErr) {
616
+ console.warn("[fulgurjs] beforeLoadRemote hook error (ignored):", hookErr);
617
+ }
568
618
  const remote = remotes.get(name);
569
619
  if (!remote) {
570
620
  throw new FgError(
@@ -575,7 +625,8 @@ function createRuntime() {
575
625
  }
576
626
  let container;
577
627
  try {
578
- container = await acquireContainer(remote, { retries: opts?.retries });
628
+ const stylesReady = module2 && remote.manifestUrl ? preloadRemote2(spec) : Promise.resolve();
629
+ [container] = await Promise.all([acquireContainer(remote, { retries: opts?.retries }), stylesReady]);
579
630
  } catch (err) {
580
631
  if (opts?.fallbackModule) {
581
632
  console.error(`[fulgurjs] loadRemote("${spec}") failed; returning fallbackModule (\u663E\u5F0F\u964D\u7EA7\uFF0C\u9519\u8BEF\u5DF2\u900F\u51FA)`, err);
@@ -611,7 +662,11 @@ function createRuntime() {
611
662
  }
612
663
  throw err;
613
664
  }
614
- hooks.afterLoadRemote?.({ remote: name, module: module2, module_ns: ns });
665
+ try {
666
+ hooks.afterLoadRemote?.({ remote: name, module: module2, module_ns: ns });
667
+ } catch (hookErr) {
668
+ console.warn("[fulgurjs] afterLoadRemote hook error (ignored):", hookErr);
669
+ }
615
670
  if (ns && typeof ns === "object" && Object.keys(ns).length === 0) {
616
671
  const err = new FgError(
617
672
  ErrorCodes.EMPTY_EXPORTS,
@@ -631,15 +686,44 @@ function createRuntime() {
631
686
  return acquireContainer(remote);
632
687
  }
633
688
  async function preloadRemote2(spec, opts = {}) {
634
- const { remote: name } = parseSpec(spec);
689
+ const { remote: name, module: module2 } = parseSpec2(spec);
635
690
  const remote = remotes.get(name);
636
691
  if (!remote) {
637
692
  throw new FgError(ErrorCodes.REMOTE_UNKNOWN, `unknown remote "${name}"`, { remote: name });
638
693
  }
639
694
  const mode = opts.mode ?? "preload";
695
+ const waitForStylesheet = (link, href) => {
696
+ if (link.sheet) return Promise.resolve();
697
+ const pending = stylesheetLoads.get(link);
698
+ if (pending) return pending;
699
+ const loaded = new Promise((resolve) => {
700
+ const finish = () => {
701
+ link.removeEventListener("load", finish);
702
+ link.removeEventListener("error", failed);
703
+ resolve();
704
+ };
705
+ const failed = () => {
706
+ emitError({
707
+ remote: name,
708
+ error: new FgError(ErrorCodes.PRELOAD_FAILED, `failed to load remote stylesheet ${href}`, {
709
+ remote: name
710
+ })
711
+ });
712
+ finish();
713
+ };
714
+ link.addEventListener("load", finish, { once: true });
715
+ link.addEventListener("error", failed, { once: true });
716
+ if (link.sheet) finish();
717
+ });
718
+ stylesheetLoads.set(link, loaded);
719
+ return loaded;
720
+ };
640
721
  const inject = (href, as) => {
641
- if (typeof document === "undefined") return;
642
- if (document.querySelector(`link[href="${href}"]`)) return;
722
+ if (typeof document === "undefined") return Promise.resolve();
723
+ const existing = document.querySelector(`link[href="${href}"]`);
724
+ if (existing) {
725
+ return as === "style" && mode === "preload" ? stylesheetLoads.get(existing) ?? Promise.resolve() : Promise.resolve();
726
+ }
643
727
  const link = document.createElement("link");
644
728
  link.rel = as === "style" ? "stylesheet" : "modulepreload";
645
729
  link.href = href;
@@ -647,24 +731,69 @@ function createRuntime() {
647
731
  ;
648
732
  link.fetchPriority = "low";
649
733
  }
734
+ const loaded = as === "style" ? waitForStylesheet(link, href) : Promise.resolve();
650
735
  document.head.appendChild(link);
736
+ return as === "style" && mode === "preload" ? loaded : Promise.resolve();
737
+ };
738
+ const resolveAssetUrl = (asset) => {
739
+ const pageUrl = typeof document !== "undefined" ? document.baseURI : void 0;
740
+ const base = new URL(remote.entry, pageUrl || "http://fulgurjs.invalid/");
741
+ if (!base.pathname.endsWith("/") && !base.pathname.split("/").pop().includes(".")) {
742
+ base.pathname += "/";
743
+ }
744
+ return new URL(asset, base).href;
651
745
  };
652
746
  try {
653
747
  if (remote.manifestUrl) {
654
- const res = await fetch(remote.manifestUrl, { cache: "no-cache" });
655
- if (res.ok) {
656
- const manifest = await res.json();
657
- inject(remote.entry, "modulepreload");
658
- const exposes = manifest.exposes ?? {};
659
- for (const key of Object.keys(exposes)) {
660
- const item = exposes[key];
661
- if (item?.file) inject(new URL(item.file, remote.entry).href, "modulepreload");
662
- for (const css of item?.css ?? []) inject(new URL(css, remote.entry).href, "style");
748
+ let manifestPromise = remoteManifests.get(remote.manifestUrl);
749
+ if (!manifestPromise) {
750
+ manifestPromise = fetch(remote.manifestUrl, {
751
+ cache: "no-cache",
752
+ signal: AbortSignal.timeout(8e3)
753
+ }).then((res) => res.ok ? res.json() : void 0);
754
+ remoteManifests.set(remote.manifestUrl, manifestPromise);
755
+ manifestPromise.then(
756
+ (manifest2) => {
757
+ if (!manifest2) remoteManifests.delete(remote.manifestUrl);
758
+ },
759
+ () => remoteManifests.delete(remote.manifestUrl)
760
+ );
761
+ }
762
+ const manifest = await manifestPromise;
763
+ if (manifest) {
764
+ const sv = manifest.schemaVersion;
765
+ if (typeof sv === "number" && (!Number.isInteger(sv) || sv > 1)) {
766
+ emitError({
767
+ remote: name,
768
+ error: new FgError(ErrorCodes.PRELOAD_FAILED, `unsupported manifest schemaVersion ${String(sv)} (expected 1)`, {
769
+ remote: name
770
+ })
771
+ });
772
+ await inject(remote.entry, "modulepreload");
773
+ return;
774
+ }
775
+ await inject(remote.entry, "modulepreload");
776
+ const exposes = manifest.exposes;
777
+ const entries = Array.isArray(exposes) ? exposes.filter((item) => item && typeof item === "object").map((item) => [String(item.name ?? ""), item]) : exposes && typeof exposes === "object" ? Object.entries(exposes) : [];
778
+ const selected = module2 ? entries.filter(([key, item]) => normalizeModuleName(String(item?.name ?? key)) === module2) : entries;
779
+ const cssLoads = [];
780
+ for (const [, item] of selected) {
781
+ if (typeof item?.file === "string") {
782
+ await inject(resolveAssetUrl(item.file), "modulepreload");
783
+ }
784
+ if (Array.isArray(item?.css)) {
785
+ for (const css of item.css) {
786
+ if (typeof css !== "string") continue;
787
+ const loading = inject(resolveAssetUrl(css), "style");
788
+ if (mode === "preload") cssLoads.push(loading);
789
+ }
790
+ }
663
791
  }
792
+ await Promise.all(cssLoads);
664
793
  return;
665
794
  }
666
795
  }
667
- inject(remote.entry, "modulepreload");
796
+ await inject(remote.entry, "modulepreload");
668
797
  } catch (err) {
669
798
  emitError({
670
799
  remote: name,
@@ -700,7 +829,7 @@ function createRuntime() {
700
829
  loadRemote: loadRemote2,
701
830
  getContainer: getContainer2,
702
831
  preloadRemote: preloadRemote2,
703
- parseSpec
832
+ parseSpec: parseSpec2
704
833
  };
705
834
  }
706
835
  var g = globalThis;
@@ -719,6 +848,7 @@ var loadShare = runtime.loadShare;
719
848
  var loadRemote = runtime.loadRemote;
720
849
  var getContainer = runtime.getContainer;
721
850
  var preloadRemote = runtime.preloadRemote;
851
+ var parseSpec = runtime.parseSpec;
722
852
  var shareScopeMap = runtime.shareScopeMap;
723
853
 
724
854
  // src/vue.ts