@fulgurjs/federation 2.0.3 → 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 re=/^v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-((?:[0-9A-Za-z-]+)(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z.-]+)?$/;function P(t){let n=re.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 te(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 m=t[r],d=n[r];if(m===void 0)return-1;if(d===void 0)return 1;let S=/^\d+$/.test(m),w=/^\d+$/.test(d);if(S&&w){let L=Number(m)-Number(d);if(L!==0)return L}else{if(S!==w)return S?-1:1;if(m!==d)return m<d?-1:1}}return 0}function T(t,n){let o=P(t),r=P(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:te(o.pre,r.pre)}var ne=/^[a-z]+[+:-]/i;function N(t,n){if(n===!1||n==null||n==="")return!0;let o=String(n).trim();return!o||o==="*"||o==="latest"||ne.test(o)?!0:o.split("||").some(r=>oe(t,r.trim()))}function oe(t,n){if(!n||n==="*")return!P(t)?.pre.length;let o=n.split(/\s+-\s+/);if(o.length===2){let d=o[0].trim(),S=o[1].trim();return!(!N(t,`>=${d}`)||!N(t,`<=${S}`))}let r=P(t);if(!r)return!1;let m=ie(n);if(m===null)return!0;for(let d of m)if(!ue(r,t,d))return!1;return!(r.pre.length>0&&!m.some(d=>d.ver.pre.length>0)&&!m.some(S=>S.ver.major===r.major&&S.ver.minor===r.minor&&S.ver.patch===r.patch))}function ie(t){let n=[],o=t.trim().split(/\s+/).filter(Boolean);if(o.length===0)return null;for(let r of o){let m=/^(>=|<=|>|<|=|\^|~)?(.*)$/.exec(r);if(!m)return null;let d=m[1],S=m[2],w=se(d,S);if(w===null)return null;n.push(...w)}return n}function se(t,n){let o=ce(n);if(!o)return null;let{ver:r,partial:m}=o;switch(t){case"^":{let d=ae(r);return d?[{op:">=",ver:r},{op:"<",ver:d}]:null}case"~":{let d=m>=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:d}]}case">=":return[{op:">=",ver:r}];case">":return m===1?[{op:">=",ver:{major:r.major+1,minor:0,patch:0,pre:[]}}]:m===2?[{op:">=",ver:{major:r.major,minor:r.minor+1,patch:0,pre:[]}}]:[{op:">",ver:r}];case"<":return m===1?[{op:"<",ver:{major:r.major,minor:0,patch:0,pre:[]}}]:m===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 m===1?[{op:">=",ver:r},{op:"<",ver:{major:r.major+1,minor:0,patch:0,pre:[]}}]:m===2?[{op:">=",ver:r},{op:"<",ver:{major:r.major,minor:r.minor+1,patch:0,pre:[]}}]:[{op:"=",ver:r}];default:return null}}function ae(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 ce(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=[],m=0;for(let w of o.slice(0,3))if(/^\d+$/.test(w))r.push(Number(w)),m++;else{if(w==="x"||w==="X"||w==="*")break;break}let d=n.split("-"),S=d.length>1?d.slice(1).join("-").split("+")[0].split("."):[];return m===0&&S.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:S},partial:m}}function le(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 ue(t,n,o){let r=`${t.major}.${t.minor}.${t.patch}`+(t.pre.length?`-${t.pre.join(".")}`:"");if(o.upper)return!(!(T(r,`${o.ver.major}.${o.ver.minor}.${o.ver.patch}`+(o.ver.pre.length?`-${o.ver.pre.join(".")}`:""))>=0)||!(T(r,`${o.upper.major}.${o.upper.minor}.${o.upper.patch}`)<0));let m=T(r,`${o.ver.major}.${o.ver.minor}.${o.ver.patch}`+(o.ver.pre.length?`-${o.ver.pre.join(".")}`:"")),d=le(m,o.op);return o.ver.pre.length>0&&t.pre.length===0&&(o.op===">"||o.op==="<")?!1:d}var R=class extends Error{code;details;constructor(n,o,r){super(`[fulgurjs:${n}] ${o}`),this.name="FgError",this.code=n,this.details=r}},b={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 V="2.0.3";var H=15e3,K=2,me=5,fe=3e4;function pe(){let t={},n=new Map,o=[],r=new Map,m=new Map,d=new WeakMap,S=new WeakMap,w={},L=()=>{for(let e of o)try{e.init?.(w)}catch(i){console.warn("[fulgurjs] runtimePlugin init failed:",e.name,i)}},I=e=>t[e]??={};function $(e){try{w.onRemoteError?.(e)}catch{}typeof window<"u"&&window.dispatchEvent(new CustomEvent("fulgurjs:error",{detail:e}))}function z(e="default"){return I(e),t}function W(e,i,s,l,h={}){let c=I(e||"default"),f=c[i]??={};f[s]||(f[s]={version:s,get:l,from:h.from??"unknown",eager:!!h.eager,loaded:!!h.loaded})}function A(e){for(let i of e){let s=n.get(i.name);n.set(i.name,{timeout:H,retries:K,...s,...i,state:s?.state??"idle",breakerState:s?.breakerState??{fails:0,openUntil:0},debug:s?.debug??{entry:i.entry,status:"idle"}})}}function G(e){A([e])}function J(e){o.push(...e),L()}async function B(e,i={}){let s=i.shareScope||"default",l=i.shareKey||e,c=I(s)[l]||{},f=Object.keys(c),y=i.requiredVersion,g=f.filter(a=>y===void 0||y===!1||N(a,y)),u;if(i.singleton&&f.length>0){let a=f.filter(p=>c[p].loaded);if(u=[...a.length?a:f].sort(T).pop(),f.length>1||!g.includes(u)){let p=c[u],j=`singleton skew "${l}": req "${y??"any"}" use ${u} from ${p.from}`;if(i.strictVersion){let _=new R(b.SHARE_STRICT_VERSION,j,{shareKey:l,requiredVersion:y});throw $({remote:p.from,error:_}),_}console.warn(`[fulgurjs:MFU-010] ${j}`)}}else if(u=[...g].sort(T).pop(),!u){if(i.strictVersion){let p=new R(b.SHARE_STRICT_VERSION,`no satisfying version for "${l}" (required "${y}") in share scope "${s}"`);throw $({remote:l,error:p}),p}if(i.fallback)return i.fallback();let a=new R(b.SHARE_NOT_AVAILABLE,`shared "${l}" (${y??"any"}) unavailable in scope "${s}" (no local fallback)`,{shareKey:l,requiredVersion:y});throw $({remote:l,error:a}),a}let E=c[u];if(w.resolveShare){let a=await w.resolveShare({shareKey:l,shareScope:s,requiredVersion:y,picked:E,available:Object.values(c)});a&&(E=a)}return E.loaded=!0,E.get()}function X(e,i,s){return new Promise((l,h)=>{let c=setTimeout(()=>h(new Error(`timeout after ${i}ms: ${s}`)),i);e.then(f=>{clearTimeout(c),l(f)},f=>{clearTimeout(c),h(f)})})}let Y=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 s=`remoteEntry self-reported name "${e.name}" mismatches configured name "${i.name}"`;if(i.promise)console.warn(`[fulgurjs] ${s} (promise-based remote, continuing)`);else throw Object.assign(new Error(s),{code:b.REMOTE_NAME_MISMATCH})}}async function x(e,i){return X(import(e),i.timeout??H,`load remoteEntry ${e}`)}async function C(e,i){let s=e.shareScope||"default";if(e.container){let c=S.get(e.container);if(c&&c!==s)throw new R(b.CONTAINER_REINIT_CONFLICT,`container "${e.name}" already initialized with share scope "${c}", cannot re-init with "${s}"`);return e.container}if(e.containerPromise)return e.containerPromise;let l=e.breakerState;if(l.openUntil>Date.now()){let c=new R(b.REMOTE_LOAD_FAILED,`circuit breaker open for remote "${e.name}" until ${new Date(l.openUntil).toISOString()}`,{remote:e.name});throw $({remote:e.name,error:c}),c}e.state="loading",e.debug.status="loading";let h=(async()=>{let c=[];if(e.promise)c.push(async()=>{let a=await e.promise(),p=typeof a=="string"?await x(a,e):a;return k(p,e),p});else for(let a of[e.entry,...e.fallback??[]])c.push(async()=>{let p=await x(a,e);return k(p,e),p});let f,y,g=!1,u=i?.retries??e.retries??K;for(let a of c){for(let p=0;p<=u;p++){let j=performance.now();try{f=await a(),e.lastLoadMs=Math.round(performance.now()-j),e.debug.lastLoadMs=e.lastLoadMs,g=!0;break}catch(_){if(y=_,_?.code)throw _;p<u&&await Y(200*2**p)}}if(g)break}if(!g||!f){let a=[];throw/^https?:\/\//.test(e.entry)&&typeof location<"u"?(a.push(`1. is the remote dev server running? try opening ${e.entry} in the browser \u2014 it must return JS, not HTML/an error`),a.push("2. is the URL correct in federation({ remotes })? dev and prod entries can differ"),a.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)")):(a.push(`1. is the remote deployed and reachable? try opening ${e.entry} in the browser \u2014 it must return JS`),a.push("2. NGINX/CDN routing: the entry path must serve the remoteEntry JS file (check try_files / fallback rules)")),new R(b.REMOTE_LOAD_FAILED,`failed to load remote "${e.name}" from ${e.entry}: ${String(y?.message??y)}
2
- `+a.join(`
3
- `),{remote:e.name})}k(f,e);let E=S.get(f);if(E&&E!==s)throw new R(b.CONTAINER_REINIT_CONFLICT,`container "${e.name}" already initialized with share scope "${E}", cannot re-init with "${s}"`);return typeof f.init=="function"&&await f.init(t),S.set(f,s),e.container=f,e.state="loaded",e.debug.status="loaded",l.fails=0,f})();e.containerPromise=h;try{return await h}catch(c){if(e.containerPromise=void 0,e.state="failed",e.debug.status="failed",e.debug.error=String(c?.message??c),l.fails+=1,l.fails>=me&&(l.openUntil=Date.now()+fe,l.fails=0),c instanceof R)throw $({remote:e.name,error:c}),c;if(c?.code)throw $({remote:e.name,error:c}),c;let f=new R(b.REMOTE_LOAD_FAILED,String(c?.message??c),{remote:e.name});throw $({remote:e.name,error:f}),f}}function U(e){let i=e.indexOf("/");return i===-1?{remote:e,module:""}:{remote:e.slice(0,i),module:F(e.slice(i+1))}}function F(e){return e?e.startsWith(".")?e:`./${e}`:""}async function Z(e,i){let{remote:s,module:l}=U(e);w.beforeLoadRemote?.({remote:s,module:l});let h=n.get(s);if(!h)throw new R(b.REMOTE_UNKNOWN,`unknown remote "${s}". Register it via federation({ remotes }) or registerRemote().`,{remote:s});let c;try{let g=l&&h.manifestUrl?D(e):Promise.resolve();[c]=await Promise.all([C(h,{retries:i?.retries}),g])}catch(g){if(i?.fallbackModule)return console.error(`[fulgurjs] loadRemote("${e}") failed; returning fallbackModule (\u663E\u5F0F\u964D\u7EA7\uFF0C\u9519\u8BEF\u5DF2\u900F\u51FA)`,g),$({remote:s,error:g}),await i.fallbackModule();throw g}if(!l)return c;let f=`${s}@${h.shareScope||"default"}#${l}`;r.has(f)||r.set(f,c.get(l).catch(g=>{throw r.delete(f),g instanceof R||g?.code?g:new R(b.REMOTE_LOAD_FAILED,`failed to load module "${l}" from remote "${s}": ${String(g?.message??g)}`,{remote:s,module:l})}));let y;try{y=await r.get(f)}catch(g){if(i?.fallbackModule)return console.error(`[fulgurjs] loadRemote("${e}") failed; returning fallbackModule (\u663E\u5F0F\u964D\u7EA7\uFF0C\u9519\u8BEF\u5DF2\u900F\u51FA)`,g),$({remote:s,error:g}),await i.fallbackModule();throw g}if(w.afterLoadRemote?.({remote:s,module:l,module_ns:y}),y&&typeof y=="object"&&Object.keys(y).length===0){let g=new R(b.EMPTY_EXPORTS,`no exports: ${l} @ ${s}`,{remote:s,module:l});$({remote:s,error:g}),console.error(`[fulgurjs:MFU-009] ${g.message}`)}return y}function Q(e){let i=n.get(e);if(!i)throw new R(b.REMOTE_UNKNOWN,`unknown remote "${e}"`,{remote:e});return C(i)}async function D(e,i={}){let{remote:s,module:l}=U(e),h=n.get(s);if(!h)throw new R(b.REMOTE_UNKNOWN,`unknown remote "${s}"`,{remote:s});let c=i.mode??"preload",f=(u,E)=>{if(u.sheet)return Promise.resolve();let a=d.get(u);if(a)return a;let p=new Promise(j=>{let _=()=>{u.removeEventListener("load",_),u.removeEventListener("error",v),j()},v=()=>{$({remote:s,error:new R(b.PRELOAD_FAILED,`failed to load remote stylesheet ${E}`,{remote:s})}),_()};u.addEventListener("load",_,{once:!0}),u.addEventListener("error",v,{once:!0}),u.sheet&&_()});return d.set(u,p),p},y=(u,E)=>{if(typeof document>"u")return Promise.resolve();let a=document.querySelector(`link[href="${u}"]`);if(a)return E==="style"&&c==="preload"?d.get(a)??Promise.resolve():Promise.resolve();let p=document.createElement("link");p.rel=E==="style"?"stylesheet":"modulepreload",p.href=u,c==="prefetch"&&(p.fetchPriority="low");let j=E==="style"?f(p,u):Promise.resolve();return document.head.appendChild(p),E==="style"&&c==="preload"?j:Promise.resolve()},g=u=>{let E=typeof document<"u"?document.baseURI:void 0,a=new URL(h.entry,E||"http://fulgurjs.invalid/");return new URL(u,a).href};try{if(h.manifestUrl){let u=m.get(h.manifestUrl);u||(u=fetch(h.manifestUrl,{cache:"no-cache"}).then(a=>a.ok?a.json():void 0),m.set(h.manifestUrl,u),u.then(a=>{a||m.delete(h.manifestUrl)},()=>m.delete(h.manifestUrl)));let E=await u;if(E){await y(h.entry,"modulepreload");let a=E.exposes,p=Array.isArray(a)?a.filter(v=>v&&typeof v=="object").map(v=>[String(v.name??""),v]):a&&typeof a=="object"?Object.entries(a):[],j=l?p.filter(([v,O])=>F(String(O?.name??v))===l):p,_=[];for(let[,v]of j)if(typeof v?.file=="string"&&await y(g(v.file),"modulepreload"),Array.isArray(v?.css))for(let O of v.css){if(typeof O!="string")continue;let ee=y(g(O),"style");c==="preload"&&_.push(ee)}await Promise.all(_);return}}await y(h.entry,"modulepreload")}catch(u){$({remote:s,error:new R(b.PRELOAD_FAILED,String(u?.message??u),{remote:s})})}}return typeof window>"u"||(window.__FULGURJS_SCOPE__=t,window.__FULGURJS_INFO__={get remotes(){let e={};for(let[i,s]of n)e[i]={...s.debug,entry:s.entry};return e},errors:[]}),{shareScopeMap:t,initSharing:z,registerShare:W,registerRemotes:A,registerRemote:G,registerPlugins:J,loadShare:B,loadRemote:Z,getContainer:Q,preloadRemote:D,parseSpec:U}}var q=globalThis,M=q.__FULGURJS_RUNTIME__??pe();q.__FULGURJS_RUNTIME__=M;try{Object.freeze(M)}catch{}var we=V;function be(){return M}var ve=M.initSharing,_e=M.registerShare,Me=M.registerRemotes,$e=M.registerRemote,je=M.registerPlugins,Te=M.loadShare,Le=M.loadRemote,Oe=M.getContainer,Pe=M.preloadRemote,Ne=M.shareScopeMap;function Ie(t){if(t&&typeof t=="object"&&"default"in t){let n=t.default;return n!==void 0?n:t}return t}var ke=M;export{ke as default,Oe as getContainer,be as getRuntime,ve as initSharing,Le as loadRemote,Te as loadShare,Pe as preloadRemote,je as registerPlugins,$e as registerRemote,Me as registerRemotes,_e as registerShare,M as runtime,Ne as shareScopeMap,Ie as unwrapDefault,we 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,8 +262,10 @@ 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();
@@ -280,7 +282,7 @@ function createRuntime() {
280
282
  }
281
283
  }
282
284
  };
283
- const getScope = (name) => shareScopeMap2[name] ??= {};
285
+ const getScope = (name) => shareScopeMap2[name] ??= /* @__PURE__ */ Object.create(null);
284
286
  function emitError(info) {
285
287
  try {
286
288
  hooks.onRemoteError?.(info);
@@ -296,22 +298,49 @@ function createRuntime() {
296
298
  }
297
299
  function registerShare2(scopeName, name, version, get, opts = {}) {
298
300
  const scope = getScope(scopeName || "default");
299
- const byName = scope[name] ??= {};
300
- 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]) {
301
308
  return;
302
309
  }
303
310
  byName[version] = { version, get, from: opts.from ?? "unknown", eager: !!opts.eager, loaded: !!opts.loaded };
304
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
+ }
305
329
  function registerRemotes2(list) {
306
330
  for (const r of list) {
331
+ assertRemoteParams(r);
307
332
  const prev = remotes.get(r.name);
308
333
  remotes.set(r.name, {
309
334
  timeout: DEFAULT_TIMEOUT,
310
335
  retries: DEFAULT_RETRIES,
311
336
  ...prev,
312
337
  ...r,
338
+ // 熔断参数随最新配置刷新;计数状态(fails/openUntil)跨注册保留——半开窗口不被重置
339
+ breakerThreshold: r.breaker?.threshold ?? prev?.breakerThreshold ?? BREAKER_THRESHOLD,
340
+ breakerResetMs: r.breaker?.resetMs ?? prev?.breakerResetMs ?? BREAKER_RESET_MS,
313
341
  state: prev?.state ?? "idle",
314
342
  breakerState: prev?.breakerState ?? { fails: 0, openUntil: 0 },
343
+ entryInflight: prev?.entryInflight ?? /* @__PURE__ */ new Map(),
315
344
  debug: prev?.debug ?? { entry: r.entry, status: "idle" }
316
345
  });
317
346
  }
@@ -385,6 +414,14 @@ function createRuntime() {
385
414
  entry.loaded = true;
386
415
  return entry.get();
387
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
+ }
388
425
  function withTimeout(p, ms, label) {
389
426
  return new Promise((resolve, reject) => {
390
427
  const t = setTimeout(() => reject(new Error(`timeout after ${ms}ms: ${label}`)), ms);
@@ -417,14 +454,16 @@ function createRuntime() {
417
454
  }
418
455
  }
419
456
  async function importEntry(url, remote) {
420
- return withTimeout(
421
- import(
422
- /* @vite-ignore */
423
- url
424
- ),
425
- remote.timeout ?? DEFAULT_TIMEOUT,
426
- `load remoteEntry ${url}`
457
+ const cached = remote.entryInflight.get(url);
458
+ const inflight = cached ?? import(
459
+ /* @vite-ignore */
460
+ url
427
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}`);
428
467
  }
429
468
  async function acquireContainer(remote, overrides) {
430
469
  const scopeKey = remote.shareScope || "default";
@@ -455,7 +494,11 @@ function createRuntime() {
455
494
  const loaders = [];
456
495
  if (remote.promise) {
457
496
  loaders.push(async () => {
458
- 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
+ );
459
502
  const container2 = typeof resolved === "string" ? await importEntry(resolved, remote) : resolved;
460
503
  validateContainerInterface(container2, remote);
461
504
  return container2;
@@ -486,25 +529,26 @@ function createRuntime() {
486
529
  lastErr = err;
487
530
  if (err?.code) throw err;
488
531
  if (attempt < maxRetries) {
489
- await sleep(200 * 2 ** attempt);
532
+ await sleep(Math.min(BACKOFF_MAX_MS, 200 * 2 ** attempt) * (1 - BACKOFF_JITTER * (1 - 2 * Math.random())));
490
533
  }
491
534
  }
492
535
  }
493
536
  if (succeeded) break;
494
537
  }
495
538
  if (!succeeded || !container) {
539
+ const shown = sanitizeUrl(remote.entry);
496
540
  const hints = [];
497
541
  if (/^https?:\/\//.test(remote.entry) && typeof location !== "undefined") {
498
- 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`);
499
- hints.push(`2. is the URL correct in federation({ remotes })? dev and prod entries can differ`);
500
- 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)`);
501
545
  } else {
502
- hints.push(`1. is the remote deployed and reachable? try opening ${remote.entry} in the browser \u2014 it must return JS`);
503
- 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)`);
504
548
  }
505
549
  throw new FgError(
506
550
  ErrorCodes.REMOTE_LOAD_FAILED,
507
- `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)}
508
552
  ` + hints.join("\n"),
509
553
  { remote: remote.name }
510
554
  );
@@ -536,8 +580,8 @@ function createRuntime() {
536
580
  remote.debug.status = "failed";
537
581
  remote.debug.error = String(err?.message ?? err);
538
582
  b.fails += 1;
539
- if (b.fails >= BREAKER_THRESHOLD) {
540
- b.openUntil = Date.now() + BREAKER_RESET_MS;
583
+ if (b.fails >= remote.breakerThreshold) {
584
+ b.openUntil = Date.now() + remote.breakerResetMs;
541
585
  b.fails = 0;
542
586
  }
543
587
  if (err instanceof FgError) {
@@ -555,7 +599,7 @@ function createRuntime() {
555
599
  throw wrapped;
556
600
  }
557
601
  }
558
- function parseSpec(spec) {
602
+ function parseSpec2(spec) {
559
603
  const idx = spec.indexOf("/");
560
604
  if (idx === -1) return { remote: spec, module: "" };
561
605
  return { remote: spec.slice(0, idx), module: normalizeModuleName(spec.slice(idx + 1)) };
@@ -565,8 +609,12 @@ function createRuntime() {
565
609
  return m.startsWith(".") ? m : `./${m}`;
566
610
  }
567
611
  async function loadRemote2(spec, opts) {
568
- const { remote: name, module: module2 } = parseSpec(spec);
569
- 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
+ }
570
618
  const remote = remotes.get(name);
571
619
  if (!remote) {
572
620
  throw new FgError(
@@ -614,7 +662,11 @@ function createRuntime() {
614
662
  }
615
663
  throw err;
616
664
  }
617
- 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
+ }
618
670
  if (ns && typeof ns === "object" && Object.keys(ns).length === 0) {
619
671
  const err = new FgError(
620
672
  ErrorCodes.EMPTY_EXPORTS,
@@ -634,7 +686,7 @@ function createRuntime() {
634
686
  return acquireContainer(remote);
635
687
  }
636
688
  async function preloadRemote2(spec, opts = {}) {
637
- const { remote: name, module: module2 } = parseSpec(spec);
689
+ const { remote: name, module: module2 } = parseSpec2(spec);
638
690
  const remote = remotes.get(name);
639
691
  if (!remote) {
640
692
  throw new FgError(ErrorCodes.REMOTE_UNKNOWN, `unknown remote "${name}"`, { remote: name });
@@ -686,15 +738,19 @@ function createRuntime() {
686
738
  const resolveAssetUrl = (asset) => {
687
739
  const pageUrl = typeof document !== "undefined" ? document.baseURI : void 0;
688
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
+ }
689
744
  return new URL(asset, base).href;
690
745
  };
691
746
  try {
692
747
  if (remote.manifestUrl) {
693
748
  let manifestPromise = remoteManifests.get(remote.manifestUrl);
694
749
  if (!manifestPromise) {
695
- manifestPromise = fetch(remote.manifestUrl, { cache: "no-cache" }).then(
696
- (res) => res.ok ? res.json() : void 0
697
- );
750
+ manifestPromise = fetch(remote.manifestUrl, {
751
+ cache: "no-cache",
752
+ signal: AbortSignal.timeout(8e3)
753
+ }).then((res) => res.ok ? res.json() : void 0);
698
754
  remoteManifests.set(remote.manifestUrl, manifestPromise);
699
755
  manifestPromise.then(
700
756
  (manifest2) => {
@@ -705,6 +761,17 @@ function createRuntime() {
705
761
  }
706
762
  const manifest = await manifestPromise;
707
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
+ }
708
775
  await inject(remote.entry, "modulepreload");
709
776
  const exposes = manifest.exposes;
710
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) : [];
@@ -762,7 +829,7 @@ function createRuntime() {
762
829
  loadRemote: loadRemote2,
763
830
  getContainer: getContainer2,
764
831
  preloadRemote: preloadRemote2,
765
- parseSpec
832
+ parseSpec: parseSpec2
766
833
  };
767
834
  }
768
835
  var g = globalThis;
@@ -781,6 +848,7 @@ var loadShare = runtime.loadShare;
781
848
  var loadRemote = runtime.loadRemote;
782
849
  var getContainer = runtime.getContainer;
783
850
  var preloadRemote = runtime.preloadRemote;
851
+ var parseSpec = runtime.parseSpec;
784
852
  var shareScopeMap = runtime.shareScopeMap;
785
853
 
786
854
  // src/vue.ts