porter-source
Version:
Messaging Library for Web Extensions
3 lines (2 loc) • 24.9 kB
JavaScript
import X from"webextension-polyfill";var L=(a=>(a.ContentScript="contentscript",a.Extension="extension",a.Popup="popup",a.Sidepanel="sidepanel",a.Devtools="devtools",a.Options="options",a.Unknown="unknown",a))(L||{}),z=(o=>(o.NewTab="new-tab",o.NewFrame="new-frame",o.Refresh="refresh",o.NewExtensionContext="new-extension-context",o))(z||{}),y=(i=>(i.CONNECTION_FAILED="connection-failed",i.CONNECTION_TIMEOUT="connection-timeout",i.INVALID_TARGET="invalid-target",i.MESSAGE_FAILED="message-failed",i.INVALID_CONTEXT="invalid-context",i.INVALID_PORT="invalid-port",i))(y||{}),g=class extends Error{constructor(t,n,o){super(n);this.type=t;this.details=o;this.name="PorterError"}};function k(){return typeof ServiceWorkerGlobalScope!="undefined"&&self instanceof ServiceWorkerGlobalScope}var $=(s=>(s[s.ERROR=0]="ERROR",s[s.WARN=1]="WARN",s[s.INFO=2]="INFO",s[s.DEBUG=3]="DEBUG",s[s.TRACE=4]="TRACE",s))($||{}),l=class l{constructor(e){this.context=e}static getLevel(){var t,n;return((t=l.globalOptions)==null?void 0:t.level)!==void 0?l.globalOptions.level:typeof process!="undefined"&&((n=process.env)==null?void 0:n.PORTER_ENV)==="production"?1:4}static configure(e){l.globalOptions=e,e.level!==void 0&&(l.level=e.level),e.enabled!==void 0&&(l.enabled=e.enabled)}static getLogger(e){return this.instances.has(e)||this.instances.set(e,new l(e)),this.instances.get(e)}error(e,...t){l.enabled&&l.level>=0&&console.error(`[Porter:${this.context}] ${e}`,...t)}warn(e,...t){l.enabled&&l.level>=1&&console.warn(`[Porter:${this.context}] ${e}`,...t)}info(e,...t){l.enabled&&l.level>=2&&console.info(`[Porter:${this.context}] ${e}`,...t)}debug(e,...t){l.enabled&&l.level>=3&&console.debug(`[Porter:${this.context}] ${e}`,...t)}trace(e,...t){l.enabled&&l.level>=4&&console.trace(`[Porter:${this.context}] ${e}`,...t)}};l.level=l.getLevel(),l.enabled=!1,l.instances=new Map;var p=l;import V from"webextension-polyfill";import{v4 as G}from"uuid";var P=class{constructor(e){this.logger=e;this.agents=new Map;this.agentsInfo=new Map;this.eventHandlers=new Map;this.eventHandlers.set("agentSetup",new Set),this.eventHandlers.set("agentMessage",new Set),this.eventHandlers.set("agentDisconnect",new Set)}addAgent(e,t){var m,I;this.logger.debug("Adding agent",{context:t,port:e});let n=this.identifyConnectionSource(e);if(!n){this.logger.error("Cannot add agent that did not have a sender");return}let o=n.context,s=n.tabId||-1,i=n.frameId||0;this.logger.debug("Determined context for new agent",{determinedContext:o,tabId:s,frameId:i});let a=Array.from(this.agentsInfo.values()).filter(d=>d.location.context===o&&d.location.tabId===s&&d.location.frameId===i);a.length>0&&this.logger.debug("Adding agent: Found existing similar agent.",{tabAgentsInfo:a});let u=((I=(m=this.getAgentByLocation({context:o,tabId:s,frameId:i}))==null?void 0:m.info)==null?void 0:I.id)||G();this.logger.debug(`Adding agent with id: ${u}`),this.agents.set(u,e);let c={id:u,location:{context:o,tabId:s,frameId:i},createdAt:Date.now(),lastActiveAt:Date.now()};this.agentsInfo.set(u,c),this.logger.debug(`Constructed agent info: ${JSON.stringify(c)}`),e.onMessage.addListener(d=>this.emit("agentMessage",d,c));let h={port:e,info:c};return e.onDisconnect.addListener(()=>{this.emit("agentDisconnect",c),this.logger.debug("Agent disconnected, removing from manager. ",{agentInfo:c}),this.removeAgent(u)}),this.emit("agentSetup",h),this.logger.debug("Setup complete for adding agent. ",{agentInfo:c}),u}getAgentByLocation(e){let{context:t,tabId:n,frameId:o}=e,s=Array.from(this.agentsInfo.entries()).find(([c,h])=>h.location.context===t&&h.location.tabId===n&&h.location.frameId===o);if(s===void 0)return this.logger.error("No agent found for location. ",{location:e}),null;let i=s[0],a=this.agents.get(i),u=this.agentsInfo.get(i);return!a||!u?(this.logger.error("No agent found for location. ",{location:e}),null):{port:a,info:u}}getAgentsByContext(e){return Array.from(this.agentsInfo.entries()).filter(([n,o])=>o.location.context===e).map(([n,o])=>({port:this.agents.get(n),info:o}))}getAllAgents(){return Array.from(this.agentsInfo.entries()).map(([t,n])=>({port:this.agents.get(t),info:n}))}queryAgents(e){return Array.from(this.agentsInfo.entries()).filter(([n,o])=>{let s=e.context?o.location.context===e.context:!0,i=e.tabId?o.location.tabId===e.tabId:!0,a=e.frameId?o.location.frameId===e.frameId:!0;return s&&i&&a}).map(([n,o])=>({port:this.agents.get(n),info:o}))}getAgentById(e){let t=this.agents.get(e),n=this.agentsInfo.get(e);return!t||!n?(this.logger.error("No agent found for agentId. ",{id:e}),null):{port:t,info:n}}getAllAgentsInfo(){return Array.from(this.agentsInfo.values())}hasPort(e){return!!Array.from(this.agents.values()).find(n=>n.name===e.name)}removeAgent(e){this.agents.has(e)&&this.agentsInfo.has(e)?(this.agents.delete(e),this.agentsInfo.delete(e)):this.logger.error("No agent found to remove. ",{agentId:e})}printAgents(){let e=Array.from(this.agents.entries()),t=Array.from(this.agentsInfo.entries());this.logger.debug("Current agents:",{allAgents:e,allAgentsInfo:t})}on(e,t){let n=this.eventHandlers.get(e);n&&n.add(t)}emit(e,...t){let n=this.eventHandlers.get(e);n==null||n.forEach(o=>o(...t))}identifyConnectionSource(e){var I,d,b,A,v,C,w;let t=e.sender;if(!t)return this.logger.error("Cannot add agent that did not have a sender"),null;let n=V.runtime.getManifest(),o=((I=n==null?void 0:n.side_panel)==null?void 0:I.default_path)||"",s=n.options_page||"",i=((d=n.action)==null?void 0:d.default_popup)||"",a=n.devtools_page||"",u=((b=n.chrome_url_overrides)==null?void 0:b.newtab)||"",c=((A=n.chrome_url_overrides)==null?void 0:A.bookmarks)||"",h=((v=n.chrome_url_overrides)==null?void 0:v.history)||"",m={sidepanel:o?o.split("/").pop():"sidepanel.html",options:s?s.split("/").pop():"options.html",popup:i?i.split("/").pop():"popup.html",devtools:a?a.split("/").pop():"devtools.html",newtab:u?u.split("/").pop():"newtab.html",bookmarks:c?c.split("/").pop():"bookmarks.html",history:h?h.split("/").pop():"history.html"};if(t.tab&&t.url&&!t.url.includes("extension://"))return{context:"contentscript",tabId:t.tab.id,frameId:t.frameId||0,url:t.url,portName:e.name};if(t.url&&t.url.includes("extension://")){let F=new URL(t.url).pathname.split("/").pop();for(let[H,U]of Object.entries(m))if(F===U)return{context:H,tabId:((C=t.tab)==null?void 0:C.id)||0,frameId:t.frameId||0,url:t.url,portName:e.name};return{context:"unknown",tabId:((w=t.tab)==null?void 0:w.id)||0,frameId:t.frameId||0,url:t.url,portName:e.name}}return{context:"unknown",tabId:0,url:t.url,portName:e.name}}};var E=class{constructor(e,t,n){this.agentOperations=e;this.namespace=t;this.logger=n}handleConnection(e){try{if(this.logger.info("New connection request:",e.name),!e.name)throw new g("invalid-context","Port name not provided");if(!e.name||!e.name.startsWith(this.namespace+":"))throw new g("invalid-context",`Invalid namespace or port name format. port name was ${(e==null?void 0:e.name)||"port undefined"} but namespace is ${this.namespace}`);e.onMessage.addListener(this.handleInitMessage.bind(this,e)),setTimeout(()=>{if(!this.agentOperations.hasPort(e))try{e.disconnect()}catch(t){this.logger.error("Failed to disconnect port:",t)}},5e3)}catch(t){this.handleConnectionError(e,t)}}handleInitMessage(e,t){if(t.action==="porter-init")try{e.onMessage.removeListener(this.handleInitMessage.bind(this,e));let{connectionId:n}=t.payload;if(!n)throw new g("invalid-context","Missing context or connection ID. Message was: "+JSON.stringify(t));let o=this.agentOperations.addAgent(e);if(!o)throw new g("invalid-context","Failed to add agent");let s=this.agentOperations.getAgentById(o);s&&this.confirmConnection(s),this.agentOperations.printAgents()}catch(n){this.handleConnectionError(e,n)}}handleConnectionError(e,t){let n=t instanceof g?t:new g("connection-failed",t instanceof Error?t.message:"Unknown connection error",{originalError:t});this.logger.error("Connection handling failed: ",{porterError:n});try{e.postMessage({action:"porter-error",payload:{error:n}})}catch(o){this.logger.error("Failed to send error message: ",{error:o})}try{e.disconnect()}catch(o){this.logger.error("Failed to disconnect port: ",{error:o})}}confirmConnection(e){if(this.logger.debug("Sending confirmation message back to initiator ",{agent:e}),!e.port)throw new g("invalid-port","Agent port is undefined when confirming connection");e.port.postMessage({action:"porter-handshake",payload:{info:e.info,currentConnections:this.agentOperations.getAllAgentsInfo()}})}};var x=class{constructor(e,t){this.agentOperations=e;this.logger=t;this.eventListeners=new Map;this.messageListeners=new Set;this.initializationHandler={"porter-messages-established":(n,o)=>{var i;if(!o||!o.id)return;let s=(i=this.agentOperations.getAgentById(o.id))==null?void 0:i.info;if(!s){this.logger.error("No agent info found for agent id: ",o.id);return}this.logger.debug("internalHandlers, established message received: ",o.id,n),this.emitEvent("onMessagesSet",s)}}}async post(e,t){return new Promise((n,o)=>{try{this.logger.debug("Post request received:",{action:e.action,target:t});let s=setTimeout(()=>{let i=new Error("Message posting timed out");this.logger.error("Post timeout:",i),o(i)},5e3);t===void 0?this.broadcastMessage(e):W(t)?this.postToLocation(e,t):_(t)?this.postToContext(e,t):typeof t=="string"?this.postToId(e,t):this.postToTab(e,t),clearTimeout(s),n()}catch(s){let i=s instanceof Error?s.message:"Unknown error";this.logger.error("Failed to post message:",i),o(new Error(`Failed to post message: ${i}`))}})}broadcastMessage(e){this.logger.info("Broadcasting message to all agents: ",e),this.agentOperations.getAllAgents().forEach(t=>{t.port&&t.port.postMessage(e)})}postToTab(e,t){let n=this.agentOperations.queryAgents({context:"contentscript",tabId:t});if(n.length===0){throw this.logger.warn("post: No agents found for tab: ",t),new g("message-failed",`Failed to post message to tabId ${t}`,{originalError:e});return}n.forEach(o=>{o.port&&this.postToPort(e,o.port)})}postToLocation(e,t){this.agentOperations.queryAgents(t).forEach(o=>{if(!o.port)throw new g("invalid-target","No port found for agent",{agentInfo:o.info});this.postToPort(e,o.port)})}postToContext(e,t){this.agentOperations.queryAgents({context:t}).forEach(o=>{if(!o.port)throw new g("invalid-target","No port found for agent",{agentInfo:o.info});this.postToPort(e,o.port)})}postToPort(e,t){try{t.postMessage(e)}catch(n){throw new g("message-failed","Failed to post message to port",{originalError:n,message:e})}}postToId(e,t){let n=this.agentOperations.getAgentById(t);if(!(n!=null&&n.port))throw new g("invalid-target",`No agent found for key: ${t}`);this.postToPort(e,n.port)}onMessage(e){Array.from(this.messageListeners).find(o=>JSON.stringify(o.config)===JSON.stringify(e))&&this.logger.warn(`Listener with same config already exists: ${JSON.stringify(e)}`);let n={config:e,listener:o=>{let s=e[o.message.action];if(s){this.logger.debug("onMessage, calling handler ",{event:o});let{message:i,...a}=o;s(i,a)}else this.logger.debug("onMessage, no handler found ",{event:o})}};return this.messageListeners.add(n),()=>{this.messageListeners.delete(n)}}on(e){return this.onMessage(e)}handleIncomingMessage(e,t){this.logger.debug("Received message",{message:e,info:t}),this.emitMessage({...t,message:e})}emitEvent(e,t){var n;this.logger.debug("emitting event: ",e,t),(n=this.eventListeners.get(e))==null||n.forEach(o=>o(t))}emitMessage(e){if(this.logger.debug("Dispatching incoming message to subscribers",{messageEvent:e}),e.message.action.startsWith("porter-")){let n=this.initializationHandler[e.message.action];if(n){this.logger.debug("Internal message being handled",{messageEvent:e});let{message:o,...s}=e;n(o,s);return}}e.message.target&&(this.logger.debug("Relaying message to target:",e.message.target),this.post(e.message,e.message.target));let t=0;this.logger.trace("Processing message with registered handlers");for(let{listener:n,config:o}of this.messageListeners)o[e.message.action]&&(n(e),t++,this.logger.debug("Message handled by registered listener: ",{listener:n,config:o}));t===0?this.logger.warn("No handler found for message:",e.message.action):this.logger.debug(`Message handled by ${t} registered listeners`)}addListener(e,t){return this.eventListeners.has(e)||this.eventListeners.set(e,new Set),this.eventListeners.get(e).add(t),()=>{var n;(n=this.eventListeners.get(e))==null||n.delete(t)}}handleDisconnect(e){this.messageListeners.forEach(t=>{t.config[e.id]&&this.messageListeners.delete(t)}),this.logger.info("Agent disconnected:",{info:e}),this.emitEvent("onDisconnect",e)}handleConnect(e){this.logger.info("Agent connected:",{info:e}),this.emitEvent("onConnect",e)}onConnect(e){return this.addListener("onConnect",e)}onMessagesSet(e){return this.addListener("onMessagesSet",e)}onDisconnect(e){return this.addListener("onDisconnect",e)}};function W(r){return typeof r=="object"&&r!==null&&"context"in r&&"tabId"in r&&"frameId"in r}function _(r){return typeof r=="string"&&Object.values(L).includes(r)}var f=class f{constructor(e,t){if((t==null?void 0:t.debug)!==void 0&&p.configure({enabled:t.debug}),this.logger=p.getLogger("SW"),this.namespace=e||"porter",e||this.logger.error('No namespace provided, defaulting to "porter"'),this.agentManager=new P(this.logger),this.messageHandler=new x(this.agentManager,this.logger),this.connectionManager=new E(this.agentManager,this.namespace,this.logger),this.logger.info(`Constructing Porter with namespace: ${this.namespace}`),!k())throw new g("invalid-context","Can only create in a service worker");this.agentManager.on("agentMessage",(n,o)=>{this.messageHandler.handleIncomingMessage(n,o)}),this.agentManager.on("agentDisconnect",n=>{this.messageHandler.handleDisconnect(n)}),this.agentManager.on("agentSetup",n=>{this.logger.debug("Handling agent setup",{agent:n}),this.messageHandler.handleConnect(n.info),this.connectionManager.confirmConnection(n)}),X.runtime.onConnect.addListener(this.connectionManager.handleConnection.bind(this.connectionManager))}static getInstance(e="porter",t){return f.staticLogger.debug(`Getting instance for namespace: ${e}`),f.instances.has(e)?(t==null?void 0:t.debug)!==void 0&&p.configure({enabled:t.debug}):(f.staticLogger.info(`Creating new instance for namespace: ${e}`),f.instances.set(e,new f(e,t))),f.instances.get(e)}post(e,t){return this.messageHandler.post(e,t)}onMessage(e){return this.messageHandler.onMessage(e)}on(e){return this.messageHandler.on(e)}onConnect(e){return this.messageHandler.onConnect(e)}onDisconnect(e){return this.messageHandler.onDisconnect(e)}onMessagesSet(e){return this.messageHandler.onMessagesSet(e)}getInfo(e){var t;return((t=this.agentManager.getAgentById(e))==null?void 0:t.info)||null}getAgentById(e){return this.agentManager.getAgentById(e)}getAgentByLocation(e){return this.agentManager.getAgentByLocation(e)}queryAgents(e){return this.agentManager.queryAgents(e)}};f.instances=new Map,f.staticLogger=p.getLogger("SW");var N=f;function J(r="porter",e){let t=N.getInstance(r,e);return{type:"source",post:t.post.bind(t),onMessage:t.onMessage.bind(t),on:t.on.bind(t),onConnect:t.onConnect.bind(t),onDisconnect:t.onDisconnect.bind(t),onMessagesSet:t.onMessagesSet.bind(t),getAgentById:t.getAgentById.bind(t),getAgentByLocation:t.getAgentByLocation.bind(t),queryAgents:t.queryAgents.bind(t)}}import K from"webextension-polyfill";var T=class{constructor(e){this.queue=[];this.maxQueueSize=1e3;this.maxMessageAge=5*60*1e3;this.logger=e,this.logger.debug("MessageQueue initialized",{maxQueueSize:this.maxQueueSize,maxMessageAge:`${this.maxMessageAge/1e3} seconds`})}enqueue(e,t){let n=this.queue.length;this.cleanup(),n!==this.queue.length&&this.logger.debug(`Cleaned up ${n-this.queue.length} old messages`),this.queue.length>=this.maxQueueSize&&(this.logger.warn("Message queue is full, dropping oldest message",{queueSize:this.queue.length,maxSize:this.maxQueueSize}),this.queue.shift()),this.queue.push({message:e,target:t,timestamp:Date.now()}),this.logger.debug("Message queued",{queueSize:this.queue.length,message:e,target:t,timestamp:new Date().toISOString()})}dequeue(){let e=[...this.queue];return this.queue=[],this.logger.info(`Dequeued ${e.length} messages`,{oldestMessage:e[0]?new Date(e[0].timestamp).toISOString():null,newestMessage:e[e.length-1]?new Date(e[e.length-1].timestamp).toISOString():null}),e}isEmpty(){return this.queue.length===0}cleanup(){let e=Date.now(),t=this.queue.length;this.queue=this.queue.filter(n=>e-n.timestamp<this.maxMessageAge),t!==this.queue.length&&this.logger.debug(`Cleaned up ${t-this.queue.length} expired messages`,{remaining:this.queue.length,maxAge:`${this.maxMessageAge/1e3} seconds`})}};var S=class{constructor(e,t){this.namespace=e;this.CONNECTION_TIMEOUT=5e3;this.RECONNECT_INTERVAL=1e3;this.connectionTimer=null;this.reconnectTimer=null;this.agentInfo=null;this.port=null;this.isReconnecting=!1;this.reconnectAttemptCount=0;this.connectionId=`${Date.now()}-${Math.random().toString(36).substring(2,9)}`,this.logger=t,this.messageQueue=new T(t)}async initializeConnection(){var e;try{this.connectionTimer&&clearTimeout(this.connectionTimer);let t=`${this.namespace}:${this.connectionId}`;this.logger.debug("Connecting new port with name: ",{portName:t}),this.port=K.runtime.connect({name:t});let n=new Promise((o,s)=>{var u;let i=setTimeout(()=>s(new g("connection-timeout","Connection timed out waiting for handshake")),this.CONNECTION_TIMEOUT),a=c=>{var h,m;c.action==="porter-handshake"?(this.logger.debug("Received handshake:",c),clearTimeout(i),this.agentInfo=c.payload.info,this.logger.debug("Handshake agent info:",{agentInfo:this.agentInfo}),(h=this.port)==null||h.onMessage.removeListener(a),o()):c.action==="porter-error"&&(clearTimeout(i),(m=this.port)==null||m.onMessage.removeListener(a),this.logger.error("Error:",c),s(new g(c.payload.type,c.payload.message,c.payload.details)))};(u=this.port)==null||u.onMessage.addListener(a)});(e=this.port)==null||e.postMessage({action:"porter-init",payload:{info:this.agentInfo,connectionId:this.connectionId}}),await n,await this.processQueuedMessages()}catch(t){throw this.logger.error("Connection initialization failed:",t),this.handleDisconnect(this.port),t}}async processQueuedMessages(){if(!this.port||this.messageQueue.isEmpty())return;let e=this.messageQueue.dequeue();this.logger.info(`Processing ${e.length} queued messages after reconnection`);for(let{message:t,target:n}of e)try{this.port.postMessage({action:"porter-message",payload:{message:t,target:n}}),this.logger.debug("Successfully resent queued message:",{message:t,target:n})}catch(o){this.logger.error("Failed to process queued message:",o),this.messageQueue.enqueue(t,n),this.logger.debug("Re-queued failed message for retry")}}getPort(){return this.port}getAgentInfo(){return this.agentInfo}getNamespace(){return this.namespace}handleDisconnect(e){this.logger.info("Port disconnected",{portName:e.name,connectionId:this.connectionId,queuedMessages:this.messageQueue.isEmpty()?0:"some"}),this.port=null,this.agentInfo=null,this.isReconnecting||this.startReconnectionAttempts()}startReconnectionAttempts(){this.isReconnecting=!0,this.reconnectAttemptCount=0,this.reconnectTimer&&clearInterval(this.reconnectTimer),this.logger.info("Starting reconnection attempts",{interval:this.RECONNECT_INTERVAL,queuedMessages:this.messageQueue.isEmpty()?0:"some"}),this.reconnectTimer=setInterval(async()=>{this.reconnectAttemptCount++;try{this.logger.debug(`Reconnection attempt ${this.reconnectAttemptCount}`),await this.initializeConnection(),this.isReconnecting=!1,this.reconnectTimer&&clearInterval(this.reconnectTimer),this.logger.info("Reconnection successful",{attempts:this.reconnectAttemptCount,queuedMessages:this.messageQueue.isEmpty()?0:"some"})}catch(e){this.logger.debug(`Reconnection attempt ${this.reconnectAttemptCount} failed:`,e)}},this.RECONNECT_INTERVAL)}queueMessage(e,t){this.messageQueue.enqueue(e,t),this.logger.debug("Message queued for retry",{message:e,target:t,queueSize:this.messageQueue.isEmpty()?0:"some"})}};var O=class{constructor(e){this.logger=e;this.MAX_QUEUE_SIZE=1e3;this.MESSAGE_TIMEOUT=3e4;this.messageQueue=[];this.handlers=new Map}handleMessage(e,t){if(this.logger.debug("handleMessage, message: ",t),this.handlers.size===0){if(this.messageQueue.length>=this.MAX_QUEUE_SIZE){this.logger.warn("Message queue full, dropping message:",t);return}this.logger.warn("No message handlers configured yet, queueing message: ",t),this.messageQueue.push({message:t,timestamp:Date.now()});return}this.processMessage(e,t)}onMessage(e){this.logger.debug("Setting message handlers from config: ",e),this.handlers.clear(),this.on(e),this.processQueuedMessages()}on(e){this.logger.debug("Adding message handlers from config: ",e),Object.entries(e).forEach(([t,n])=>{this.handlers.has(t)||this.handlers.set(t,[]),this.handlers.get(t).push(n)}),this.processQueuedMessages()}processQueuedMessages(){for(;this.messageQueue.length>0;){let e=this.messageQueue[0];if(Date.now()-e.timestamp>this.MESSAGE_TIMEOUT){this.logger.warn("Message timeout, dropping message: ",this.messageQueue.shift());continue}this.processMessage(null,e.message),this.messageQueue.shift()}}processMessage(e,t){let n=t.action,o=this.handlers.get(n)||[];o.length>0?(this.logger.debug(`Found ${o.length} handlers for action: ${n}`),o.forEach(s=>s(t))):this.logger.debug(`No handlers for message with action: ${n}`)}post(e,t,n){this.logger.debug("Sending message",{action:t.action,target:n,hasPayload:!!t.payload});try{n&&(t.target=n),e.postMessage(t)}catch(o){throw new g("message-failed","Failed to post message",{originalError:o,message:t,target:n})}}};var M=class M{constructor(e={}){var o,s;let t=(o=e.namespace)!=null?o:"porter",n=(s=e.agentContext)!=null?s:this.determineContext();e.debug!==void 0&&p.configure({enabled:e.debug}),this.logger=p.getLogger("Agent"),this.connectionManager=new S(t,this.logger),this.messageHandler=new O(this.logger),this.logger.info("Initializing with options: ",{options:e,context:n}),this.initializeConnection()}static getInstance(e={}){return!M.instance||M.instance.connectionManager.getNamespace()!==e.namespace?M.instance=new M(e):e.debug!==void 0&&p.configure({enabled:e.debug}),M.instance}async initializeConnection(){await this.connectionManager.initializeConnection();let e=this.connectionManager.getPort();e&&(e.onMessage.addListener(t=>this.messageHandler.handleMessage(e,t)),e.onDisconnect.addListener(t=>this.connectionManager.handleDisconnect(t)))}onMessage(e){this.messageHandler.onMessage(e);let t=this.connectionManager.getPort();t==null||t.postMessage({action:"porter-messages-established"})}on(e){this.messageHandler.on(e);let t=this.connectionManager.getPort();t==null||t.postMessage({action:"porter-messages-established"})}post(e,t){let n=this.connectionManager.getPort();if(this.logger.debug("Posting message",{message:e,target:t,port:n}),n)try{this.messageHandler.post(n,e,t)}catch(o){this.logger.error("Failed to post message, queueing for retry",{error:o}),this.connectionManager.queueMessage(e,t)}else this.logger.debug("No port found, queueing message",{message:e,target:t}),this.connectionManager.queueMessage(e,t)}determineContext(){return window.location.protocol.includes("extension")?"extension":"contentscript"}getAgentInfo(){return this.connectionManager.getAgentInfo()||null}};M.instance=null;var R=M;function D(r){let e=R.getInstance(r);return{type:"agent",post:e.post.bind(e),onMessage:e.onMessage.bind(e),on:e.on.bind(e),getAgentInfo:e.getAgentInfo.bind(e)}}import{useState as q,useEffect as j,useCallback as Q,useRef as B,useMemo as Z}from"react";function Y(r){let[e,t]=q(!1),[n,o]=q(null),[s,i]=q(null),a=B(null),u=B(null),c=B(null),h=Z(()=>({agentContext:r==null?void 0:r.agentContext,namespace:r==null?void 0:r.namespace,debug:r==null?void 0:r.debug}),[r==null?void 0:r.agentContext,r==null?void 0:r.namespace,r==null?void 0:r.debug]);j(()=>{let d=!0;return(async()=>{try{let{post:A,on:v,getAgentInfo:C}=D(h);d&&(a.current=A,u.current=v,c.current=C,t(!0),o(null),v({"porter-handshake":w=>{console.log("[PORTER] porter-handshake heard: ",w.payload),d&&i(w.payload.info)}}))}catch(A){d&&(console.log("[PORTER] initializePorter error ",A),o(A instanceof Error?A:new Error("Failed to connect to Porter")),t(!1))}})(),()=>{d=!1}},[h]);let m=Q(d=>{if(a.current)try{a.current(d)}catch(b){o(b instanceof Error?b:new Error("Failed to send message"))}else o(new Error("Porter is not connected"))},[]),I=Q(d=>{if(u.current)try{u.current(d)}catch(b){o(b instanceof Error?b:new Error("Failed to set message handlers"))}else o(new Error("Porter is not connected"))},[]);return{post:m,on:I,isConnected:e,error:n,agentInfo:s}}export{z as ConnectionType,$ as LogLevel,p as Logger,L as PorterContext,g as PorterError,y as PorterErrorType,D as connect,J as source,Y as usePorter};
//# sourceMappingURL=index.js.map