comctx
Version:
Use RPC to communicate easily across contexts in any JavaScript environment.
2 lines • 4.58 kB
JavaScript
const e={APPLY:`apply`,CALLBACK:`callback`,PING:`ping`,PONG:`pong`},t={PROVIDER:`provider`,INJECTOR:`injector`},n=n=>{let r=!!n?.type&&Object.values(e).includes(n.type),i=!!n?.id&&typeof n.id==`string`,a=!!n?.path&&Array.isArray(n.path)&&n.path.every(e=>typeof e==`string`),o=!!n?.sender&&Object.values(t).includes(n.sender),s=n?.callbackIds===void 0||Array.isArray(n.callbackIds)&&n.callbackIds.every(e=>typeof e==`string`),c=n?.args===void 0||Array.isArray(n.args),l=n?.error===void 0||typeof n.error==`string`,u=!!n?.meta&&n.meta!==null&&typeof n.meta==`object`,d=!!n?.namespace&&typeof n.namespace==`string`,f=!!n?.timeStamp&&typeof n.timeStamp==`number`;return r&&i&&a&&o&&s&&c&&l&&u&&d&&f};var r=()=>[...[,,,,]].map(()=>Math.floor(Math.random()*(2**53-1)).toString(16)).join(`-`),i=(e,t,...n)=>{let r=setTimeout(()=>{clearTimeout(r),e(...n),r=setInterval(e,t,...n)},0);return()=>clearInterval(r)},a=(e,t)=>!!t?.prototype&&e instanceof t,o=e=>{let t=new WeakSet,n=e=>!e||typeof e!=`object`||t.has(e)?[]:(t.add(e),a(e,globalThis.ArrayBuffer)||a(e,globalThis.MessagePort)||a(e,globalThis.ImageBitmap)||a(e,globalThis.OffscreenCanvas)||a(e,globalThis.AudioData)||a(e,globalThis.VideoFrame)||a(e,globalThis.RTCDataChannel)||a(e,globalThis.MediaSourceHandle)||a(e,globalThis.MIDIAccess)||a(e,globalThis.MediaStreamTrack)||a(e,globalThis.ReadableStream)||a(e,globalThis.WritableStream)||a(e,globalThis.TransformStream)?[e]:Array.isArray(e)?e.flatMap(n):Object.values(e).flatMap(n));return n(e)};const s=Symbol(`PROXY_MARKER`),c=e=>e!==null&&(typeof e==`object`||typeof e==`function`)&&e[s]===!0,l=async(a,s)=>{let c,l,u=new Set,d=new Promise((l,d)=>{c=i(async()=>{try{let i=r(),c=await a.onMessage(r=>{if(!n(r))return;let a=r;a.namespace===s.namespace&&a.sender===t.PROVIDER&&a.type===e.PONG&&a.id===i&&l()});c&&u.add(c);let d={type:e.PING,sender:t.INJECTOR,id:i,path:[],meta:{},namespace:s.namespace,timeStamp:Date.now()};a.sendMessage(d,s.transfer?o(d):[])}catch(e){d(e)}},s.heartbeatInterval)}),f=new Promise((e,t)=>{let n=setTimeout(()=>t(Error(`Provider unavailable: heartbeat check timeout ${s.heartbeatTimeout}ms.`)),s.heartbeatTimeout);l=()=>clearTimeout(n)});await Promise.race([d,f]).finally(()=>{c(),l(),u.forEach(e=>e()),u.clear()})},u=(r,i,a)=>(i.onMessage(async s=>{if(!n(s))return;let c=s;if(c.namespace===a.namespace&&c.sender===t.INJECTOR)switch(c.type){case e.PING:{let n={type:e.PONG,sender:t.PROVIDER,id:c.id,path:c.path,meta:c.meta,namespace:a.namespace,timeStamp:Date.now()};i.sendMessage(n,a.transfer?o(n):[]);break}case e.APPLY:{try{let n=c.args?.map(n=>c.callbackIds?.includes(n)?(...r)=>{let s={type:e.CALLBACK,sender:t.PROVIDER,id:n,path:c.path,meta:c.meta,data:r,namespace:a.namespace,timeStamp:Date.now()};i.sendMessage(s,a.transfer?o(s):[])}:n);c.data=await(c.path?.reduce((e,t)=>e[t],r)).apply(r,n||[])}catch(e){c.error=e.message}let n={type:e.APPLY,sender:t.PROVIDER,id:c.id,path:c.path,data:c.data,error:c.error,meta:c.meta,namespace:a.namespace,timeStamp:Date.now()};i.sendMessage(n,a.transfer?o(n):[]);break}}}),r),d=(i,a,c)=>{let u=(i,d)=>new Proxy(i,{get(e,t,n){return t===s?!0:typeof e==`function`&&(t===`apply`||t===`call`||t===`bind`||t===`length`||t===`name`)?Reflect.get(e,t,n):u((()=>{}),[...d,t])},apply(i,s,u){return new Promise(async(i,s)=>{try{c.heartbeatCheck&&await l(a,c);let f=[],p=u.map(i=>{if(typeof i==`function`){let o=r();return f.push(o),a.onMessage(r=>{if(!n(r))return;let a=r;a.namespace===c.namespace&&a.sender===t.PROVIDER&&a.type===e.CALLBACK&&a.id===o&&i(...a.data)}),o}else return i}),m=r(),h=await a.onMessage(r=>{if(!n(r))return;let a=r;a.namespace===c.namespace&&a.sender===t.PROVIDER&&a.type===e.APPLY&&a.id===m&&(a.error?s(Error(a.error)):i(a.data),h?.())}),g={type:e.APPLY,sender:t.INJECTOR,id:m,path:d,args:p,meta:{},callbackIds:f,timeStamp:Date.now(),namespace:c.namespace};a.sendMessage(g,c.transfer?o(g):[])}catch(e){s(e)}})}});return u(i,[])},f=(e,t)=>{let n;return(r,...i)=>n??=u(e(...i),r,t)},p=(e,t)=>{let n;return r=>n??=d(t.backup?Object.freeze(e()):{},r,t)},m=(e,t)=>{let n={namespace:t?.namespace??`__comctx__`,heartbeatCheck:t?.heartbeatCheck??!0,heartbeatInterval:t?.heartbeatInterval??300,heartbeatTimeout:t?.heartbeatTimeout??1e3,transfer:t?.transfer??!1,backup:t?.backup??!1};if(n.heartbeatTimeout<=n.heartbeatInterval)throw Error(`Invalid heartbeat config: timeout (${n.heartbeatTimeout}ms) must exceed interval (${n.heartbeatInterval}ms).`);return[f(e,n),p(e,n)]};export{t as MESSAGE_SENDER,e as MESSAGE_TYPE,n as checkMessage,m as defineProxy,c as isProxy};
//# sourceMappingURL=index.js.map