UNPKG

@amadeus-it-group/microfrontends

Version:
3 lines (2 loc) 9.8 kB
const e={handshake:!0,error:!0,disconnect:!0,declare_messages:!0,connect:!0};function s(s){return void 0!==e[s?.type]}class t extends Error{messageObject;constructor(e,s){super(s),this.messageObject=e,this.name="MessageError"}}let n=!1;function o(e){n=e}function i(...e){n&&console.log(...e)}function r(e){if(!(e&&e.from&&e.to&&"string"==typeof e.from&&Array.isArray(e.to)))throw new t(e,"Message should have 'from'(string) and 'to'(string|string[]) properties");const{payload:s}=e;if(!(s&&s.type&&s.version&&"string"==typeof s.type&&"string"==typeof s.version))throw new t(e,"Message should have 'payload' property that has 'type'(string) and 'version'(string) defined")}class d extends EventTarget{otherPort=null;onmessage=null;onmessageerror=null;postMessage(e){const s=new MessageEvent("message",{data:structuredClone(e)});this.otherPort?.dispatchEvent(s),this.otherPort?.onmessage?.call(this.otherPort,s)}start(){}close(){}}class h{port1;port2;constructor(){this.port1=new d,this.port2=new d,this.port1.otherPort=this.port2,this.port2.otherPort=this.port1}}class a{id;#e=null;#s=null;#t=null;#n=null;#o=null;#i=!1;#r=[];#d=null;#h=null;constructor(e){this.id=e}listen(e,s){const{hostOrigin:t,hostWindow:n}=this.#a(s);return this.#n=e,i(`EP(${this.id}): waiting for connections from '${e}' at ${t}`),this.#o||(this.#o=new Promise((o=>{this.#t=d=>{let h;d instanceof CustomEvent?(h=d.detail,i(`EP(${this.id}): received 'CustomEvent'`,d)):(h=d,i(`EP(${this.id}): received 'postMessage'`,d));const{origin:a,ports:c,source:g}=h,p=h.data;try{r(p);const{payload:d}=p;if("handshake"===d.type&&d.endpointId===this.id&&d.remoteId===e&&(a===t||"null"===a&&g===n)){this.#s?.close(),this.#s=c[0],this.#n=d.remoteId,this.#s.onmessage=e=>{const s=e.data;i(`EP(${this.id}): '${d.type}' message received from '${this.#n??"?"}':`,s),this.#c(s)};const t=this.#g(e,s.knownPeers);i(`EP(${this.id}): handshake received from '${e}', sending handshake back`,t),this.#d?.(p),this.#s.postMessage(t),this.#i=!0,this.#p(),o((()=>this.disconnect()))}}catch{}},window.addEventListener("message",this.#t),window.addEventListener("handshake",this.#t)}))),this.#o}connect(e,s){const{hostOrigin:t,hostWindow:n}=this.#a(s);return this.#n=e,i(`EP(${this.id}): connecting to '${e}' at ${t}`),this.#o||(this.#o=new Promise(((o,r)=>{this.#e=window===n?new h:new MessageChannel,this.#s=this.#e.port1,this.#s.onmessage=s=>{const n=s.data,d=n.payload;i(`EP(${this.id}): '${d.type}' message received from '${this.#n??"?"}':`,n),this.#i?this.#c(n):"handshake"===d.type?d.endpointId===this.id&&d.remoteId===e&&(this.#n=d.remoteId,i(`EP(${this.id}): handshake received from ${this.remoteId}:`,t),this.#d?.(n),this.#i=!0,this.#p(),o((()=>this.disconnect()))):(i(`EP(${this.id}): handshake was expected, got:`,n),r(`Handshake was expected, got: ${JSON.stringify(n)}`))};const d=this.#g(e,s.knownPeers);if(window===n){const s={data:d,origin:t,ports:[this.#e.port2]};i(`EP(${this.id}): sending 'CustomEvent' handshake to '${e}':`,d),window.dispatchEvent(new CustomEvent("handshake",{detail:s}))}else i(`EP(${this.id}): sending 'postMessage' handshake to '${e}':`,d),n.postMessage(d,{targetOrigin:t,transfer:[this.#e.port2]})}))),this.#o}get connected(){return this.#i}get remoteId(){return this.#n}disconnect(){this.#n=null,this.#d=null,this.#h=null,this.#o=null,this.#i=!1,this.#s?.close(),this.#s=null,this.#e=null,window.removeEventListener("message",this.#t),window.removeEventListener("handshake",this.#t),this.#t=null}send(e){this.#i&&this.#s?(i(`EP(${this.id}): sending message '${e.payload.type}' to '${this.#n}':`,e),this.#s.postMessage(e)):(i(`EP(${this.id}): queueing message:`,e),"connect"===e.payload.type?this.#r.unshift(structuredClone(e)):this.#r.push(structuredClone(e)))}#a(e){const s=e?.window||window,t=e?.origin||window.origin;return function(e){const s=URL.parse(e);if(!s)throw new Error(`'${e}' is not a valid URL`);if(s.origin!==e)throw new Error(`'${e}' is not a valid origin, did you mean '${s.origin}'?`)}(t),this.#d=e?.onMessage,this.#h=e?.onError||(e=>console.warn(e)),{hostOrigin:t,hostWindow:s}}#g(e,s){return{from:this.id,to:[e],payload:{type:"handshake",version:"1.0",endpointId:e,remoteId:this.id,knownPeers:new Map(s)}}}#c(e){try{r(e),"handshake"===e.payload.type?console.warn(`EP(${this.id}): Unexpected handshake message received:`,e):this.#d?.(e)}catch(e){i(`EP(${this.id}):`,e),this.#h?.(e)}}#p(){for(const e of this.#r)this.send(e);this.#r.length=0}}function c(e,s){const n=s.knownPeers.get(s.id),{payload:o}=e;if(n&&!n.find((({type:e})=>e===o.type))){const s=[...new Set(n.map((({type:e})=>e)))];throw new t(e,`Unknown message type "${o.type}". Known types: ${JSON.stringify(s)}`)}}function g(e,s){const n=s.knownPeers.get(s.id),{payload:o}=e;if(n&&!n?.find((({type:e,version:s})=>e===o.type&&s===o.version))){const s=n.filter((({type:e})=>e===o.type)).map((({version:e})=>e));throw new t(e,`Unknown message version "${o.version}". Known versions: ${JSON.stringify(s)}`)}}function p(){return[{description:"Check that message is known",check:c},{description:"Check that message version is known",check:g}]}const l={unsubscribe:()=>{}};class m{#l=new Set;get subscribers(){return this.#l}subscribe(e){return e?(this.#l.add(e),{unsubscribe:()=>{this.#l.delete(e)}}):l}emit(e){for(const s of this.#l)"function"==typeof s?s(e):s.next?.(e)}[Symbol.observable](){return this}"@@observable"(){return this}}const u={window:window,origin:window.origin};class w{#m;#u=new Map;#w=new Map;#f=new m;#E=new m;#k=new m;#P=[...p()];#y=new Map;#r=[];constructor(e){if(this.#m=e.id,this.#y.set(this.id,[]),e.knownMessages)for(const s of e.knownMessages)this.registerMessage(s);i(`PEER(${this.id}): created`,this.#y)}get id(){return this.#m}get knownPeers(){return this.#y}get messages(){return this.#f}get serviceMessages(){return this.#E}get errors(){return this.#k}connect(e,s){i(`PEER(${this.id}): connecting to '${e}'`);const t=new a(this.id);return this.#u.set(e,t),t.connect(e,{...u,...s,knownPeers:this.#y,onMessage:e=>this.#$(t,e),onError:e=>this.#v(t,e)})}send(e,s){this.#M(e,s)}listen(e,s){i(`PEER(${this.id}): listening for '${e}'`);const t=new a(this.id);return this.#u.set(e,t),t.listen(e,{...u,...s,knownPeers:this.#y,onMessage:e=>this.#$(t,e),onError:e=>this.#v(t,e)})}registerMessage(e){const s=this.#y.get(this.id);s.find((s=>s.type===e.type&&s.version===e.version))||s.push(e)}log(){const e=[...this.#u.values()].map((e=>`${e.id}:${e.connected?e.remoteId:e.remoteId+"*"}`)),s=[...this.#w].map((([e,s])=>`${e}: ${[...s].join(", ")}`));console.log(`PEER(${this.id}):`,e,s,this.#y)}disconnect(e){if(e){const s=this.#u.get(e);s&&this.#b(s)}else for(const e of this.#u.values())this.#b(e)}#b(e){const s=e.remoteId,t=[...this.#w.get(s)||[]],n=[this.id];for(const[e,t]of this.#w)e!==s&&n.push(...t);e.connected&&e.send({from:this.id,to:[],payload:{type:"disconnect",version:"1.0",disconnected:this.id,unreachable:n}}),this.#w.delete(s);for(const e of t)this.#y.delete(e);this.#u.delete(s),e.disconnect(),this.#M({type:"disconnect",version:"1.0",disconnected:s,unreachable:t}),i(`PEER(${this.id}): disconnected from '${s}'`,this.#u,this.#y)}#v(e,s){this.#k.emit(s),0===this.#k.subscribers.size&&console.error(e),e.send({from:this.id,to:[s.messageObject.from],payload:{type:"error",version:"1.0",error:s.message,message:s.messageObject}})}#$(e,n){i(`PEER(${this.id}): received message`,n,this.#y);const{payload:o}=n;if(s(o))switch(o.type){case"handshake":{i(`PEER(${this.id}): handshake message from '${o.remoteId}'`,o);const s=[...this.knownPeers.keys()];for(const[e,s]of o.knownPeers)this.#I(e,s);this.#w.set(o.remoteId,new Set(o.knownPeers.keys()));for(const s of this.#u.values())s!==e&&s.connected&&s.send({from:this.id,to:[],payload:{type:"connect",version:"1.0",knownPeers:this.#y,connected:[...o.knownPeers.keys()]}});e.send({from:this.id,to:[o.remoteId],payload:{type:"connect",version:"1.0",knownPeers:new Map,connected:s}});break}case"connect":if(n.to.includes(this.id)||0===n.to.length){i(`PEER(${this.id}): connect message from '${e.remoteId}'`,o);for(const[e,s]of o.knownPeers)this.#I(e,s);const s=this.#w.get(e.remoteId);for(const e of o.knownPeers.keys())e===this.id||[...this.#u.keys()].includes(e)||s.add(e);this.#p(),this.#E.emit({...n,payload:{...o,knownPeers:[]}})}this.#R(e,n);break;case"disconnect":i(`PEER(${this.id}): disconnect message from '${e.remoteId}'`,o);this.#u.get(o.disconnected)&&e.disconnect();for(const e of o.unreachable){this.#y.delete(e),this.#u.delete(e);for(const[s,t]of this.#w)t.delete(e),0===t.size&&this.#w.delete(s)}this.#E.emit(n),this.#R(e,n);break;case"declare_messages":i(`PEER(${this.id}): declare_messages from '${n.from}'`,o),this.#I(n.from,o.messages),this.#E.emit(n),this.#R(e,n);break;case"error":i(`PEER(${this.id}): 'error' message from '${n.from}'`,o),n.to.includes(this.id)?this.#E.emit(n):this.#R(e,n);break;default:i(`PEER(${this.id}):`,`unknown message type: ${o.type}`),this.#v(e,new t(n,`unknown message type: ${o.type}`))}else try{if(n.to.includes(this.id)||0===n.to.length){for(const{check:e}of this.#P)e(n,this);this.#f.emit(n)}}catch(s){i(`PEER(${this.id}):`,s),this.#v(e,s)}finally{this.#R(e,n)}}#M(e,s){const t={from:this.id,to:s?.to?Array.isArray(s.to)?s.to:[s.to]:[],payload:e};if(this.#y.size>1)for(const e of this.#u.values())e.send(t);else this.#r.push(t)}#R(e,s){if(1!==s.to.length||s.to[0]!==this.id)for(const t of this.#u.values())t!==e&&t.connected&&t.send(s)}#I(e,s){if(this.id!==e){const t=this.#y.get(e);if(t)for(const e of s)t.find((s=>s.type===e.type&&s.version===e.version))||t.push(e);else this.#y.set(e,s)}}#p(){for(const e of this.#r)for(const s of this.#u.values())s.send(e);this.#r.length=0}}export{t as MessageError,w as MessagePeer,o as enableLogging,s as isServiceMessage}; //# sourceMappingURL=microfrontends.min.mjs.map