@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/CHANGELOG.md +27 -0
- package/README.md +68 -9
- package/client.d.ts +17 -1
- package/dist/{chunk-UDLQALHC.js → chunk-RP6HNIQT.js} +1 -1
- package/dist/cli.js +147 -3
- package/dist/index.cjs +536 -86
- package/dist/index.d.cts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +538 -86
- package/dist/runtime.js +3 -3
- package/dist/vue.cjs +170 -40
- package/dist/vue.js +171 -41
- package/docs/P5-vite7-8/345/205/274/345/256/271/347/237/251/351/230/265.md +4 -2
- package/package.json +6 -6
package/dist/runtime.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var
|
|
2
|
-
`+
|
|
3
|
-
`),{remote:e.name})}
|
|
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
|
|
298
|
-
|
|
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
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
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
|
|
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?
|
|
497
|
-
hints.push(`2. is the URL correct in federation({ remotes })? dev
|
|
498
|
-
hints.push(`3. CORS: the remote dev server must allow cross-origin requests (
|
|
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
|
|
501
|
-
hints.push(`2. NGINX/CDN routing: the entry path must serve the remoteEntry JS
|
|
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 ${
|
|
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 >=
|
|
538
|
-
b.openUntil = Date.now() +
|
|
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
|
|
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 } =
|
|
567
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 } =
|
|
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
|
-
|
|
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
|
-
|
|
655
|
-
if (
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
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
|