crann 1.0.49 → 2.0.2
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/README.md +353 -594
- package/dist/cjs/index.js +2 -2
- package/dist/cjs/index.js.map +4 -4
- package/dist/cjs/react.js +2 -0
- package/dist/cjs/react.js.map +7 -0
- package/dist/esm/index.js +2 -2
- package/dist/esm/index.js.map +4 -4
- package/dist/esm/react.js +2 -0
- package/dist/esm/react.js.map +7 -0
- package/dist/types/__mocks__/uuid.d.ts +10 -0
- package/dist/types/__tests__/integration.test.d.ts +7 -0
- package/dist/types/agent/Agent.d.ts +77 -0
- package/dist/types/agent/__tests__/Agent.test.d.ts +1 -0
- package/dist/types/agent/__tests__/setup.d.ts +4 -0
- package/dist/types/agent/index.d.ts +7 -0
- package/dist/types/agent/types.d.ts +73 -0
- package/dist/types/crann.d.ts +1 -2
- package/dist/types/errors.d.ts +59 -0
- package/dist/types/model/crann.model.d.ts +1 -1
- package/dist/types/react/__tests__/hooks.test.d.ts +6 -0
- package/dist/types/react/hooks.d.ts +44 -0
- package/dist/types/react/index.d.ts +13 -0
- package/dist/types/react/types.d.ts +74 -0
- package/dist/types/rpc/adapter.d.ts +1 -1
- package/dist/types/rpc/types.d.ts +1 -1
- package/dist/types/store/ActionExecutor.d.ts +35 -0
- package/dist/types/store/AgentRegistry.d.ts +49 -0
- package/dist/types/store/Persistence.d.ts +59 -0
- package/dist/types/store/StateManager.d.ts +65 -0
- package/dist/types/store/Store.d.ts +188 -0
- package/dist/types/store/__tests__/ActionExecutor.test.d.ts +1 -0
- package/dist/types/store/__tests__/AgentRegistry.test.d.ts +1 -0
- package/dist/types/store/__tests__/Persistence.test.d.ts +6 -0
- package/dist/types/store/__tests__/StateManager.test.d.ts +1 -0
- package/dist/types/store/__tests__/setup.d.ts +4 -0
- package/dist/types/store/__tests__/types.test.d.ts +1 -0
- package/dist/types/store/index.d.ts +10 -0
- package/dist/types/store/types.d.ts +169 -0
- package/dist/types/transport/core/PorterAgent.d.ts +46 -0
- package/dist/types/transport/core/PorterSource.d.ts +40 -0
- package/dist/types/transport/index.d.ts +6 -0
- package/dist/types/transport/managers/AgentConnectionManager.d.ts +42 -0
- package/dist/types/transport/managers/AgentManager.d.ts +40 -0
- package/dist/types/transport/managers/AgentMessageHandler.d.ts +17 -0
- package/dist/types/transport/managers/ConnectionManager.d.ts +14 -0
- package/dist/types/transport/managers/MessageHandler.d.ts +29 -0
- package/dist/types/transport/managers/MessageQueue.d.ts +19 -0
- package/dist/types/transport/porter.model.d.ts +71 -0
- package/dist/types/transport/porter.utils.d.ts +44 -0
- package/dist/types/transport/react/index.d.ts +1 -0
- package/dist/types/transport/react/usePorter.d.ts +17 -0
- package/dist/types/utils/agent.d.ts +1 -1
- package/package.json +28 -2
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var ne=Object.create;var P=Object.defineProperty;var oe=Object.getOwnPropertyDescriptor;var re=Object.getOwnPropertyNames;var se=Object.getPrototypeOf,ie=Object.prototype.hasOwnProperty;var ae=(r,e)=>{for(var t in e)P(r,t,{get:e[t],enumerable:!0})},G=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of re(e))!ie.call(r,o)&&o!==t&&P(r,o,{get:()=>e[o],enumerable:!(n=oe(e,o))||n.enumerable});return r};var H=(r,e,t)=>(t=r!=null?ne(se(r)):{},G(e||!r||!r.__esModule?P(t,"default",{value:r,enumerable:!0}):t,r)),ce=r=>G(P({},"__esModule",{value:!0}),r);var de={};ae(de,{createCrannHooks:()=>ee});module.exports=ce(de);var p=require("react");var J=H(require("webextension-polyfill"));var w=(a=>(a.ContentScript="contentscript",a.Extension="extension",a.Popup="popup",a.Sidepanel="sidepanel",a.Devtools="devtools",a.Options="options",a.Unknown="unknown",a))(w||{});var u=class extends Error{constructor(t,n,o){super(n);this.type=t;this.details=o;this.name="PorterError"}};function j(){return typeof ServiceWorkerGlobalScope<"u"&&self instanceof ServiceWorkerGlobalScope}var d=class d{constructor(e){this.context=e}static getLevel(){var t,n,o;return((t=d.globalOptions)==null?void 0:t.level)!==void 0?d.globalOptions.level:typeof process<"u"&&(((n=process.env)==null?void 0:n.NODE_ENV)==="production"||((o=process.env)==null?void 0:o.PORTER_ENV)==="production")?1:4}static configure(e){d.globalOptions=e,e.level!==void 0&&(d.level=e.level),e.enabled!==void 0&&(d.enabled=e.enabled)}static getLogger(e){return this.instances.has(e)||this.instances.set(e,new d(e)),this.instances.get(e)}error(e,...t){d.enabled&&d.level>=0&&console.error(`[Porter:${this.context}] ${e}`,...t)}warn(e,...t){d.enabled&&d.level>=1&&console.warn(`[Porter:${this.context}] ${e}`,...t)}info(e,...t){d.enabled&&d.level>=2&&console.info(`[Porter:${this.context}] ${e}`,...t)}debug(e,...t){d.enabled&&d.level>=3&&console.debug(`[Porter:${this.context}] ${e}`,...t)}trace(e,...t){d.enabled&&d.level>=4&&console.trace(`[Porter:${this.context}] ${e}`,...t)}};d.level=d.getLevel(),d.enabled=!1,d.instances=new Map;var A=d;var K=H(require("webextension-polyfill")),W=require("uuid");var E=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 g,h;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(m=>m.location.context===o&&m.location.tabId===s&&m.location.frameId===i);a.length>0&&this.logger.debug("Adding agent: Found existing similar agent.",{tabAgentsInfo:a});let l=((h=(g=this.getAgentByLocation({context:o,tabId:s,frameId:i}))==null?void 0:g.info)==null?void 0:h.id)||(0,W.v4)();this.logger.debug(`Adding agent with id: ${l}`),this.agents.set(l,e);let f={id:l,location:{context:o,tabId:s,frameId:i},createdAt:Date.now(),lastActiveAt:Date.now()};this.agentsInfo.set(l,f),this.logger.debug(`Constructed agent info: ${JSON.stringify(f)}`),e.onMessage.addListener(m=>this.emit("agentMessage",m,f));let c={port:e,info:f};return e.onDisconnect.addListener(()=>{this.emit("agentDisconnect",f),this.logger.debug("Agent disconnected, removing from manager. ",{agentInfo:f}),this.removeAgent(l)}),this.emit("agentSetup",c),this.logger.debug("Setup complete for adding agent. ",{agentInfo:f}),l}getAgentByLocation(e){let{context:t,tabId:n,frameId:o}=e,s=Array.from(this.agentsInfo.entries()).find(([f,c])=>c.location.context===t&&c.location.tabId===n&&c.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),l=this.agentsInfo.get(i);return!a||!l?(this.logger.error("No agent found for location. ",{location:e}),null):{port:a,info:l}}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 h,m,y,I,b,C,M;let t=e.sender;if(!t)return this.logger.error("Cannot add agent that did not have a sender"),null;let n=K.default.runtime.getManifest(),o=((h=n==null?void 0:n.side_panel)==null?void 0:h.default_path)||"",s=n.options_page||"",i=((m=n.action)==null?void 0:m.default_popup)||"",a=n.devtools_page||"",l=((y=n.chrome_url_overrides)==null?void 0:y.newtab)||"",f=((I=n.chrome_url_overrides)==null?void 0:I.bookmarks)||"",c=((b=n.chrome_url_overrides)==null?void 0:b.history)||"",g={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:l?l.split("/").pop():"newtab.html",bookmarks:f?f.split("/").pop():"bookmarks.html",history:c?c.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 z=new URL(t.url).pathname.split("/").pop();for(let[_,T]of Object.entries(g))if(z===T)return{context:_,tabId:((C=t.tab)==null?void 0:C.id)||0,frameId:t.frameId||0,url:t.url,portName:e.name};return{context:"unknown",tabId:((M=t.tab)==null?void 0:M.id)||0,frameId:t.frameId||0,url:t.url,portName:e.name}}return{context:"unknown",tabId:0,url:t.url,portName:e.name}}};var L=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 u("invalid-context","Port name not provided");if(!e.name||!e.name.startsWith(this.namespace+":"))throw new u("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 u("invalid-context","Missing context or connection ID. Message was: "+JSON.stringify(t));let o=this.agentOperations.addAgent(e);if(!o)throw new u("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 u?t:new u("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 u("invalid-port","Agent port is undefined when confirming connection");e.port.postMessage({action:"porter-handshake",payload:{info:e.info,currentConnections:this.agentOperations.getAllAgentsInfo()}})}};var R=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):ge(t)?this.postToLocation(e,t):le(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 u("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 u("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 u("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 u("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 u("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 ge(r){return typeof r=="object"&&r!==null&&"context"in r&&"tabId"in r&&"frameId"in r}function le(r){return typeof r=="string"&&Object.values(w).includes(r)}var v=class v{constructor(e,t){if((t==null?void 0:t.debug)!==void 0&&A.configure({enabled:t.debug}),this.logger=A.getLogger("SW"),this.namespace=e||"porter",e||this.logger.error('No namespace provided, defaulting to "porter"'),this.agentManager=new E(this.logger),this.messageHandler=new R(this.agentManager,this.logger),this.connectionManager=new L(this.agentManager,this.namespace,this.logger),this.logger.info(`Constructing Porter with namespace: ${this.namespace}`),!j())throw new u("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)}),J.default.runtime.onConnect.addListener(this.connectionManager.handleConnection.bind(this.connectionManager))}static getInstance(e="porter",t){return v.staticLogger.debug(`Getting instance for namespace: ${e}`),v.instances.has(e)?(t==null?void 0:t.debug)!==void 0&&A.configure({enabled:t.debug}):(v.staticLogger.info(`Creating new instance for namespace: ${e}`),v.instances.set(e,new v(e,t))),v.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)}};v.instances=new Map,v.staticLogger=A.getLogger("SW");var X=v;var Z=H(require("webextension-polyfill"));var k=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 N=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.disconnectCallbacks=new Set;this.reconnectCallbacks=new Set;this.connectionId=`${Date.now()}-${Math.random().toString(36).substring(2,9)}`,this.logger=t,this.messageQueue=new k(t)}onDisconnect(e){return this.disconnectCallbacks.add(e),()=>{this.disconnectCallbacks.delete(e)}}onReconnect(e){return this.reconnectCallbacks.add(e),()=>{this.reconnectCallbacks.delete(e)}}emitDisconnect(){this.logger.debug("Emitting disconnect event",{callbackCount:this.disconnectCallbacks.size}),this.disconnectCallbacks.forEach(e=>{try{e()}catch(t){this.logger.error("Error in disconnect callback:",t)}})}emitReconnect(e){this.logger.debug("Emitting reconnect event",{callbackCount:this.reconnectCallbacks.size,info:e}),this.reconnectCallbacks.forEach(t=>{try{t(e)}catch(n){this.logger.error("Error in reconnect callback:",n)}})}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=Z.default.runtime.connect({name:t});let n=new Promise((o,s)=>{var l;let i=setTimeout(()=>s(new u("connection-timeout","Connection timed out waiting for handshake")),this.CONNECTION_TIMEOUT),a=f=>{var c,g;f.action==="porter-handshake"?(this.logger.debug("Received handshake:",f),clearTimeout(i),this.agentInfo=f.payload.info,this.logger.debug("Handshake agent info:",{agentInfo:this.agentInfo}),(c=this.port)==null||c.onMessage.removeListener(a),o()):f.action==="porter-error"&&(clearTimeout(i),(g=this.port)==null||g.onMessage.removeListener(a),this.logger.error("Error:",f),s(new u(f.payload.type,f.payload.message,f.payload.details)))};(l=this.port)==null||l.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{let o={...t};n&&(o.target=n),this.port.postMessage(o),this.logger.debug("Successfully resent queued message:",{message:o})}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.emitDisconnect(),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.reconnectTimer=null),this.logger.info("Reconnection successful",{attempts:this.reconnectAttemptCount,queuedMessages:this.messageQueue.isEmpty()?0:"some"}),this.agentInfo&&this.emitReconnect(this.agentInfo)}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 u("message-failed","Failed to post message",{originalError:o,message:t,target:n})}}};var S=class S{constructor(e={}){let t=e.namespace??"porter",n=e.agentContext??this.determineContext();e.debug!==void 0&&A.configure({enabled:e.debug}),this.logger=A.getLogger("Agent"),this.connectionManager=new N(t,this.logger),this.messageHandler=new O(this.logger),this.connectionManager.onReconnect(o=>{this.logger.info("Reconnected, re-wiring port listeners",{info:o}),this.setupPortListeners()}),this.logger.info("Initializing with options: ",{options:e,context:n}),this.initializeConnection()}static getInstance(e={}){return!S.instance||S.instance.connectionManager.getNamespace()!==e.namespace?S.instance=new S(e):e.debug!==void 0&&A.configure({enabled:e.debug}),S.instance}async initializeConnection(){await this.connectionManager.initializeConnection(),this.setupPortListeners()}setupPortListeners(){let e=this.connectionManager.getPort();e?(this.logger.debug("Setting up port listeners"),e.onMessage.addListener(t=>this.messageHandler.handleMessage(e,t)),e.onDisconnect.addListener(t=>this.connectionManager.handleDisconnect(t))):this.logger.warn("Cannot setup port listeners: no port available")}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}onDisconnect(e){return this.connectionManager.onDisconnect(e)}onReconnect(e){return this.connectionManager.onReconnect(e)}};S.instance=null;var F=S;function q(r){let e=F.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),onDisconnect:e.onDisconnect.bind(e),onReconnect:e.onReconnect.bind(e)}}var x=require("react");var Q=class extends Error{constructor(e){super(e),this.name="CrannError",Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor)}};var $=class extends Q{constructor(t,n){super(`${n==="store"?"Store":"Agent"} "${t}" has been ${n==="store"?"destroyed":"disconnected"} and cannot be used.`);this.storeName=t;this.entity=n;this.name="LifecycleError"}};var B=class{constructor(e,t={}){this._agentInfo=null;this._isConnected=!1;this._isDisconnected=!1;this.subscribers=new Set;this.readyCallbacks=new Set;this.disconnectCallbacks=new Set;this.reconnectCallbacks=new Set;this.config=e,this.options=t,this._state=this.buildDefaultState(),this.readyPromise=new Promise(n=>{this.readyResolve=n}),this.porter=q({namespace:e.name,debug:t.debug??!1}),this.setupMessageHandlers(),this.actionsProxy=this.createActionsProxy()}ready(){return this.assertNotDisconnected(),this.readyPromise}onReady(e){return this.assertNotDisconnected(),this._isConnected&&setTimeout(()=>e(this._state),0),this.readyCallbacks.add(e),()=>{this.readyCallbacks.delete(e)}}getState(){return this.assertNotDisconnected(),{...this._state}}get state(){return this.getState()}async setState(e){this.assertNotDisconnected(),this._state={...this._state,...e},this.porter.post({action:"setState",payload:{state:e}})}subscribe(e,t){this.assertNotDisconnected();let n,o;typeof e=="function"?o=e:(n=e,o=t);let s={keys:n,callback:o};return this.subscribers.add(s),()=>{this.subscribers.delete(s)}}get actions(){return this.assertNotDisconnected(),this.actionsProxy}getInfo(){return this._agentInfo?{id:this._agentInfo.id,tabId:this._agentInfo.location.tabId,frameId:this._agentInfo.location.frameId,context:this._agentInfo.location.context}:null}onDisconnect(e){return this.assertNotDisconnected(),this.disconnectCallbacks.add(e),()=>{this.disconnectCallbacks.delete(e)}}onReconnect(e){return this.assertNotDisconnected(),this.reconnectCallbacks.add(e),()=>{this.reconnectCallbacks.delete(e)}}disconnect(){this._isDisconnected||(this._isDisconnected=!0,this._isConnected=!1,this.subscribers.clear(),this.readyCallbacks.clear(),this.disconnectCallbacks.clear(),this.reconnectCallbacks.clear())}setupMessageHandlers(){this.porter.on({initialState:e=>{let{state:t,info:n}=e.payload;this._state=t,this._agentInfo=n,this._isConnected=!0,this.readyResolve(this._state),this.readyCallbacks.forEach(o=>{try{o(this._state)}catch(s){console.error("[Crann Agent] Error in onReady callback:",s)}}),this.notifySubscribers(t)},stateUpdate:e=>{let{state:t}=e.payload;this._state={...this._state,...t},this.notifySubscribers(t)},rpcResult:e=>{}}),this.porter.onDisconnect(()=>{this._isConnected=!1,this.disconnectCallbacks.forEach(e=>{try{e()}catch(t){console.error("[Crann Agent] Error in onDisconnect callback:",t)}})}),this.porter.onReconnect(e=>{this._agentInfo=e,this._isConnected=!0,this.reconnectCallbacks.forEach(t=>{try{t(this._state)}catch(n){console.error("[Crann Agent] Error in onReconnect callback:",n)}})})}notifySubscribers(e){this.subscribers.forEach(t=>{if(!(t.keys&&!t.keys.some(o=>o in e)))try{t.callback(e,this._state)}catch(n){console.error("[Crann Agent] Error in subscriber callback:",n)}})}createActionsProxy(){let e=new Map,t=0;return this.porter.on({rpcResult:n=>{let{callId:o,result:s,error:i,success:a}=n.payload,l=e.get(o);l&&(e.delete(o),a?l.resolve(s):l.reject(new Error(i)))}}),new Proxy({},{get:(n,o)=>async(...s)=>{let i=`${t++}`;return new Promise((a,l)=>{e.set(i,{resolve:a,reject:l}),this.porter.post({action:"rpc",payload:{callId:i,actionName:o,args:s}})})}})}buildDefaultState(){let e={};for(let[t,n]of Object.entries(this.config))t==="name"||t==="version"||t==="actions"||typeof n=="object"&&n!==null&&"default"in n&&(e[t]=n.default);return e}assertNotDisconnected(){if(this._isDisconnected)throw new $(this.config.name,"agent")}};function V(r,e){return new B(r,e)}var te=require("react/jsx-runtime");function ee(r,e={}){let t=null,n=(0,p.createContext)(null);function o(){return t||(t=V(r,{debug:e.debug})),t}function s(){let c=(0,p.useContext)(n),[g,h]=(0,p.useState)(c??t);return(0,p.useEffect)(()=>{g||h(o())},[g]),c??g}function i(){let c=s(),[g,h]=(0,p.useState)(!1);return(0,p.useEffect)(()=>{if(!c)return;c.getInfo()&&h(!0);let y=c.onReady(()=>{h(!0)}),I=c.onDisconnect(()=>{h(!1)}),b=c.onReconnect(()=>{h(!0)});return()=>{y(),I(),b()}},[c]),g}function a(c){let g=s(),h=typeof c=="function",m=h?c:C=>C[c],[y,I]=(0,p.useState)(()=>{if(!g){let C=ue(r);return m(C)}return m(g.getState())}),b=(0,p.useRef)(y);if((0,p.useEffect)(()=>{if(!g)return;let C=g.getState(),M=m(C);return Y(M,b.current)||(b.current=M,I(M)),g.subscribe((z,_)=>{let T=m(_);Y(T,b.current)||(b.current=T,I(T))})},[g,m]),!h){let C=c,M=(0,p.useCallback)(async U=>{g&&await g.setState({[C]:U})},[g,C]);return[y,M]}return y}function l(){let c=s(),g=(0,p.useRef)(null);g.current=c;let h=(0,p.useRef)(null);return h.current||(h.current=new Proxy({},{get:(m,y)=>async(...I)=>{let b=g.current;if(!b)throw new Error(`Cannot call action "${y}" before agent is connected`);return b.actions[y](...I)}})),h.current}return{useCrannState:a,useCrannActions:l,useCrannReady:i,useAgent:s,CrannProvider:({agent:c,children:g})=>{let h=c??o();return(0,te.jsx)(n.Provider,{value:h,children:g})}}}function ue(r){let e={};for(let[t,n]of Object.entries(r))t==="name"||t==="version"||t==="actions"||typeof n=="object"&&n!==null&&"default"in n&&(e[t]=n.default);return e}function Y(r,e){if(r===e)return!0;if(typeof r!="object"||typeof e!="object"||r===null||e===null)return!1;let t=Object.keys(r),n=Object.keys(e);if(t.length!==n.length)return!1;for(let o of t)if(r[o]!==e[o])return!1;return!0}0&&(module.exports={createCrannHooks});
|
|
2
|
+
//# sourceMappingURL=react.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/react/index.ts", "../../src/react/hooks.tsx", "../../src/transport/core/PorterSource.ts", "../../src/transport/porter.model.ts", "../../src/transport/porter.utils.ts", "../../src/transport/managers/AgentManager.ts", "../../src/transport/managers/ConnectionManager.ts", "../../src/transport/managers/MessageHandler.ts", "../../src/transport/managers/AgentConnectionManager.ts", "../../src/transport/managers/MessageQueue.ts", "../../src/transport/managers/AgentMessageHandler.ts", "../../src/transport/core/PorterAgent.ts", "../../src/transport/react/usePorter.ts", "../../src/errors.ts", "../../src/agent/Agent.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Crann React Integration\n *\n * Provides React hooks for connecting to and using a Crann store.\n *\n * @example\n * import { createCrannHooks } from 'crann/react';\n * import { config } from './config';\n *\n * export const { useCrannState, useCrannActions, useCrannReady } = createCrannHooks(config);\n */\n\nexport { createCrannHooks } from \"./hooks\";\n\nexport type {\n CrannHooks,\n CreateCrannHooksOptions,\n UseCrannStateSelector,\n UseCrannStateTuple,\n} from \"./types\";\n\n", "/**\n * Crann React Hooks Implementation\n *\n * Provides React hooks for connecting to and using a Crann store.\n */\n\nimport {\n createContext,\n useContext,\n useState,\n useEffect,\n useRef,\n useCallback,\n useMemo,\n type ReactNode,\n type FC,\n} from \"react\";\nimport { connectStore } from \"../agent\";\nimport type { AgentAPI } from \"../agent/types\";\nimport type {\n ConfigSchema,\n ValidatedConfig,\n DerivedState,\n DerivedActions,\n} from \"../store/types\";\nimport type { CrannHooks, CreateCrannHooksOptions, UseCrannStateTuple } from \"./types\";\n\n/**\n * Creates a set of React hooks for a Crann store.\n *\n * This is the main entry point for React integration. Call once at module\n * level with your config, then use the returned hooks in your components.\n *\n * @param config - Validated config from createConfig()\n * @param options - Optional hook options\n * @returns Object containing all Crann React hooks\n *\n * @example\n * // hooks.ts\n * import { createConfig } from 'crann';\n * import { createCrannHooks } from 'crann/react';\n *\n * const config = createConfig({\n * name: 'myFeature',\n * count: { default: 0 },\n * actions: {\n * increment: { handler: async (ctx) => ctx.setState({ count: ctx.state.count + 1 }) },\n * },\n * });\n *\n * export const { useCrannState, useCrannActions, useCrannReady } = createCrannHooks(config);\n *\n * // MyComponent.tsx\n * function Counter() {\n * const count = useCrannState(s => s.count);\n * const { increment } = useCrannActions();\n * const isReady = useCrannReady();\n *\n * if (!isReady) return <div>Loading...</div>;\n *\n * return <button onClick={() => increment()}>Count: {count}</button>;\n * }\n */\nexport function createCrannHooks<TConfig extends ConfigSchema>(\n config: ValidatedConfig<TConfig>,\n options: CreateCrannHooksOptions = {}\n): CrannHooks<TConfig> {\n // Module-level agent instance (created lazily)\n let moduleAgent: AgentAPI<TConfig> | null = null;\n\n // Create React context for optional provider override\n const AgentContext = createContext<AgentAPI<TConfig> | null>(null);\n\n /**\n * Get or create the agent instance.\n */\n function getAgent(): AgentAPI<TConfig> {\n if (!moduleAgent) {\n moduleAgent = connectStore(config, { debug: options.debug });\n }\n return moduleAgent;\n }\n\n /**\n * Hook to get the current agent (from context or module-level).\n */\n function useAgent(): AgentAPI<TConfig> | null {\n const contextAgent = useContext(AgentContext);\n const [agent, setAgent] = useState<AgentAPI<TConfig> | null>(\n contextAgent ?? moduleAgent\n );\n\n useEffect(() => {\n if (!agent) {\n setAgent(getAgent());\n }\n }, [agent]);\n\n return contextAgent ?? agent;\n }\n\n /**\n * Hook to check if connection is ready.\n */\n function useCrannReady(): boolean {\n const agent = useAgent();\n const [isReady, setIsReady] = useState(false);\n\n useEffect(() => {\n if (!agent) return;\n\n // Check if already ready\n const info = agent.getInfo();\n if (info) {\n setIsReady(true);\n }\n\n // Subscribe to ready event\n const unsubReady = agent.onReady(() => {\n setIsReady(true);\n });\n\n // Subscribe to disconnect/reconnect\n const unsubDisconnect = agent.onDisconnect(() => {\n setIsReady(false);\n });\n\n const unsubReconnect = agent.onReconnect(() => {\n setIsReady(true);\n });\n\n return () => {\n unsubReady();\n unsubDisconnect();\n unsubReconnect();\n };\n }, [agent]);\n\n return isReady;\n }\n\n /**\n * Hook to read state with selector or key pattern.\n */\n function useCrannState<TSelected>(\n selector: (state: DerivedState<TConfig>) => TSelected\n ): TSelected;\n function useCrannState<K extends keyof DerivedState<TConfig>>(\n key: K\n ): UseCrannStateTuple<TConfig, K>;\n function useCrannState<TSelected, K extends keyof DerivedState<TConfig>>(\n selectorOrKey: ((state: DerivedState<TConfig>) => TSelected) | K\n ): TSelected | UseCrannStateTuple<TConfig, K> {\n const agent = useAgent();\n const isFunction = typeof selectorOrKey === \"function\";\n\n // For selector pattern\n const selector = isFunction\n ? (selectorOrKey as (state: DerivedState<TConfig>) => TSelected)\n : (state: DerivedState<TConfig>) => state[selectorOrKey as K] as unknown as TSelected;\n\n // Track selected value\n const [selectedValue, setSelectedValue] = useState<TSelected>(() => {\n if (!agent) {\n // Return default from config\n const defaultState = buildDefaultState(config);\n return selector(defaultState);\n }\n return selector(agent.getState());\n });\n\n // Store previous selected value for comparison\n const prevSelectedRef = useRef(selectedValue);\n\n useEffect(() => {\n if (!agent) return;\n\n // Update with current state\n const currentState = agent.getState();\n const newSelected = selector(currentState);\n if (!shallowEqual(newSelected, prevSelectedRef.current)) {\n prevSelectedRef.current = newSelected;\n setSelectedValue(newSelected);\n }\n\n // Subscribe to changes\n const unsubscribe = agent.subscribe((changes, state) => {\n const newSelected = selector(state);\n if (!shallowEqual(newSelected, prevSelectedRef.current)) {\n prevSelectedRef.current = newSelected;\n setSelectedValue(newSelected);\n }\n });\n\n return unsubscribe;\n }, [agent, selector]);\n\n // For key pattern, return tuple\n if (!isFunction) {\n const key = selectorOrKey as K;\n const setValue = useCallback(\n async (value: DerivedState<TConfig>[K]) => {\n if (agent) {\n await agent.setState({ [key]: value } as Partial<DerivedState<TConfig>>);\n }\n },\n [agent, key]\n );\n\n return [selectedValue as DerivedState<TConfig>[K], setValue] as UseCrannStateTuple<TConfig, K>;\n }\n\n return selectedValue;\n }\n\n /**\n * Hook to get typed actions with stable references.\n */\n function useCrannActions(): DerivedActions<TConfig> {\n const agent = useAgent();\n\n // Store agent in ref so proxy can access current value\n const agentRef = useRef<AgentAPI<TConfig> | null>(null);\n agentRef.current = agent;\n\n // Return stable proxy that delegates to agent.actions\n const actionsRef = useRef<DerivedActions<TConfig> | null>(null);\n\n if (!actionsRef.current) {\n actionsRef.current = new Proxy({} as DerivedActions<TConfig>, {\n get: (_target, actionName: string) => {\n return async (...args: unknown[]) => {\n const currentAgent = agentRef.current;\n if (!currentAgent) {\n throw new Error(\n `Cannot call action \"${actionName}\" before agent is connected`\n );\n }\n return (currentAgent.actions as any)[actionName](...args);\n };\n },\n });\n }\n\n return actionsRef.current;\n }\n\n /**\n * Optional provider for dependency injection.\n */\n const CrannProvider: FC<{ agent?: AgentAPI<TConfig>; children: ReactNode }> = ({\n agent,\n children,\n }) => {\n const value = agent ?? getAgent();\n return <AgentContext.Provider value={value}>{children}</AgentContext.Provider>;\n };\n\n return {\n useCrannState,\n useCrannActions,\n useCrannReady,\n useAgent,\n CrannProvider,\n };\n}\n\n// =============================================================================\n// Utilities\n// =============================================================================\n\n/**\n * Build default state from config.\n */\nfunction buildDefaultState<TConfig extends ConfigSchema>(\n config: ValidatedConfig<TConfig>\n): DerivedState<TConfig> {\n const state: Record<string, unknown> = {};\n\n for (const [key, item] of Object.entries(config)) {\n if (key === \"name\" || key === \"version\" || key === \"actions\") continue;\n if (typeof item === \"object\" && item !== null && \"default\" in item) {\n state[key] = item.default;\n }\n }\n\n return state as DerivedState<TConfig>;\n}\n\n/**\n * Shallow equality check.\n */\nfunction shallowEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (typeof a !== \"object\" || typeof b !== \"object\") return false;\n if (a === null || b === null) return false;\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if ((a as any)[key] !== (b as any)[key]) return false;\n }\n\n return true;\n}\n\n", "import browser, { Runtime } from 'webextension-polyfill';\nimport {\n AgentInfo,\n Listener,\n Message,\n MessageConfig,\n PorterError,\n PorterErrorType,\n BrowserLocation,\n Unsubscribe,\n AgentId,\n MessageTarget,\n} from '../porter.model';\nimport { Agent } from '../porter.model';\nimport { isServiceWorker } from '../porter.utils';\nimport { Logger } from '../porter.utils';\nimport { AgentManager } from '../managers/AgentManager';\nimport { ConnectionManager } from '../managers/ConnectionManager';\nimport { MessageHandler } from '../managers/MessageHandler';\n\nexport interface PorterSourceOptions {\n namespace?: string;\n debug?: boolean;\n}\n\nexport class PorterSource {\n private static instances: Map<string, PorterSource> = new Map();\n private readonly agentManager: AgentManager;\n private readonly messageHandler: MessageHandler;\n private readonly connectionManager: ConnectionManager;\n private readonly logger: Logger;\n private static staticLogger = Logger.getLogger('SW');\n private namespace: string;\n\n private constructor(namespace?: string, options?: PorterSourceOptions) {\n // Configure logger if debug option is provided\n if (options?.debug !== undefined) {\n Logger.configure({ enabled: options.debug });\n }\n\n this.logger = Logger.getLogger(`SW`);\n this.namespace = namespace || 'porter';\n if (!namespace) {\n this.logger.error('No namespace provided, defaulting to \"porter\"');\n }\n\n this.agentManager = new AgentManager(this.logger);\n this.messageHandler = new MessageHandler(this.agentManager, this.logger);\n this.connectionManager = new ConnectionManager(\n this.agentManager,\n this.namespace,\n this.logger\n );\n this.logger.info(`Constructing Porter with namespace: ${this.namespace}`);\n\n if (!isServiceWorker()) {\n throw new PorterError(\n PorterErrorType.INVALID_CONTEXT,\n 'Can only create in a service worker'\n );\n }\n\n // Wire up event handlers\n this.agentManager.on(\n 'agentMessage',\n (message: any, metadata: AgentInfo) => {\n this.messageHandler.handleIncomingMessage(message, metadata);\n }\n );\n\n this.agentManager.on('agentDisconnect', (metadata: AgentInfo) => {\n this.messageHandler.handleDisconnect(metadata);\n });\n\n this.agentManager.on('agentSetup', (agent: Agent) => {\n this.logger.debug(`Handling agent setup`, { agent });\n this.messageHandler.handleConnect(agent.info);\n this.connectionManager.confirmConnection(agent);\n });\n\n browser.runtime.onConnect.addListener(\n this.connectionManager.handleConnection.bind(this.connectionManager)\n );\n }\n\n public static getInstance(\n namespace: string = 'porter',\n options?: PorterSourceOptions\n ): PorterSource {\n PorterSource.staticLogger.debug(\n `Getting instance for namespace: ${namespace}`\n );\n if (!PorterSource.instances.has(namespace)) {\n PorterSource.staticLogger.info(\n `Creating new instance for namespace: ${namespace}`\n );\n PorterSource.instances.set(\n namespace,\n new PorterSource(namespace, options)\n );\n } else if (options?.debug !== undefined) {\n // If instance exists but debug setting changed, configure logger\n Logger.configure({ enabled: options.debug });\n }\n return PorterSource.instances.get(namespace)!;\n }\n\n // Public API methods that will be exposed via the source function\n public post(message: Message<any>, target?: MessageTarget): Promise<void> {\n return this.messageHandler.post(message, target);\n }\n\n public onMessage(config: MessageConfig): Unsubscribe {\n return this.messageHandler.onMessage(config);\n }\n\n public on(config: MessageConfig): Unsubscribe {\n return this.messageHandler.on(config);\n }\n\n public onConnect(listener: Listener<'onConnect'>): Unsubscribe {\n return this.messageHandler.onConnect(listener);\n }\n\n public onDisconnect(listener: Listener<'onDisconnect'>): Unsubscribe {\n return this.messageHandler.onDisconnect(listener);\n }\n\n public onMessagesSet(listener: Listener<'onMessagesSet'>): Unsubscribe {\n return this.messageHandler.onMessagesSet(listener);\n }\n\n // Utility methods that might be needed externally\n public getInfo(key: string): AgentInfo | null {\n return this.agentManager.getAgentById(key)?.info || null;\n }\n\n // Utility methods that might be needed externally\n public getAgentById(agentId: AgentId): Agent | null {\n return this.agentManager.getAgentById(agentId);\n }\n\n public getAgentByLocation(location: BrowserLocation): Agent | null {\n return this.agentManager.getAgentByLocation(location);\n }\n\n public queryAgents(location: Partial<BrowserLocation>): Agent[] {\n return this.agentManager.queryAgents(location);\n }\n}\n\nexport interface PorterAPI {\n type: 'source';\n post: (message: Message<any>, target?: MessageTarget) => Promise<void>;\n onMessage: (config: MessageConfig) => Unsubscribe;\n on: (config: MessageConfig) => Unsubscribe;\n onConnect: (listener: Listener<'onConnect'>) => Unsubscribe;\n onDisconnect: (listener: Listener<'onDisconnect'>) => Unsubscribe;\n onMessagesSet: (listener: Listener<'onMessagesSet'>) => Unsubscribe;\n getAgentById: (id: AgentId) => Agent | null;\n getAgentByLocation: (location: BrowserLocation) => Agent | null;\n queryAgents: (location: Partial<BrowserLocation>) => Agent[];\n}\n\nexport function source(\n namespace: string = 'porter',\n options?: PorterSourceOptions\n): PorterAPI {\n const instance = PorterSource.getInstance(namespace, options);\n return {\n type: 'source',\n post: instance.post.bind(instance),\n onMessage: instance.onMessage.bind(instance),\n on: instance.on.bind(instance),\n onConnect: instance.onConnect.bind(instance),\n onDisconnect: instance.onDisconnect.bind(instance),\n onMessagesSet: instance.onMessagesSet.bind(instance),\n getAgentById: instance.getAgentById.bind(instance),\n getAgentByLocation: instance.getAgentByLocation.bind(instance),\n queryAgents: instance.queryAgents.bind(instance),\n };\n}\n", "import browser from 'webextension-polyfill';\n\nexport type AgentId = string;\n\nexport enum PorterContext {\n ContentScript = 'contentscript',\n Extension = 'extension',\n Popup = 'popup',\n Sidepanel = 'sidepanel',\n Devtools = 'devtools',\n Options = 'options',\n Unknown = 'unknown',\n}\n\n// Describes how the connection was established\nexport enum ConnectionType {\n NewTab = 'new-tab',\n NewFrame = 'new-frame',\n Refresh = 'refresh',\n NewExtensionContext = 'new-extension-context',\n}\n\n// export interface AgentLocation {\n// context: PorterContext;\n// tabId?: number;\n// frameId?: number;\n// customIdentifier?: string;\n// }\n\nexport interface AgentInfo {\n id: AgentId;\n location: BrowserLocation;\n createdAt: number;\n lastActiveAt: number;\n}\n\nexport type Agent = {\n port?: browser.Runtime.Port;\n info: AgentInfo;\n};\n\n// export type AgentTarget = {\n// id?: AgentId;\n// context?: PorterContext;\n// location?: AgentLocation;\n// };\n\nexport type BrowserLocation = {\n context: PorterContext;\n tabId: number;\n frameId: number;\n};\n\nexport type MessageTarget =\n | BrowserLocation // Target a specific location\n | PorterContext // Target all agents in a specific context\n | string // Target agent by unique id (advanced)\n | number; // Target a content script by tabId (all frames)\n\nexport type Unsubscribe = () => void;\n\nexport type Message<K extends keyof MessageAction> = {\n action: K;\n target?: MessageTarget;\n payload?: MessageAction[K];\n};\n\nexport type MessageAction = {\n [key: string]: any;\n};\n\nexport type Listener<T extends keyof PorterEvent> = (\n arg: PorterEvent[T]\n) => void;\n\nexport type MessageListener = {\n config: MessageConfig;\n listener: Listener<'onMessage'>;\n};\n\nexport type MessageConfig = {\n [K in keyof MessageAction]: (message: Message<K>, info?: AgentInfo) => void;\n};\n\n// export type GetAgentOptions = {\n// context?: PorterContext;\n// index?: number;\n// subIndex?: number;\n// };\n\nexport interface PorterEvent {\n onConnect: AgentInfo;\n onDisconnect: AgentInfo;\n onMessagesSet: AgentInfo;\n onMessage: AgentInfo & { message: Message<any> };\n}\n\nexport enum PorterErrorType {\n CONNECTION_FAILED = 'connection-failed',\n CONNECTION_TIMEOUT = 'connection-timeout',\n INVALID_TARGET = 'invalid-target',\n MESSAGE_FAILED = 'message-failed',\n INVALID_CONTEXT = 'invalid-context',\n INVALID_PORT = 'invalid-port',\n}\n\nexport class PorterError extends Error {\n constructor(\n public type: PorterErrorType,\n message: string,\n public details?: any\n ) {\n super(message);\n this.name = 'PorterError';\n }\n}\n", "import { Runtime } from 'webextension-polyfill';\n\nfunction isServiceWorker() {\n return (\n typeof ServiceWorkerGlobalScope !== 'undefined' &&\n self instanceof ServiceWorkerGlobalScope\n );\n}\n\nfunction isValidPort(port: Runtime.Port): port is Runtime.Port & {\n sender: Runtime.MessageSender & { tab: { id: number }; frameId: number };\n} {\n return !!port && !!port.sender && isValidSender(port.sender);\n}\n\nfunction isValidSender(\n sender: Runtime.MessageSender\n): sender is Runtime.MessageSender & { tab: { id: number }; frameId: number } {\n return !(\n !sender ||\n !sender.tab ||\n sender.frameId === undefined ||\n sender.tab.id === undefined\n );\n}\n\nexport enum LogLevel {\n ERROR = 0,\n WARN = 1,\n INFO = 2,\n DEBUG = 3,\n TRACE = 4,\n}\n\nexport interface LoggerOptions {\n level?: LogLevel;\n enabled?: boolean;\n}\n\nexport class Logger {\n private static level: LogLevel = Logger.getLevel();\n private static enabled: boolean = false;\n private static instances: Map<string, Logger> = new Map();\n private static globalOptions?: LoggerOptions;\n\n private static getLevel(): LogLevel {\n if (Logger.globalOptions?.level !== undefined) {\n return Logger.globalOptions.level;\n }\n const isProd =\n typeof process !== 'undefined' &&\n (process.env?.NODE_ENV === 'production' ||\n process.env?.PORTER_ENV === 'production');\n return isProd ? LogLevel.WARN : LogLevel.TRACE;\n }\n // Add a configure method to set global options\n static configure(options: LoggerOptions) {\n Logger.globalOptions = options;\n if (options.level !== undefined) {\n Logger.level = options.level;\n }\n if (options.enabled !== undefined) {\n Logger.enabled = options.enabled;\n }\n }\n\n // Factory method to get or create logger instance\n static getLogger(context: string): Logger {\n if (!this.instances.has(context)) {\n this.instances.set(context, new Logger(context));\n }\n return this.instances.get(context)!;\n }\n\n private constructor(private context: string) {}\n\n error(message: string, ...args: any[]) {\n if (!Logger.enabled) return;\n if (Logger.level >= LogLevel.ERROR) {\n console.error(`[Porter:${this.context}] ${message}`, ...args);\n }\n }\n\n warn(message: string, ...args: any[]) {\n if (!Logger.enabled) return;\n if (Logger.level >= LogLevel.WARN) {\n console.warn(`[Porter:${this.context}] ${message}`, ...args);\n }\n }\n\n info(message: string, ...args: any[]) {\n if (!Logger.enabled) return;\n if (Logger.level >= LogLevel.INFO) {\n console.info(`[Porter:${this.context}] ${message}`, ...args);\n }\n }\n\n debug(message: string, ...args: any[]) {\n if (!Logger.enabled) return;\n if (Logger.level >= LogLevel.DEBUG) {\n console.debug(`[Porter:${this.context}] ${message}`, ...args);\n }\n }\n\n trace(message: string, ...args: any[]) {\n if (!Logger.enabled) return;\n if (Logger.level >= LogLevel.TRACE) {\n console.trace(`[Porter:${this.context}] ${message}`, ...args);\n }\n }\n}\n\nexport { isValidPort, isValidSender, isServiceWorker };\n", "import browser, { Runtime } from 'webextension-polyfill';\nimport { v4 as uuidv4 } from 'uuid';\nimport {\n Agent,\n AgentInfo,\n ConnectionType,\n PorterContext,\n MessageTarget,\n AgentId,\n BrowserLocation,\n} from '../porter.model';\nimport { Logger } from '../porter.utils';\n\nexport interface AgentOperations {\n addAgent(port: Runtime.Port, context?: PorterContext): AgentId | undefined;\n queryAgents(location: Partial<BrowserLocation>): Agent[];\n getAgentById(id: AgentId): Agent | null;\n getAgentsByContext(context: PorterContext): Agent[];\n getAgentByLocation(location: BrowserLocation): Agent | null;\n getAllAgents(): Agent[];\n getAllAgentsInfo(): AgentInfo[];\n hasPort(port: Runtime.Port): boolean;\n removeAgent(agentId: AgentId): void;\n printAgents(): void;\n}\n\nexport interface AgentEventEmitter {\n on(event: 'agentSetup', handler: (agent: Agent) => void): void;\n on(\n event: 'agentMessage',\n handler: (message: any, info: AgentInfo) => void\n ): void;\n on(event: 'agentDisconnect', handler: (info: AgentInfo) => void): void;\n}\n\nexport class AgentManager implements AgentOperations, AgentEventEmitter {\n private agents: Map<AgentId, Runtime.Port> = new Map();\n private agentsInfo: Map<AgentId, AgentInfo> = new Map();\n private eventHandlers: Map<string, Set<Function>> = new Map();\n\n constructor(private logger: Logger) {\n this.eventHandlers.set('agentSetup', new Set());\n this.eventHandlers.set('agentMessage', new Set());\n this.eventHandlers.set('agentDisconnect', new Set());\n }\n\n public addAgent(\n port: Runtime.Port,\n context?: PorterContext\n ): AgentId | undefined {\n this.logger.debug(`Adding agent`, { context, port });\n const connectionSource = this.identifyConnectionSource(port);\n if (!connectionSource) {\n this.logger.error(`Cannot add agent that did not have a sender`);\n return;\n }\n\n const determinedContext = connectionSource.context;\n const tabId = connectionSource.tabId || -1;\n const frameId = connectionSource.frameId || 0;\n\n this.logger.debug(`Determined context for new agent`, {\n determinedContext,\n tabId,\n frameId,\n });\n // Find agents in the same tab or under the same extension context\n const tabAgentsInfo = Array.from(this.agentsInfo.values()).filter(\n (info) => {\n return (\n info.location.context === determinedContext &&\n info.location.tabId === tabId &&\n info.location.frameId === frameId\n );\n }\n );\n\n if (tabAgentsInfo.length > 0) {\n this.logger.debug('Adding agent: Found existing similar agent.', {\n tabAgentsInfo,\n });\n }\n\n const agentId =\n this.getAgentByLocation({ context: determinedContext, tabId, frameId })\n ?.info?.id || (uuidv4() as AgentId);\n\n this.logger.debug(`Adding agent with id: ${agentId}`);\n\n this.agents.set(agentId, port);\n\n const agentInfo: AgentInfo = {\n id: agentId,\n location: { context: determinedContext, tabId, frameId },\n createdAt: Date.now(),\n lastActiveAt: Date.now(),\n };\n this.agentsInfo.set(agentId, agentInfo);\n\n this.logger.debug(`Constructed agent info: ${JSON.stringify(agentInfo)}`);\n port.onMessage.addListener((message: any) =>\n this.emit('agentMessage', message, agentInfo)\n );\n\n const agent: Agent = { port, info: agentInfo };\n port.onDisconnect.addListener(() => {\n this.emit('agentDisconnect', agentInfo);\n this.logger.debug('Agent disconnected, removing from manager. ', {\n agentInfo,\n });\n this.removeAgent(agentId);\n });\n\n this.emit('agentSetup', agent);\n this.logger.debug('Setup complete for adding agent. ', {\n agentInfo,\n });\n return agentId;\n }\n\n public getAgentByLocation(location: BrowserLocation): Agent | null {\n const { context, tabId, frameId } = location;\n\n const infoEntry: [AgentId, AgentInfo] | undefined = Array.from(\n this.agentsInfo.entries()\n ).find(\n ([key, info]) =>\n info.location.context === context &&\n info.location.tabId === tabId &&\n info.location.frameId === frameId\n );\n if (infoEntry === undefined) {\n this.logger.error('No agent found for location. ', {\n location,\n });\n return null;\n }\n const agentId = infoEntry[0];\n let port = this.agents.get(agentId);\n let info = this.agentsInfo.get(agentId);\n if (!port || !info) {\n this.logger.error('No agent found for location. ', {\n location,\n });\n return null;\n }\n return { port, info };\n }\n\n public getAgentsByContext(context: PorterContext): Agent[] {\n let infoForAgents = Array.from(this.agentsInfo.entries()).filter(\n ([key, value]) => value.location.context === context\n );\n return infoForAgents.map(([key, value]) => ({\n port: this.agents.get(key),\n info: value,\n }));\n }\n\n public getAllAgents(): Agent[] {\n let allInfo = Array.from(this.agentsInfo.entries());\n return allInfo.map(([key, value]) => ({\n port: this.agents.get(key),\n info: value,\n }));\n }\n\n public queryAgents(location: Partial<BrowserLocation>): Agent[] {\n let infoForAgents = Array.from(this.agentsInfo.entries()).filter(\n ([key, value]) => {\n const hasContext = location.context\n ? value.location.context === location.context\n : true;\n const hasTabId = location.tabId\n ? value.location.tabId === location.tabId\n : true;\n const hasFrameId = location.frameId\n ? value.location.frameId === location.frameId\n : true;\n return hasContext && hasTabId && hasFrameId;\n }\n );\n return infoForAgents.map(([key, value]) => ({\n port: this.agents.get(key),\n info: value,\n }));\n }\n\n public getAgentById(id: AgentId): Agent | null {\n let port = this.agents.get(id);\n let info = this.agentsInfo.get(id);\n if (!port || !info) {\n this.logger.error('No agent found for agentId. ', {\n id,\n });\n return null;\n }\n return { port, info };\n }\n\n public getAllAgentsInfo(): AgentInfo[] {\n return Array.from(this.agentsInfo.values());\n }\n\n public hasPort(port: Runtime.Port): boolean {\n const matchingPort = Array.from(this.agents.values()).find(\n (p) => p.name === port.name\n );\n return !!matchingPort;\n }\n\n public removeAgent(agentId: AgentId) {\n if (this.agents.has(agentId) && this.agentsInfo.has(agentId)) {\n this.agents.delete(agentId);\n this.agentsInfo.delete(agentId);\n } else {\n this.logger.error('No agent found to remove. ', {\n agentId,\n });\n }\n }\n\n public printAgents() {\n const allAgents = Array.from(this.agents.entries());\n const allAgentsInfo = Array.from(this.agentsInfo.entries());\n this.logger.debug('Current agents:', {\n allAgents,\n allAgentsInfo,\n });\n }\n\n // private isContentScript(port: Runtime.Port) {\n // if (!port.sender) return false;\n // const hasFrame =\n // port.sender.tab &&\n // port.sender.tab.id !== undefined &&\n // port.sender.frameId !== undefined;\n // if (!hasFrame) return false;\n // if (!(port.sender as any).origin) return false;\n\n // const contentPage =\n // !(port.sender as any)!.origin.startsWith('chrome-extension://') &&\n // !(port.sender as any)!.tab!.url?.startsWith('moz-extension://');\n // return contentPage;\n // }\n\n public on(event: string, handler: Function) {\n const handlers = this.eventHandlers.get(event);\n if (handlers) {\n handlers.add(handler);\n }\n }\n\n private emit(event: string, ...args: any[]) {\n const handlers = this.eventHandlers.get(event);\n handlers?.forEach((handler) => handler(...args));\n }\n\n private identifyConnectionSource(port: Runtime.Port): {\n context: PorterContext;\n tabId?: number;\n frameId?: number;\n url?: string;\n portName?: string;\n } | null {\n const sender = port.sender;\n if (!sender) {\n this.logger.error(`Cannot add agent that did not have a sender`);\n return null;\n }\n // Cache the manifest data\n const manifest = browser.runtime.getManifest();\n\n // Extract page URLs from manifest\n const sidePanel = (manifest as any)?.side_panel?.default_path || '';\n const optionsPage = (manifest as any).options_page || '';\n const popupPage = (manifest as any).action?.default_popup || '';\n const devtoolsPage = (manifest as any).devtools_page || '';\n const newTabOverride = (manifest as any).chrome_url_overrides?.newtab || '';\n const bookmarksOverride =\n (manifest as any).chrome_url_overrides?.bookmarks || '';\n const historyOverride =\n (manifest as any).chrome_url_overrides?.history || '';\n\n // Create URL endings for matching\n // (handles both full paths and just filenames)\n const pageMatchers = {\n sidepanel: sidePanel ? sidePanel.split('/').pop() : 'sidepanel.html',\n options: optionsPage ? optionsPage.split('/').pop() : 'options.html',\n popup: popupPage ? popupPage.split('/').pop() : 'popup.html',\n devtools: devtoolsPage ? devtoolsPage.split('/').pop() : 'devtools.html',\n newtab: newTabOverride ? newTabOverride.split('/').pop() : 'newtab.html',\n bookmarks: bookmarksOverride\n ? bookmarksOverride.split('/').pop()\n : 'bookmarks.html',\n history: historyOverride\n ? historyOverride.split('/').pop()\n : 'history.html',\n };\n\n // Content scripts (web pages)\n if (sender.tab && sender.url && !sender.url.includes('extension://')) {\n return {\n context: PorterContext.ContentScript,\n tabId: sender.tab.id,\n frameId: sender.frameId || 0,\n url: sender.url,\n portName: port.name,\n };\n }\n\n // Extension pages\n if (sender.url && sender.url.includes('extension://')) {\n const urlPath = new URL(sender.url).pathname;\n const filename = urlPath.split('/').pop();\n\n // Check against our manifest-derived page matchers\n for (const [pageType, pageMatcher] of Object.entries(pageMatchers)) {\n if (filename === pageMatcher) {\n // It's a main extension page\n // Different handling based on presence of tab\n\n return {\n context: pageType as PorterContext,\n tabId: sender.tab?.id || 0,\n frameId: sender.frameId || 0,\n url: sender.url,\n portName: port.name,\n };\n }\n }\n\n // It's some other extension page not specifically listed in our matchers\n\n return {\n context: PorterContext.Unknown,\n tabId: sender.tab?.id || 0,\n frameId: sender.frameId || 0,\n url: sender.url,\n portName: port.name,\n };\n }\n\n // Fallback for unknown sources\n return {\n context: PorterContext.Unknown,\n tabId: 0,\n url: sender.url,\n portName: port.name,\n };\n }\n}\n", "import { Runtime } from 'webextension-polyfill';\nimport { AgentOperations } from './AgentManager';\nimport {\n Agent,\n AgentInfo,\n PorterError,\n PorterErrorType,\n} from '../porter.model';\nimport { Logger } from '../porter.utils';\n\nexport class ConnectionManager {\n constructor(\n private agentOperations: AgentOperations,\n private namespace: string,\n private logger: Logger\n ) {}\n\n public handleConnection(port: Runtime.Port) {\n try {\n this.logger.info('New connection request:', port.name);\n if (!port.name) {\n throw new PorterError(\n PorterErrorType.INVALID_CONTEXT,\n 'Port name not provided'\n );\n }\n\n if (!port.name || !port.name.startsWith(this.namespace + ':')) {\n throw new PorterError(\n PorterErrorType.INVALID_CONTEXT,\n `Invalid namespace or port name format. port name was ${port?.name || 'port undefined'} but namespace is ${this.namespace}`\n );\n }\n\n port.onMessage.addListener(this.handleInitMessage.bind(this, port));\n\n setTimeout(() => {\n if (!this.agentOperations.hasPort(port)) {\n try {\n port.disconnect();\n } catch (e) {\n this.logger.error('Failed to disconnect port:', e);\n }\n }\n }, 5000);\n } catch (error) {\n this.handleConnectionError(port, error as Error);\n }\n }\n\n private handleInitMessage(port: Runtime.Port, message: any): void {\n // Process only the init message\n if (message.action !== 'porter-init') {\n return;\n }\n\n try {\n // Remove this listener since we only need it once\n port.onMessage.removeListener(this.handleInitMessage.bind(this, port));\n\n const { connectionId } = message.payload;\n\n if (!connectionId) {\n throw new PorterError(\n PorterErrorType.INVALID_CONTEXT,\n 'Missing context or connection ID. Message was: ' +\n JSON.stringify(message)\n );\n }\n\n // Now add the agent with the provided context\n const agentId = this.agentOperations.addAgent(port);\n\n if (!agentId) {\n throw new PorterError(\n PorterErrorType.INVALID_CONTEXT,\n 'Failed to add agent'\n );\n }\n\n // Get the agent info to send back\n const agent = this.agentOperations.getAgentById(agentId);\n\n if (agent) {\n this.confirmConnection(agent);\n }\n\n this.agentOperations.printAgents();\n } catch (error) {\n this.handleConnectionError(port, error as Error);\n }\n }\n\n private handleConnectionError(port: Runtime.Port, error: Error): void {\n const porterError =\n error instanceof PorterError\n ? error\n : new PorterError(\n PorterErrorType.CONNECTION_FAILED,\n error instanceof Error ? error.message : 'Unknown connection error',\n { originalError: error }\n );\n this.logger.error('Connection handling failed: ', {\n porterError,\n });\n try {\n port.postMessage({\n action: 'porter-error',\n payload: { error: porterError },\n });\n } catch (e) {\n this.logger.error('Failed to send error message: ', {\n error: e,\n });\n }\n\n try {\n port.disconnect();\n } catch (e) {\n this.logger.error('Failed to disconnect port: ', {\n error: e,\n });\n }\n }\n\n public confirmConnection(agent: Agent) {\n this.logger.debug('Sending confirmation message back to initiator ', {\n agent,\n });\n if (!agent.port) {\n throw new PorterError(\n PorterErrorType.INVALID_PORT,\n 'Agent port is undefined when confirming connection'\n );\n }\n agent.port.postMessage({\n action: 'porter-handshake',\n payload: {\n info: agent.info,\n currentConnections: this.agentOperations.getAllAgentsInfo(),\n },\n });\n }\n}\n", "import { Runtime } from 'webextension-polyfill';\nimport { AgentOperations } from './AgentManager';\nimport {\n PorterEvent,\n Listener,\n MessageListener,\n Message,\n PorterErrorType,\n MessageConfig,\n PorterContext,\n PorterError,\n AgentInfo,\n MessageTarget,\n BrowserLocation,\n AgentId,\n} from '../porter.model';\nimport { Logger } from '../porter.utils';\n\nexport class MessageHandler {\n private eventListeners: Map<\n keyof PorterEvent,\n Set<Listener<keyof PorterEvent>>\n > = new Map();\n private messageListeners: Set<MessageListener> = new Set();\n private initializationHandler: MessageConfig;\n\n constructor(\n private agentOperations: AgentOperations,\n private logger: Logger\n ) {\n this.initializationHandler = {\n 'porter-messages-established': (\n message: Message<any>,\n agent?: AgentInfo\n ) => {\n if (!agent || !agent.id) return;\n const agentInfo = this.agentOperations.getAgentById(agent.id)?.info;\n if (!agentInfo) {\n this.logger.error('No agent info found for agent id: ', agent.id);\n return;\n }\n this.logger.debug(\n 'internalHandlers, established message received: ',\n agent.id,\n message\n );\n this.emitEvent('onMessagesSet', agentInfo);\n },\n };\n }\n\n public async post(\n message: Message<any>,\n target?: MessageTarget\n ): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n this.logger.debug('Post request received:', {\n action: message.action,\n target,\n });\n\n const timeoutId = setTimeout(() => {\n const error = new Error('Message posting timed out');\n this.logger.error('Post timeout:', error);\n reject(error);\n }, 5000);\n\n if (target === undefined) {\n this.broadcastMessage(message);\n // how to tell if target is BrowserLocation type?\n } else if (isBrowserLocation(target)) {\n this.postToLocation(message, target);\n } else if (isPorterContext(target)) {\n this.postToContext(message, target);\n } else if (typeof target === 'string') {\n this.postToId(message, target);\n } else {\n this.postToTab(message, target);\n }\n\n clearTimeout(timeoutId);\n resolve();\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error';\n this.logger.error('Failed to post message:', errorMessage);\n reject(new Error(`Failed to post message: ${errorMessage}`));\n }\n });\n }\n\n private broadcastMessage(message: Message<any>): void {\n this.logger.info('Broadcasting message to all agents: ', message);\n this.agentOperations.getAllAgents().forEach((agent) => {\n if (agent.port) {\n agent.port.postMessage(message);\n }\n });\n }\n\n // Post to all frames in a tab\n private postToTab(message: Message<any>, tabId: number): void {\n // const key = `${PorterContext.ContentScript}:${tabId}:0`;\n const agents = this.agentOperations.queryAgents({\n context: PorterContext.ContentScript,\n tabId,\n });\n if (agents.length === 0) {\n this.logger.warn('post: No agents found for tab: ', tabId);\n throw new PorterError(\n PorterErrorType.MESSAGE_FAILED,\n `Failed to post message to tabId ${tabId}`,\n { originalError: message }\n );\n return;\n }\n agents.forEach((agent) => {\n if (agent.port) {\n this.postToPort(message, agent.port);\n }\n });\n }\n\n private postToLocation(\n message: Message<any>,\n location: BrowserLocation\n ): void {\n const agents = this.agentOperations.queryAgents(location);\n agents.forEach((agent) => {\n if (!agent.port) {\n throw new PorterError(\n PorterErrorType.INVALID_TARGET,\n `No port found for agent`,\n { agentInfo: agent.info }\n );\n return;\n }\n this.postToPort(message, agent.port);\n });\n }\n\n private postToContext(message: Message<any>, context: PorterContext): void {\n const agents = this.agentOperations.queryAgents({\n context,\n });\n agents.forEach((agent) => {\n if (!agent.port) {\n throw new PorterError(\n PorterErrorType.INVALID_TARGET,\n `No port found for agent`,\n { agentInfo: agent.info }\n );\n return;\n }\n this.postToPort(message, agent.port);\n });\n }\n\n private postToPort(message: Message<any>, port: Runtime.Port): void {\n try {\n port.postMessage(message);\n } catch (error) {\n throw new PorterError(\n PorterErrorType.MESSAGE_FAILED,\n `Failed to post message to port`,\n { originalError: error, message }\n );\n }\n }\n\n private postToId(message: Message<any>, agentId: AgentId): void {\n const agent = this.agentOperations.getAgentById(agentId);\n if (!agent?.port) {\n throw new PorterError(\n PorterErrorType.INVALID_TARGET,\n `No agent found for key: ${agentId}`\n );\n }\n this.postToPort(message, agent.port);\n }\n\n public onMessage(config: MessageConfig) {\n // Optionally: Check for existing listeners with same config\n const existingListener = Array.from(this.messageListeners).find(\n (listener) => JSON.stringify(listener.config) === JSON.stringify(config)\n );\n\n if (existingListener) {\n this.logger.warn(\n `Listener with same config already exists: ${JSON.stringify(config)}`\n );\n }\n\n const messageListener: MessageListener = {\n config,\n listener: (event: PorterEvent['onMessage']) => {\n const handler = config[event.message.action];\n if (handler) {\n this.logger.debug('onMessage, calling handler ', { event });\n const { message, ...info } = event;\n handler(message, info);\n } else {\n this.logger.debug('onMessage, no handler found ', { event });\n }\n },\n };\n this.messageListeners.add(messageListener);\n\n return () => {\n this.messageListeners.delete(messageListener);\n };\n }\n\n // Adding new 'on' method that works the same way as onMessage\n public on(config: MessageConfig) {\n return this.onMessage(config);\n }\n\n // Handles messages incomng from ports\n public handleIncomingMessage(message: any, info: AgentInfo) {\n this.logger.debug(`Received message`, {\n message,\n info,\n });\n\n this.emitMessage({ ...info, message });\n }\n\n private emitEvent<T extends keyof PorterEvent>(\n event: T,\n arg: PorterEvent[T]\n ) {\n this.logger.debug('emitting event: ', event, arg);\n this.eventListeners\n .get(event)\n ?.forEach((listener) => (listener as Listener<T>)(arg));\n }\n\n // Dispatches incoming messages, either to a registered listener on the source, or to a specific agent\n // if a target was specified (calling this a relay)\n private emitMessage(messageEvent: PorterEvent['onMessage']) {\n this.logger.debug('Dispatching incoming message to subscribers', {\n messageEvent,\n });\n\n if (messageEvent.message.action.startsWith('porter-')) {\n const handler = this.initializationHandler[messageEvent.message.action];\n if (handler) {\n this.logger.debug('Internal message being handled', {\n messageEvent,\n });\n const { message, ...info } = messageEvent;\n handler(message, info);\n return;\n }\n }\n\n // Handle relaying to a target\n if (!!messageEvent.message.target) {\n this.logger.debug(\n 'Relaying message to target:',\n messageEvent.message.target\n );\n this.post(messageEvent.message, messageEvent.message.target);\n }\n\n let handlerCount = 0;\n\n this.logger.trace('Processing message with registered handlers');\n for (const { listener, config } of this.messageListeners) {\n if (config[messageEvent.message.action]) {\n listener(messageEvent as PorterEvent['onMessage']);\n handlerCount++;\n this.logger.debug('Message handled by registered listener: ', {\n listener,\n config,\n });\n }\n }\n\n if (handlerCount === 0) {\n this.logger.warn(\n 'No handler found for message:',\n messageEvent.message.action\n );\n } else {\n this.logger.debug(\n `Message handled by ${handlerCount} registered listeners`\n );\n }\n }\n\n public addListener<T extends keyof PorterEvent>(\n event: T,\n listener: Listener<T>\n ) {\n if (!this.eventListeners.has(event)) {\n this.eventListeners.set(event, new Set());\n }\n this.eventListeners\n .get(event)!\n .add(listener as Listener<keyof PorterEvent>);\n\n return () => {\n this.eventListeners\n .get(event)\n ?.delete(listener as Listener<keyof PorterEvent>);\n };\n }\n\n public handleDisconnect(info: AgentInfo) {\n // Remove all message listeners for this agent\n this.messageListeners.forEach((messageListener) => {\n if (messageListener.config[info.id]) {\n this.messageListeners.delete(messageListener);\n }\n });\n this.logger.info('Agent disconnected:', { info });\n this.emitEvent('onDisconnect', info);\n }\n\n public handleConnect(info: AgentInfo) {\n this.logger.info('Agent connected:', { info });\n this.emitEvent('onConnect', info);\n }\n\n public onConnect(listener: Listener<'onConnect'>) {\n return this.addListener('onConnect', listener);\n }\n\n public onMessagesSet(listener: Listener<'onMessagesSet'>) {\n return this.addListener('onMessagesSet', listener);\n }\n\n public onDisconnect(listener: Listener<'onDisconnect'>) {\n return this.addListener('onDisconnect', listener);\n }\n}\n\n// Type guard for BrowserLocation\nfunction isBrowserLocation(target: MessageTarget): target is BrowserLocation {\n return (\n typeof target === 'object' &&\n target !== null &&\n 'context' in target &&\n 'tabId' in target &&\n 'frameId' in target\n );\n}\n\nfunction isPorterContext(target: MessageTarget): target is PorterContext {\n return (\n typeof target === 'string' &&\n Object.values(PorterContext).includes(target as PorterContext)\n );\n}\n", "import browser, { Runtime } from 'webextension-polyfill';\nimport { AgentInfo, PorterError, PorterErrorType } from '../porter.model';\nimport { Logger } from '../porter.utils';\nimport { MessageQueue } from './MessageQueue';\n\nexport type DisconnectCallback = () => void;\nexport type ReconnectCallback = (info: AgentInfo) => void;\n\nexport class AgentConnectionManager {\n private readonly CONNECTION_TIMEOUT = 5000;\n private readonly RECONNECT_INTERVAL = 1000; // 1 second\n private connectionTimer: NodeJS.Timeout | null = null;\n private reconnectTimer: NodeJS.Timeout | null = null;\n private agentInfo: AgentInfo | null = null;\n private port: Runtime.Port | null = null;\n private readonly logger: Logger;\n private readonly connectionId: string;\n private readonly messageQueue: MessageQueue;\n private isReconnecting: boolean = false;\n private reconnectAttemptCount: number = 0;\n\n // Event callbacks\n private disconnectCallbacks: Set<DisconnectCallback> = new Set();\n private reconnectCallbacks: Set<ReconnectCallback> = new Set();\n\n constructor(\n private readonly namespace: string,\n logger: Logger\n ) {\n this.connectionId = `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;\n this.logger = logger;\n this.messageQueue = new MessageQueue(logger);\n }\n\n /**\n * Register a callback to be called when the connection is lost\n * @returns Unsubscribe function\n */\n public onDisconnect(callback: DisconnectCallback): () => void {\n this.disconnectCallbacks.add(callback);\n return () => {\n this.disconnectCallbacks.delete(callback);\n };\n }\n\n /**\n * Register a callback to be called when reconnection succeeds\n * @returns Unsubscribe function\n */\n public onReconnect(callback: ReconnectCallback): () => void {\n this.reconnectCallbacks.add(callback);\n return () => {\n this.reconnectCallbacks.delete(callback);\n };\n }\n\n private emitDisconnect(): void {\n this.logger.debug('Emitting disconnect event', {\n callbackCount: this.disconnectCallbacks.size,\n });\n this.disconnectCallbacks.forEach((callback) => {\n try {\n callback();\n } catch (error) {\n this.logger.error('Error in disconnect callback:', error);\n }\n });\n }\n\n private emitReconnect(info: AgentInfo): void {\n this.logger.debug('Emitting reconnect event', {\n callbackCount: this.reconnectCallbacks.size,\n info,\n });\n this.reconnectCallbacks.forEach((callback) => {\n try {\n callback(info);\n } catch (error) {\n this.logger.error('Error in reconnect callback:', error);\n }\n });\n }\n\n public async initializeConnection(): Promise<void> {\n try {\n if (this.connectionTimer) {\n clearTimeout(this.connectionTimer);\n }\n\n const portName = `${this.namespace}:${this.connectionId}`;\n this.logger.debug('Connecting new port with name: ', { portName });\n this.port = browser.runtime.connect({ name: portName });\n\n const handshakePromise = new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(\n () =>\n reject(\n new PorterError(\n PorterErrorType.CONNECTION_TIMEOUT,\n 'Connection timed out waiting for handshake'\n )\n ),\n this.CONNECTION_TIMEOUT\n );\n\n const onMessage = (message: any) => {\n if (message.action === 'porter-handshake') {\n this.logger.debug('Received handshake:', message);\n clearTimeout(timeout);\n this.agentInfo = message.payload.info;\n this.logger.debug('Handshake agent info:', {\n agentInfo: this.agentInfo,\n });\n this.port?.onMessage.removeListener(onMessage);\n resolve();\n } else if (message.action === 'porter-error') {\n clearTimeout(timeout);\n this.port?.onMessage.removeListener(onMessage);\n this.logger.error('Error:', message);\n reject(\n new PorterError(\n message.payload.type,\n message.payload.message,\n message.payload.details\n )\n );\n }\n };\n\n this.port?.onMessage.addListener(onMessage);\n });\n\n this.port?.postMessage({\n action: 'porter-init',\n payload: {\n info: this.agentInfo,\n connectionId: this.connectionId,\n },\n });\n\n await handshakePromise;\n\n // After successful connection, process any queued messages\n await this.processQueuedMessages();\n } catch (error) {\n this.logger.error('Connection initialization failed:', error);\n this.handleDisconnect(this.port!);\n throw error;\n }\n }\n\n private async processQueuedMessages(): Promise<void> {\n if (!this.port || this.messageQueue.isEmpty()) {\n return;\n }\n\n const messages = this.messageQueue.dequeue();\n this.logger.info(\n `Processing ${messages.length} queued messages after reconnection`\n );\n\n for (const { message, target } of messages) {\n try {\n // Send message in the same format as normal messages\n const messageToSend = { ...message };\n if (target) {\n messageToSend.target = target;\n }\n this.port.postMessage(messageToSend);\n this.logger.debug('Successfully resent queued message:', {\n message: messageToSend,\n });\n } catch (error) {\n this.logger.error('Failed to process queued message:', error);\n // Re-queue the message if it fails\n this.messageQueue.enqueue(message, target);\n this.logger.debug('Re-queued failed message for retry');\n }\n }\n }\n\n public getPort(): Runtime.Port | null {\n return this.port;\n }\n\n public getAgentInfo(): AgentInfo | null {\n return this.agentInfo;\n }\n\n public getNamespace(): string {\n return this.namespace;\n }\n\n public handleDisconnect(port: Runtime.Port) {\n this.logger.info('Port disconnected', {\n portName: port.name,\n connectionId: this.connectionId,\n queuedMessages: this.messageQueue.isEmpty() ? 0 : 'some',\n });\n this.port = null;\n this.agentInfo = null;\n\n // Notify listeners of disconnection\n this.emitDisconnect();\n\n // Start reconnection attempts if not already reconnecting\n if (!this.isReconnecting) {\n this.startReconnectionAttempts();\n }\n }\n\n private startReconnectionAttempts(): void {\n this.isReconnecting = true;\n this.reconnectAttemptCount = 0;\n\n if (this.reconnectTimer) {\n clearInterval(this.reconnectTimer);\n }\n\n this.logger.info('Starting reconnection attempts', {\n interval: this.RECONNECT_INTERVAL,\n queuedMessages: this.messageQueue.isEmpty() ? 0 : 'some',\n });\n\n this.reconnectTimer = setInterval(async () => {\n this.reconnectAttemptCount++;\n try {\n this.logger.debug(`Reconnection attempt ${this.reconnectAttemptCount}`);\n await this.initializeConnection();\n this.isReconnecting = false;\n if (this.reconnectTimer) {\n clearInterval(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n this.logger.info('Reconnection successful', {\n attempts: this.reconnectAttemptCount,\n queuedMessages: this.messageQueue.isEmpty() ? 0 : 'some',\n });\n\n // Notify listeners of successful reconnection\n if (this.agentInfo) {\n this.emitReconnect(this.agentInfo);\n }\n } catch (error) {\n this.logger.debug(\n `Reconnection attempt ${this.reconnectAttemptCount} failed:`,\n error\n );\n }\n }, this.RECONNECT_INTERVAL);\n }\n\n public queueMessage(message: any, target?: any): void {\n this.messageQueue.enqueue(message, target);\n this.logger.debug('Message queued for retry', {\n message,\n target,\n queueSize: this.messageQueue.isEmpty() ? 0 : 'some',\n });\n }\n}\n", "import { Message, BrowserLocation } from '../porter.model';\nimport { Logger } from '../porter.utils';\n\ninterface QueuedMessage {\n message: Message<any>;\n target?: BrowserLocation;\n timestamp: number;\n}\n\nexport class MessageQueue {\n private queue: QueuedMessage[] = [];\n private readonly logger: Logger;\n private readonly maxQueueSize: number = 1000; // Prevent memory issues\n private readonly maxMessageAge: number = 5 * 60 * 1000; // 5 minutes\n\n constructor(logger: Logger) {\n this.logger = logger;\n this.logger.debug('MessageQueue initialized', {\n maxQueueSize: this.maxQueueSize,\n maxMessageAge: `${this.maxMessageAge / 1000} seconds`,\n });\n }\n\n public enqueue(message: Message<any>, target?: BrowserLocation): void {\n // Remove old messages\n const oldCount = this.queue.length;\n this.cleanup();\n if (oldCount !== this.queue.length) {\n this.logger.debug(\n `Cleaned up ${oldCount - this.queue.length} old messages`\n );\n }\n\n // Check if queue is full\n if (this.queue.length >= this.maxQueueSize) {\n this.logger.warn('Message queue is full, dropping oldest message', {\n queueSize: this.queue.length,\n maxSize: this.maxQueueSize,\n });\n this.queue.shift();\n }\n\n this.queue.push({\n message,\n target,\n timestamp: Date.now(),\n });\n\n this.logger.debug('Message queued', {\n queueSize: this.queue.length,\n message,\n target,\n timestamp: new Date().toISOString(),\n });\n }\n\n public dequeue(): QueuedMessage[] {\n const messages = [...this.queue];\n this.queue = [];\n this.logger.info(`Dequeued ${messages.length} messages`, {\n oldestMessage: messages[0]\n ? new Date(messages[0].timestamp).toISOString()\n : null,\n newestMessage: messages[messages.length - 1]\n ? new Date(messages[messages.length - 1].timestamp).toISOString()\n : null,\n });\n return messages;\n }\n\n public isEmpty(): boolean {\n return this.queue.length === 0;\n }\n\n private cleanup(): void {\n const now = Date.now();\n const oldCount = this.queue.length;\n this.queue = this.queue.filter(\n (item) => now - item.timestamp < this.maxMessageAge\n );\n if (oldCount !== this.queue.length) {\n this.logger.debug(\n `Cleaned up ${oldCount - this.queue.length} expired messages`,\n {\n remaining: this.queue.length,\n maxAge: `${this.maxMessageAge / 1000} seconds`,\n }\n );\n }\n }\n}\n", "import { Runtime } from 'webextension-polyfill';\nimport {\n BrowserLocation,\n Message,\n MessageConfig,\n PorterError,\n PorterErrorType,\n} from '../porter.model';\nimport { Logger } from '../porter.utils';\n\nexport class AgentMessageHandler {\n private readonly MAX_QUEUE_SIZE = 1000;\n private readonly MESSAGE_TIMEOUT = 30000;\n private messageQueue: Array<{ message: Message<any>; timestamp: number }> =\n [];\n private handlers: Map<string, Array<Function>> = new Map();\n\n constructor(private readonly logger: Logger) {}\n\n public handleMessage(port: Runtime.Port, message: any) {\n this.logger.debug('handleMessage, message: ', message);\n if (this.handlers.size === 0) {\n if (this.messageQueue.length >= this.MAX_QUEUE_SIZE) {\n this.logger.warn('Message queue full, dropping message:', message);\n return;\n }\n this.logger.warn(\n 'No message handlers configured yet, queueing message: ',\n message\n );\n this.messageQueue.push({ message, timestamp: Date.now() });\n return;\n }\n this.processMessage(port, message);\n }\n\n // Legacy method - internally uses the new system\n public onMessage(config: MessageConfig) {\n this.logger.debug('Setting message handlers from config: ', config);\n // Clear previous handlers to maintain backward compatibility\n this.handlers.clear();\n this.on(config);\n\n this.processQueuedMessages();\n }\n\n public on(config: MessageConfig) {\n this.logger.debug('Adding message handlers from config: ', config);\n\n Object.entries(config).forEach(([action, handler]) => {\n if (!this.handlers.has(action)) {\n this.handlers.set(action, []);\n }\n this.handlers.get(action)!.push(handler);\n });\n\n this.processQueuedMessages();\n }\n\n private processQueuedMessages() {\n while (this.messageQueue.length > 0) {\n const item = this.messageQueue[0];\n if (Date.now() - item.timestamp > this.MESSAGE_TIMEOUT) {\n this.logger.warn(\n 'Message timeout, dropping message: ',\n this.messageQueue.shift()\n );\n continue;\n }\n this.processMessage(null!, item.message);\n this.messageQueue.shift();\n }\n }\n\n private processMessage(port: Runtime.Port, message: any) {\n const action = message.action;\n const actionHandlers = this.handlers.get(action) || [];\n\n if (actionHandlers.length > 0) {\n this.logger.debug(\n `Found ${actionHandlers.length} handlers for action: ${action}`\n );\n actionHandlers.forEach((handler) => handler(message));\n } else {\n this.logger.debug(`No handlers for message with action: ${action}`);\n }\n }\n\n public post(\n port: Runtime.Port,\n message: Message<any>,\n target?: BrowserLocation\n ) {\n this.logger.debug(`Sending message`, {\n action: message.action,\n target,\n hasPayload: !!message.payload,\n });\n\n try {\n if (target) {\n message.target = target;\n }\n port.postMessage(message);\n } catch (error) {\n throw new PorterError(\n PorterErrorType.MESSAGE_FAILED,\n 'Failed to post message',\n { originalError: error, message, target }\n );\n }\n }\n}\n", "import {\n AgentInfo,\n BrowserLocation,\n Message,\n MessageConfig,\n PorterContext,\n Unsubscribe,\n} from '../porter.model';\nimport {\n AgentConnectionManager,\n DisconnectCallback,\n ReconnectCallback,\n} from '../managers/AgentConnectionManager';\nimport { AgentMessageHandler } from '../managers/AgentMessageHandler';\nimport { Logger } from '../porter.utils';\n\nexport interface AgentAPI {\n type: 'agent';\n post: (message: Message<any>, target?: BrowserLocation) => void;\n onMessage: (config: MessageConfig) => void;\n on: (config: MessageConfig) => void;\n getAgentInfo: () => AgentInfo | null;\n onDisconnect: (callback: DisconnectCallback) => Unsubscribe;\n onReconnect: (callback: ReconnectCallback) => Unsubscribe;\n}\n\nexport interface PorterAgentOptions {\n agentContext?: PorterContext;\n namespace?: string;\n debug?: boolean;\n}\n\nexport class PorterAgent {\n private static instance: PorterAgent | null = null;\n private readonly connectionManager: AgentConnectionManager;\n private readonly messageHandler: AgentMessageHandler;\n private readonly logger: Logger;\n\n private constructor(options: PorterAgentOptions = {}) {\n const namespace = options.namespace ?? 'porter';\n const context = options.agentContext ?? this.determineContext();\n\n if (options.debug !== undefined) {\n Logger.configure({ enabled: options.debug });\n }\n\n this.logger = Logger.getLogger('Agent');\n\n this.connectionManager = new AgentConnectionManager(namespace, this.logger);\n this.messageHandler = new AgentMessageHandler(this.logger);\n\n // Listen for reconnection events to re-wire port listeners\n this.connectionManager.onReconnect((info) => {\n this.logger.info('Reconnected, re-wiring port listeners', { info });\n this.setupPortListeners();\n });\n\n this.logger.info('Initializing with options: ', { options, context });\n this.initializeConnection();\n }\n\n public static getInstance(options: PorterAgentOptions = {}): PorterAgent {\n if (\n !PorterAgent.instance ||\n PorterAgent.instance.connectionManager.getNamespace() !==\n options.namespace\n ) {\n PorterAgent.instance = new PorterAgent(options);\n } else if (options.debug !== undefined) {\n Logger.configure({ enabled: options.debug });\n }\n return PorterAgent.instance;\n }\n\n private async initializeConnection(): Promise<void> {\n await this.connectionManager.initializeConnection();\n this.setupPortListeners();\n }\n\n /**\n * Set up message and disconnect listeners on the current port.\n * Called after initial connection and after each reconnection.\n */\n private setupPortListeners(): void {\n const port = this.connectionManager.getPort();\n if (port) {\n this.logger.debug('Setting up port listeners');\n port.onMessage.addListener((message: any) =>\n this.messageHandler.handleMessage(port, message)\n );\n port.onDisconnect.addListener((p) =>\n this.connectionManager.handleDisconnect(p)\n );\n } else {\n this.logger.warn('Cannot setup port listeners: no port available');\n }\n }\n\n public onMessage(config: MessageConfig) {\n this.messageHandler.onMessage(config);\n const port = this.connectionManager.getPort();\n port?.postMessage({ action: 'porter-messages-established' });\n }\n\n public on(config: MessageConfig) {\n this.messageHandler.on(config);\n const port = this.connectionManager.getPort();\n port?.postMessage({ action: 'porter-messages-established' });\n }\n\n public post(message: Message<any>, target?: BrowserLocation) {\n const port = this.connectionManager.getPort();\n this.logger.debug('Posting message', { message, target, port });\n\n if (port) {\n try {\n this.messageHandler.post(port, message, target);\n } catch (error) {\n this.logger.error('Failed to post message, queueing for retry', {\n error,\n });\n this.connectionManager.queueMessage(message, target);\n }\n } else {\n this.logger.debug('No port found, queueing message', { message, target });\n this.connectionManager.queueMessage(message, target);\n }\n }\n\n private determineContext(): PorterContext {\n if (!window.location.protocol.includes('extension')) {\n return PorterContext.ContentScript;\n }\n return PorterContext.Extension;\n }\n\n public getAgentInfo(): AgentInfo | null {\n return this.connectionManager.getAgentInfo() || null;\n }\n\n /**\n * Register a callback to be called when the connection to the service worker is lost\n * @returns Unsubscribe function\n */\n public onDisconnect(callback: DisconnectCallback): Unsubscribe {\n return this.connectionManager.onDisconnect(callback);\n }\n\n /**\n * Register a callback to be called when reconnection to the service worker succeeds\n * @returns Unsubscribe function\n */\n public onReconnect(callback: ReconnectCallback): Unsubscribe {\n return this.connectionManager.onReconnect(callback);\n }\n}\n\nexport function connect(options?: PorterAgentOptions): AgentAPI {\n const porterInstance = PorterAgent.getInstance(options);\n return {\n type: 'agent',\n post: porterInstance.post.bind(porterInstance),\n onMessage: porterInstance.onMessage.bind(porterInstance),\n on: porterInstance.on.bind(porterInstance),\n getAgentInfo: porterInstance.getAgentInfo.bind(porterInstance),\n onDisconnect: porterInstance.onDisconnect.bind(porterInstance),\n onReconnect: porterInstance.onReconnect.bind(porterInstance),\n };\n}\n", "import { useState, useEffect, useCallback, useRef, useMemo } from 'react';\nimport { connect, AgentAPI } from '../core/PorterAgent';\nimport {\n AgentInfo,\n Message,\n MessageConfig,\n PorterContext,\n Unsubscribe,\n} from '../porter.model';\n\ninterface UsePorterResult {\n post: (message: Message<any>) => void;\n on: (handlers: MessageConfig) => void;\n isConnected: boolean;\n isReconnecting: boolean;\n error: Error | null;\n agentInfo: AgentInfo | null;\n}\n\nexport function usePorter(options?: {\n agentContext?: PorterContext;\n namespace?: string;\n debug?: boolean;\n onDisconnect?: () => void;\n onReconnect?: (info: AgentInfo) => void;\n}): UsePorterResult {\n const [isConnected, setIsConnected] = useState<boolean>(false);\n const [isReconnecting, setIsReconnecting] = useState<boolean>(false);\n const [error, setError] = useState<Error | null>(null);\n const [agentInfo, setAgentInfo] = useState<AgentInfo | null>(null);\n const postRef = useRef<((message: Message<any>) => void) | null>(null);\n const onRef = useRef<((handlers: MessageConfig) => void) | null>(null);\n const getAgentInfoRef = useRef<(() => AgentInfo | null) | null>(null);\n const unsubscribeRefs = useRef<Unsubscribe[]>([]);\n\n const memoizedOptions = useMemo(\n () => ({\n agentContext: options?.agentContext,\n namespace: options?.namespace,\n debug: options?.debug,\n }),\n [options?.agentContext, options?.namespace, options?.debug]\n );\n\n // Store callbacks in refs to avoid re-running effect when they change\n const onDisconnectRef = useRef(options?.onDisconnect);\n const onReconnectRef = useRef(options?.onReconnect);\n onDisconnectRef.current = options?.onDisconnect;\n onReconnectRef.current = options?.onReconnect;\n\n useEffect(() => {\n let isMounted = true;\n\n const initializePorter = async () => {\n try {\n const { post, on, getAgentInfo, onDisconnect, onReconnect } =\n connect(memoizedOptions);\n\n if (isMounted) {\n postRef.current = post;\n onRef.current = on;\n getAgentInfoRef.current = getAgentInfo;\n setIsConnected(true);\n setIsReconnecting(false);\n setError(null);\n\n // Set up disconnect handler\n const unsubDisconnect = onDisconnect(() => {\n if (isMounted) {\n setIsConnected(false);\n setIsReconnecting(true);\n setAgentInfo(null);\n onDisconnectRef.current?.();\n }\n });\n unsubscribeRefs.current.push(unsubDisconnect);\n\n // Set up reconnect handler\n const unsubReconnect = onReconnect((info: AgentInfo) => {\n if (isMounted) {\n setIsConnected(true);\n setIsReconnecting(false);\n setAgentInfo(info);\n onReconnectRef.current?.(info);\n }\n });\n unsubscribeRefs.current.push(unsubReconnect);\n\n // Set up internal porter-handshake handler\n on({\n 'porter-handshake': (message: Message<any>) => {\n if (isMounted) {\n setAgentInfo(message.payload.info);\n }\n },\n });\n }\n } catch (err) {\n if (isMounted) {\n console.error('[PORTER] initializePorter error ', err);\n setError(\n err instanceof Error\n ? err\n : new Error('Failed to connect to Porter')\n );\n setIsConnected(false);\n setIsReconnecting(false);\n }\n }\n };\n\n initializePorter();\n\n return () => {\n isMounted = false;\n // Clean up all subscriptions\n unsubscribeRefs.current.forEach((unsub) => unsub());\n unsubscribeRefs.current = [];\n };\n }, [memoizedOptions]);\n\n const post = useCallback((message: Message<any>) => {\n if (postRef.current) {\n try {\n postRef.current(message);\n } catch (err) {\n setError(\n err instanceof Error ? err : new Error('Failed to send message')\n );\n }\n } else {\n setError(new Error('Porter is not connected'));\n }\n }, []);\n\n const on = useCallback((handlers: MessageConfig) => {\n if (onRef.current) {\n try {\n onRef.current(handlers);\n } catch (err) {\n setError(\n err instanceof Error\n ? err\n : new Error('Failed to set message handlers')\n );\n }\n } else {\n setError(new Error('Porter is not connected'));\n }\n }, []);\n\n return { post, on, isConnected, isReconnecting, error, agentInfo };\n}\n", "/**\n * Crann Error Classes\n *\n * Custom error types for better debugging and error handling.\n */\n\n/**\n * Base error class for all Crann errors.\n */\nexport class CrannError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"CrannError\";\n // Maintains proper stack trace in V8 environments\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Thrown when there's an issue with the config schema.\n */\nexport class ConfigError extends CrannError {\n constructor(message: string, public readonly field?: string) {\n super(field ? `Config error in '${field}': ${message}` : `Config error: ${message}`);\n this.name = \"ConfigError\";\n }\n}\n\n/**\n * Thrown when a store or agent has been destroyed/disconnected.\n */\nexport class LifecycleError extends CrannError {\n constructor(\n public readonly storeName: string,\n public readonly entity: \"store\" | \"agent\"\n ) {\n super(\n `${entity === \"store\" ? \"Store\" : \"Agent\"} \"${storeName}\" has been ${\n entity === \"store\" ? \"destroyed\" : \"disconnected\"\n } and cannot be used.`\n );\n this.name = \"LifecycleError\";\n }\n}\n\n/**\n * Thrown when there's a connection issue.\n */\nexport class ConnectionError extends CrannError {\n constructor(\n message: string,\n public readonly storeName: string,\n public readonly reason?: string\n ) {\n super(`Connection error for store \"${storeName}\": ${message}`);\n this.name = \"ConnectionError\";\n }\n}\n\n/**\n * Thrown when an action execution fails.\n */\nexport class ActionError extends CrannError {\n constructor(\n public readonly actionName: string,\n public readonly storeName: string,\n public readonly reason: string\n ) {\n super(`Action \"${actionName}\" failed in store \"${storeName}\": ${reason}`);\n this.name = \"ActionError\";\n }\n}\n\n/**\n * Thrown when action validation fails.\n */\nexport class ValidationError extends CrannError {\n constructor(\n public readonly actionName: string,\n public readonly storeName: string,\n public readonly reason: string\n ) {\n super(\n `Validation failed for action \"${actionName}\" in store \"${storeName}\": ${reason}`\n );\n this.name = \"ValidationError\";\n }\n}\n\n/**\n * Thrown when there's a storage key collision.\n */\nexport class StorageCollisionError extends CrannError {\n constructor(public readonly storeName: string) {\n super(\n `Store name \"${storeName}\" is already in use. Each store must have a unique name. ` +\n `If you're trying to connect to an existing store, use connectStore() instead.`\n );\n this.name = \"StorageCollisionError\";\n }\n}\n\n", "/**\n * Crann v2 Agent\n *\n * Client-side connection to a Crann store.\n * Runs in content scripts, popup, sidepanel, etc.\n */\n\nimport { connect as connectPorter } from \"../transport\";\nimport type { AgentInfo } from \"../transport\";\nimport type {\n ConfigSchema,\n ValidatedConfig,\n DerivedState,\n DerivedActions,\n StateChanges,\n} from \"../store/types\";\nimport type {\n AgentOptions,\n AgentAPI,\n AgentConnectionInfo,\n StateSubscriber,\n} from \"./types\";\nimport { LifecycleError } from \"../errors\";\n\n// =============================================================================\n// Agent Class\n// =============================================================================\n\nexport class Agent<TConfig extends ConfigSchema> implements AgentAPI<TConfig> {\n private readonly config: ValidatedConfig<TConfig>;\n private readonly options: AgentOptions;\n private readonly porter: ReturnType<typeof connectPorter>;\n\n private _state: DerivedState<TConfig>;\n private _agentInfo: AgentInfo | null = null;\n private _isConnected = false;\n private _isDisconnected = false;\n\n private readonly subscribers: Set<StateSubscriber<TConfig>> = new Set();\n private readonly readyCallbacks: Set<(state: DerivedState<TConfig>) => void> = new Set();\n private readonly disconnectCallbacks: Set<() => void> = new Set();\n private readonly reconnectCallbacks: Set<(state: DerivedState<TConfig>) => void> = new Set();\n\n private readyPromise: Promise<DerivedState<TConfig>>;\n private readyResolve!: (state: DerivedState<TConfig>) => void;\n\n private readonly actionsProxy: DerivedActions<TConfig>;\n\n constructor(config: ValidatedConfig<TConfig>, options: AgentOptions = {}) {\n this.config = config;\n this.options = options;\n\n // Initialize state with defaults\n this._state = this.buildDefaultState();\n\n // Create ready promise\n this.readyPromise = new Promise((resolve) => {\n this.readyResolve = resolve;\n });\n\n // Connect to store via porter (using store name as namespace)\n this.porter = connectPorter({\n namespace: config.name,\n debug: options.debug ?? false,\n });\n\n // Set up message handlers\n this.setupMessageHandlers();\n\n // Create typed actions proxy\n this.actionsProxy = this.createActionsProxy();\n }\n\n // ===========================================================================\n // Public API - Connection\n // ===========================================================================\n\n ready(): Promise<DerivedState<TConfig>> {\n this.assertNotDisconnected();\n return this.readyPromise;\n }\n\n onReady(callback: (state: DerivedState<TConfig>) => void): () => void {\n this.assertNotDisconnected();\n \n // If already connected, call immediately\n if (this._isConnected) {\n setTimeout(() => callback(this._state), 0);\n }\n \n this.readyCallbacks.add(callback);\n return () => {\n this.readyCallbacks.delete(callback);\n };\n }\n\n // ===========================================================================\n // Public API - State\n // ===========================================================================\n\n getState(): DerivedState<TConfig> {\n this.assertNotDisconnected();\n return { ...this._state };\n }\n\n get state(): DerivedState<TConfig> {\n return this.getState();\n }\n\n async setState(state: Partial<DerivedState<TConfig>>): Promise<void> {\n this.assertNotDisconnected();\n \n // Optimistically update local state\n this._state = { ...this._state, ...state };\n \n // Send to store\n this.porter.post({\n action: \"setState\",\n payload: { state },\n });\n }\n\n // ===========================================================================\n // Public API - Subscriptions\n // ===========================================================================\n\n subscribe(\n callbackOrKeys: ((changes: StateChanges<TConfig>, state: DerivedState<TConfig>) => void) | Array<keyof DerivedState<TConfig>>,\n maybeCallback?: (changes: StateChanges<TConfig>, state: DerivedState<TConfig>) => void\n ): () => void {\n this.assertNotDisconnected();\n\n let keys: Array<keyof DerivedState<TConfig>> | undefined;\n let callback: (changes: StateChanges<TConfig>, state: DerivedState<TConfig>) => void;\n\n if (typeof callbackOrKeys === \"function\") {\n callback = callbackOrKeys;\n } else {\n keys = callbackOrKeys;\n callback = maybeCallback!;\n }\n\n const subscriber: StateSubscriber<TConfig> = { keys, callback };\n this.subscribers.add(subscriber);\n\n return () => {\n this.subscribers.delete(subscriber);\n };\n }\n\n // ===========================================================================\n // Public API - Actions\n // ===========================================================================\n\n get actions(): DerivedActions<TConfig> {\n this.assertNotDisconnected();\n return this.actionsProxy;\n }\n\n // ===========================================================================\n // Public API - Agent Info\n // ===========================================================================\n\n getInfo(): AgentConnectionInfo | null {\n if (!this._agentInfo) return null;\n \n return {\n id: this._agentInfo.id,\n tabId: this._agentInfo.location.tabId,\n frameId: this._agentInfo.location.frameId,\n context: this._agentInfo.location.context,\n };\n }\n\n // ===========================================================================\n // Public API - Lifecycle\n // ===========================================================================\n\n onDisconnect(callback: () => void): () => void {\n this.assertNotDisconnected();\n this.disconnectCallbacks.add(callback);\n return () => {\n this.disconnectCallbacks.delete(callback);\n };\n }\n\n onReconnect(callback: (state: DerivedState<TConfig>) => void): () => void {\n this.assertNotDisconnected();\n this.reconnectCallbacks.add(callback);\n return () => {\n this.reconnectCallbacks.delete(callback);\n };\n }\n\n disconnect(): void {\n if (this._isDisconnected) return;\n\n this._isDisconnected = true;\n this._isConnected = false;\n\n // Clear all callbacks\n this.subscribers.clear();\n this.readyCallbacks.clear();\n this.disconnectCallbacks.clear();\n this.reconnectCallbacks.clear();\n\n // Note: Porter doesn't have a disconnect method, but we've cleaned up our state\n }\n\n // ===========================================================================\n // Private - Message Handling\n // ===========================================================================\n\n private setupMessageHandlers(): void {\n // Handle initial state from store\n this.porter.on({\n initialState: (message) => {\n const { state, info } = message.payload;\n \n this._state = state;\n this._agentInfo = info;\n this._isConnected = true;\n\n // Resolve ready promise\n this.readyResolve(this._state);\n\n // Notify ready callbacks\n this.readyCallbacks.forEach((cb) => {\n try {\n cb(this._state);\n } catch (e) {\n console.error(\"[Crann Agent] Error in onReady callback:\", e);\n }\n });\n\n // Notify initial subscribers\n this.notifySubscribers(state);\n },\n\n stateUpdate: (message) => {\n const { state: changes } = message.payload;\n this._state = { ...this._state, ...changes };\n this.notifySubscribers(changes);\n },\n\n rpcResult: (message) => {\n // RPC results are handled by the action proxy's pending promises\n // This is handled in createActionsProxy\n },\n });\n\n // Handle disconnect/reconnect from porter\n this.porter.onDisconnect(() => {\n this._isConnected = false;\n this.disconnectCallbacks.forEach((cb) => {\n try {\n cb();\n } catch (e) {\n console.error(\"[Crann Agent] Error in onDisconnect callback:\", e);\n }\n });\n });\n\n this.porter.onReconnect((info: AgentInfo) => {\n this._agentInfo = info;\n this._isConnected = true;\n this.reconnectCallbacks.forEach((cb) => {\n try {\n cb(this._state);\n } catch (e) {\n console.error(\"[Crann Agent] Error in onReconnect callback:\", e);\n }\n });\n });\n }\n\n private notifySubscribers(changes: StateChanges<TConfig>): void {\n this.subscribers.forEach((subscriber) => {\n // If subscriber has specific keys, check if any changed\n if (subscriber.keys) {\n const hasRelevantChange = subscriber.keys.some(\n (key) => key in changes\n );\n if (!hasRelevantChange) return;\n }\n\n try {\n subscriber.callback(changes, this._state);\n } catch (e) {\n console.error(\"[Crann Agent] Error in subscriber callback:\", e);\n }\n });\n }\n\n // ===========================================================================\n // Private - Actions Proxy\n // ===========================================================================\n\n private createActionsProxy(): DerivedActions<TConfig> {\n const pendingCalls = new Map<string, { resolve: (v: any) => void; reject: (e: any) => void }>();\n let callId = 0;\n\n // Listen for RPC results\n this.porter.on({\n rpcResult: (message) => {\n const { callId: id, result, error, success } = message.payload;\n const pending = pendingCalls.get(id);\n if (pending) {\n pendingCalls.delete(id);\n if (success) {\n pending.resolve(result);\n } else {\n pending.reject(new Error(error));\n }\n }\n },\n });\n\n return new Proxy({} as DerivedActions<TConfig>, {\n get: (_target, actionName: string) => {\n return async (...args: unknown[]) => {\n const id = `${callId++}`;\n \n return new Promise((resolve, reject) => {\n pendingCalls.set(id, { resolve, reject });\n \n this.porter.post({\n action: \"rpc\",\n payload: {\n callId: id,\n actionName,\n args,\n },\n });\n });\n };\n },\n });\n }\n\n // ===========================================================================\n // Private - Utilities\n // ===========================================================================\n\n private buildDefaultState(): DerivedState<TConfig> {\n const state: Record<string, unknown> = {};\n\n for (const [key, item] of Object.entries(this.config)) {\n if (key === \"name\" || key === \"version\" || key === \"actions\") continue;\n if (typeof item === \"object\" && item !== null && \"default\" in item) {\n state[key] = item.default;\n }\n }\n\n return state as DerivedState<TConfig>;\n }\n\n private assertNotDisconnected(): void {\n if (this._isDisconnected) {\n throw new LifecycleError(this.config.name, \"agent\");\n }\n }\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\n/**\n * Connect to a Crann store from a client context.\n *\n * This should be called in content scripts, popup, sidepanel, or other\n * extension contexts that need to access the store. Uses the config's\n * `name` to find and connect to the matching store in the service worker.\n *\n * @param config - Validated config from createConfig() (must match store's config)\n * @param options - Optional connection options\n * @param options.debug - Enable debug logging\n * @returns An Agent instance for interacting with the store\n *\n * @example\n * // content.ts\n * import { createConfig, connectStore } from 'crann';\n *\n * const config = createConfig({\n * name: 'myFeature',\n * count: { default: 0, persist: 'local' },\n * });\n *\n * const agent = connectStore(config);\n *\n * // Wait for connection and initial state\n * const state = await agent.ready();\n * console.log('Initial count:', state.count);\n *\n * // Subscribe to changes\n * agent.subscribe((changes, state) => {\n * console.log('State changed:', changes);\n * });\n *\n * // Call actions\n * await agent.actions.increment();\n */\nexport function connectStore<TConfig extends ConfigSchema>(\n config: ValidatedConfig<TConfig>,\n options?: AgentOptions\n): AgentAPI<TConfig> {\n return new Agent(config, options);\n}\n\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["react_exports", "__export", "createCrannHooks", "__toCommonJS", "import_react", "import_webextension_polyfill", "PorterContext", "PorterError", "type", "message", "details", "isServiceWorker", "_Logger", "context", "_a", "_b", "_c", "options", "message", "args", "Logger", "import_webextension_polyfill", "import_uuid", "AgentManager", "logger", "port", "context", "_a", "_b", "connectionSource", "determinedContext", "tabId", "frameId", "tabAgentsInfo", "info", "agentId", "uuidv4", "agentInfo", "message", "agent", "location", "infoEntry", "key", "value", "hasContext", "hasTabId", "hasFrameId", "id", "p", "allAgents", "allAgentsInfo", "event", "handler", "handlers", "args", "_c", "_d", "_e", "_f", "_g", "sender", "manifest", "browser", "sidePanel", "optionsPage", "popupPage", "devtoolsPage", "newTabOverride", "bookmarksOverride", "historyOverride", "pageMatchers", "filename", "pageType", "pageMatcher", "ConnectionManager", "agentOperations", "namespace", "logger", "port", "PorterError", "e", "error", "message", "connectionId", "agentId", "agent", "porterError", "MessageHandler", "agentOperations", "logger", "message", "agent", "_a", "agentInfo", "target", "resolve", "reject", "timeoutId", "error", "isBrowserLocation", "isPorterContext", "errorMessage", "tabId", "agents", "PorterError", "location", "context", "port", "agentId", "config", "listener", "messageListener", "event", "handler", "info", "arg", "messageEvent", "handlerCount", "PorterContext", "_PorterSource", "namespace", "options", "Logger", "AgentManager", "MessageHandler", "ConnectionManager", "isServiceWorker", "PorterError", "message", "metadata", "agent", "browser", "target", "config", "listener", "key", "_a", "agentId", "location", "PorterSource", "import_webextension_polyfill", "MessageQueue", "logger", "message", "target", "oldCount", "messages", "now", "item", "AgentConnectionManager", "namespace", "logger", "MessageQueue", "callback", "error", "info", "_a", "portName", "browser", "handshakePromise", "resolve", "reject", "timeout", "PorterError", "onMessage", "message", "_b", "messages", "target", "messageToSend", "port", "AgentMessageHandler", "logger", "port", "message", "config", "action", "handler", "item", "actionHandlers", "target", "error", "PorterError", "_PorterAgent", "options", "namespace", "context", "Logger", "AgentConnectionManager", "AgentMessageHandler", "info", "port", "message", "p", "config", "target", "error", "callback", "PorterAgent", "connect", "porterInstance", "import_react", "CrannError", "message", "LifecycleError", "CrannError", "storeName", "entity", "Agent", "config", "options", "resolve", "connect", "callback", "state", "callbackOrKeys", "maybeCallback", "keys", "subscriber", "message", "info", "cb", "e", "changes", "key", "pendingCalls", "callId", "id", "result", "error", "success", "pending", "_target", "actionName", "args", "reject", "item", "LifecycleError", "connectStore", "import_jsx_runtime", "createCrannHooks", "config", "options", "moduleAgent", "AgentContext", "getAgent", "connectStore", "useAgent", "contextAgent", "agent", "setAgent", "useCrannReady", "isReady", "setIsReady", "unsubReady", "unsubDisconnect", "unsubReconnect", "useCrannState", "selectorOrKey", "isFunction", "selector", "state", "selectedValue", "setSelectedValue", "defaultState", "buildDefaultState", "prevSelectedRef", "currentState", "newSelected", "shallowEqual", "changes", "key", "setValue", "value", "useCrannActions", "agentRef", "actionsRef", "_target", "actionName", "args", "currentAgent", "children", "item", "a", "b", "keysA", "keysB"]
|
|
7
|
+
}
|
package/dist/esm/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
var ne=Object.defineProperty;var ie=Object.getOwnPropertyDescriptor;var z=(r,e,t,i)=>{for(var n=i>1?void 0:i?ie(e,t):e,o=r.length-1,c;o>=0;o--)(c=r[o])&&(n=(i?c(e,t,n):c(n))||n);return i&&n&&ne(e,t,n),n};import j from"webextension-polyfill";var U={Instance:"instance",Service:"service"},oe={Session:"session",Local:"local",None:"none"},M=r=>!("handler"in r),N=r=>"handler"in r;import{source as se}from"porter-source";function F(r,e){if(r===e)return!0;if(r==null||typeof r!="object"||e==null||typeof e!="object")return!1;let t=Object.keys(r),i=Object.keys(e);if(t.length!==i.length)return!1;t.sort(),i.sort();for(let n=0;n<t.length;n++){let o=t[n];if(o!==i[n]||!F(r[o],e[o]))return!1}return!0}var L=class L{static setDebug(e){L._debug=e}static isDebugEnabled(){return L._debug}};L._debug=!1;var K=L;function J(){return K.isDebugEnabled()}function H(r,e,t){let i=t.value;return t.value=function(...n){var o;if(J()){let c=new Error().stack,g=(o=((c==null?void 0:c.split(`
|
|
2
|
-
`))||[])[3])==null?void 0:o.match(/at\s+(\S+)\s+/),a=g?g[1]:"unknown",f=n.length>1?n[1]:void 0,l={source:a,timestamp:Date.now(),instanceKey:n[0],changes:f};console.log("Crann State Change:",l)}return i.apply(this,n)},t}import{source as re}from"porter-source";var y=class y{constructor(e){this.tag=null;this.context=e}static setDebug(e){y.debug=e}static setPrefix(e){y.prefix=e}setTag(e){this.tag=e}withTag(e){let t=new y(this.context);return t.setTag(e),t}getFullContext(){return this.tag?`${this.context}:${this.tag}`:this.context}createLogMethods(){let e=this.getFullContext(),t=`%c${y.prefix}%c [%c${e}%c]`,i="color: #3fcbff; font-weight: bold",n="color: #d58cff; font-weight: bold",o="";return y.debug?{debug:console.log.bind(console,t,i,o,n,o),log:console.log.bind(console,t,i,o,n,o),info:console.info.bind(console,t,i,o,n,o),warn:console.warn.bind(console,t,i,o,n,o),error:console.error.bind(console,t,i,o,n,o)}:{debug:y.noOp,log:y.noOp,info:y.noOp,warn:y.noOp,error:y.noOp}}get debug(){return this.createLogMethods().debug}get log(){return this.createLogMethods().log}get info(){return this.createLogMethods().info}get warn(){return this.createLogMethods().warn}get error(){return this.createLogMethods().error}static forContext(e,t){let i=new y(e);return t&&i.setTag(t),i}};y.debug=!1,y.prefix="CrannLogger",y.noOp=function(){};var b=y;function m(r,e={terse:!0}){let i=(n=>{let o=String(n);return o.length>=4?o.slice(-4):o.padStart(4,"0")})(r.location.tabId);return e!=null&&e.terse?`${i}:${r.location.frameId}`:`${r.location.context}:${i}:${r.location.frameId}`}function Q(r,e,t,i,n){var f,l;let o=new Map,c=new Map,C=(f=r.context)!=null&&f.isServiceWorker?"Core":"Agent",g=b.forContext(`${C}:RPC`);return(l=r.context)!=null&&l.agentInfo&&g.setTag(m(r.context.agentInfo)),r.addEventListener("message",S=>{g.debug("Message received:",S);let[p,h]=S.data;if("call"in h&&"args"in h.call){g.debug("Processing call message:",h);let v=h.call,{id:d,args:I,target:T}=v,E=t[d];if(!E){r.postMessage([p,{error:{id:d,error:"Action not found",target:T}}]);return}try{if(E.validate&&E.validate(...I),!T){r.postMessage([p,{error:{id:d,error:"No target provided for action call",target:T}}]);return}let w=e();g.debug("Executing action with most current state:",w),Promise.resolve(E.handler(w,i,T,...I)).then(k=>{g.debug("Action handler result:",{result:k,target:T}),r.postMessage([p,{result:{id:d,result:k,target:T}}])},k=>{r.postMessage([p,{error:{id:d,error:k.message,target:T}}])})}catch(w){w instanceof Error?r.postMessage([p,{error:{id:d,error:w.message,target:T}}]):r.postMessage([p,{error:{id:d,error:"Unknown error occurred",target:T}}])}}else if("result"in h){let v=h.result,d=o.get(p);d&&(d(v.result),o.delete(p))}else if("error"in h){let v=h.error,d=o.get(p);d&&(d(Promise.reject(new Error(v.error))),o.delete(p))}else if("release"in h){let v=h.release,d=c.get(v.id);d&&(d.clear(),c.delete(v.id))}}),new Proxy({},{get(S,p){return(...h)=>{let v=Math.random();return new Promise((d,I)=>{o.set(v,T=>{T instanceof Promise?T.then(d,I):d(T)}),r.postMessage([v,{call:{id:p,args:h}}])})}}})}function $(r,e,t,i){let n=t||re("crann"),o=n.type!=="agent",c=b.forContext(o?"Core:RPC":"Agent:RPC"),C={postMessage:(g,a)=>{if(o){c.debug("Posting message from service worker:",{message:g,transferables:a});let[,f]=g,l=ae(f);if(!l){c.warn("No target specified for RPC response in service worker");return}n.post({action:"rpc",payload:{message:g,transferables:a||[]}},l)}else{let f=n.getAgentInfo();if(!f){c.warn("No agent info found for posting message",{agentInfo:f});return}let l=m(f),[,S]=g;"call"in S&&(S.call.target=f==null?void 0:f.location),c.withTag(l).debug("Sending RPC message from agent:",{rpcPayload:S,message:g}),n.post({action:"rpc",payload:{message:g,transferables:a||[]}})}},addEventListener:(g,a)=>{n.on({rpc:(f,l)=>{try{if(!l)c.debug("RPC message received:",{message:f,event:g});else{let d=m(l);c.withTag(d).debug("RPC message received:",{message:f,event:g})}let{payload:S}=f,{message:p,transferables:h=[]}=S,v=new MessageEvent("message",{data:p,ports:h.filter(d=>d instanceof MessagePort)||[]});a(v)}catch(S){c.error("Failed to parse RPC message payload:",S)}}})},removeEventListener:()=>{},context:{isServiceWorker:o,agentInfo:o?void 0:n.getAgentInfo()}};return Q(C,r,e,i)}function ae(r){if("result"in r)return r.result.target;if("error"in r)return r.error.target;if("call"in r)return r.call.target;if("release"in r)return r.release.target}var D=class D{constructor(e,t){this.config=e;this.instances=new Map;this.stateChangeListeners=[];this.instanceReadyListeners=[];this.storagePrefix="crann_";this.porter=se("crann",{debug:!1});var g;t!=null&&t.debug&&(K.setDebug(!0),b.setDebug(!0)),this.storagePrefix=(g=t==null?void 0:t.storagePrefix)!=null?g:this.storagePrefix,this.logger=b.forContext("Core"),this.logger.log("Constructing Crann with new logger"),this.defaultInstanceState=this.initializeInstanceDefault(),this.defaultServiceState=this.serviceState=this.initializeServiceDefault(),this.hydrate(),this.logger.log("Crann constructed, setting initial message handlers"),this.porter.on({setState:(a,f)=>{if(!f){this.logger.warn("setState message heard from unknown agent");return}let l=m(f);this.logger.withTag(l).log("Setting state:",a),this.set(a.payload.state,f.id)}});let i=new Set;this.porter.onMessagesSet(a=>{if(!a){this.logger.error("Messages set but no agent info.",{info:a});return}let f=m(a);if(this.logger.withTag(f).log("onMessagesSet received for agent:",{id:a.id,context:a.location.context,tabId:a.location.tabId,frameId:a.location.frameId,alreadyInitialized:i.has(a.id)}),i.has(a.id)){this.logger.withTag(f).log("Already sent initialState to agent, skipping:",a.id);return}i.add(a.id),this.logger.withTag(f).log("Messages set received. Sending initial state.",{info:a});let l=this.get(a.id);this.porter.post({action:"initialState",payload:{state:l,info:a}},a.location),this.notifyInstanceReady(a.id,a)}),this.porter.onConnect(a=>{if(!a){this.logger.error("Agent connected but no agent info.",{info:a});return}let f=m(a);this.logger.withTag(f).log("Agent connected",{info:a}),this.addInstance(a.id,f),this.porter.onDisconnect(l=>{this.logger.withTag(m(l)).log("Agent disconnect heard. Connection type, context and location:",{info:l}),this.removeInstance(l.id)})});let o=(a,f)=>f!==void 0?this.set(a,f):this.set(a),c=this.extractActions(e),C=()=>{let a=this.get();return this.logger.log("State getter called, returning current state:",a),a};this.rpcEndpoint=$(C,c,this.porter,o)}static getInstance(e,t){return D.instance?t!=null&&t.debug&&b.forContext("Core").log("Instance requested and already existed, returning"):D.instance=new D(e,t),D.instance}async addInstance(e,t){if(this.instances.has(e))this.logger.withTag(t).log("Instance was already registered, ignoring request from key");else{this.logger.withTag(t).log("Adding instance from agent key");let i={...this.defaultInstanceState};this.instances.set(e,i)}}async removeInstance(e){this.instances.has(e)?(this.logger.withTag(e).log("Remove instance requested"),this.instances.delete(e)):this.logger.withTag(e).log("Remove instance requested but it did not exist!")}async setServiceState(e){this.logger.log("Request to set service state with update:",e),this.logger.log("Existing service state was ",this.serviceState);let t={...this.serviceState,...e};F(this.serviceState,t)?this.logger.log("New state seems to be the same as existing, skipping"):(this.logger.log("Confirmed new state was different than existing so proceeding to persist then notify all connected instances."),this.serviceState=t,await this.persist(e),this.notify(e))}async setInstanceState(e,t){this.logger.withTag(e).log("Request to update instance state, update:",t);let i=this.instances.get(e)||this.defaultInstanceState,n={...i,...t};F(i,n)?this.logger.withTag(e).log("Instance state update is not different, skipping update."):(this.logger.withTag(e).log("Instance state update is different, updating and notifying."),this.instances.set(e,n),this.notify(t,e))}async persist(e){this.logger.log("Persisting state");let t=!1;for(let i in e||this.serviceState){let o=this.config[i].persist||"none",c=e?e[i]:this.serviceState[i];switch(o){case"session":await j.storage.session.set({[this.storagePrefix+i]:c}),t=!0;break;case"local":await j.storage.local.set({[this.storagePrefix+i]:c}),t=!0;break;default:break}}t?this.logger.log("State was persisted"):this.logger.log("Nothing to persist")}async clear(){this.logger.log("Clearing state"),this.serviceState=this.defaultServiceState,this.instances.forEach((e,t)=>{this.instances.set(t,this.defaultInstanceState)}),await this.persist(),this.notify({})}subscribe(e){this.logger.log("Subscribing to state"),this.stateChangeListeners.push(e)}notify(e,t){let i=t?this.porter.getAgentById(t):void 0,n=t?this.get(t):this.get();this.stateChangeListeners.length>0&&(this.logger.log("Notifying state change listeners in source"),this.stateChangeListeners.forEach(o=>{o(n,e,i==null?void 0:i.info)})),t&&(i!=null&&i.info.location)?(this.logger.withTag(t).log("Notifying of state change."),this.porter.post({action:"stateUpdate",payload:{state:e}},i.info.location)):(this.logger.log("Notifying everyone"),this.instances.forEach((o,c)=>{this.porter.post({action:"stateUpdate",payload:{state:e}},c)}))}get(e){return e?{...this.serviceState,...this.instances.get(e)}:{...this.serviceState}}findInstance(e){let t=this.porter.getAgentByLocation(e);if(!t)return this.logger.log("Could not find agent for location:",{location:e}),null;for(let[i,n]of this.instances)if(i===t.info.id)return this.logger.log("Found instance for key:",i),i;return this.logger.log("Could not find instance for context and location:",{location:e}),null}queryAgents(e){return this.porter.queryAgents(e)}async set(e,t){let i={},n={};for(let o in e){let c=this.config[o];if(ge(c)){if(c.partition==="instance"){let C=o,g=e;i[C]=g[C]}else if(!c.partition||c.partition===U.Service){let C=o,g=e;n[C]=g[C]}}}t&&Object.keys(i).length>0&&(this.logger.withTag(t).log("Setting instance state:",i),this.setInstanceState(t,i)),Object.keys(n).length>0&&(this.logger.log("Setting service state:",n),this.setServiceState(n))}async hydrate(){this.logger.log("Hydrating state from storage.");let e=await j.storage.local.get(null),t=await j.storage.session.get(null),i={...e,...t};this.logger.log("Storage data is:",{local:e,session:t,combined:i});let n={},o=!1;for(let c in i){let C=this.removePrefix(c);if(this.logger.log(`Checking storage key ${c} -> ${C}`),this.config.hasOwnProperty(C)){let g=i[c];this.logger.log(`Found storage value for ${C}:`,g),n[C]=g,o=!0}}o?this.logger.log("Hydrated some items."):this.logger.log("No items found in storage."),this.serviceState={...this.defaultServiceState,...n}}removePrefix(e){return e.startsWith(this.storagePrefix)?e.replace(this.storagePrefix,""):e}initializeInstanceDefault(){let e={};return Object.keys(this.config).forEach(t=>{let i=this.config[t];M(i)&&i.partition==="instance"&&(e[t]=i.default)}),e}initializeServiceDefault(){this.logger.log("Initializing service default state"),this.logger.log("Config is:",this.config);let e={};return Object.keys(this.config).forEach(t=>{let i=this.config[t];this.logger.log("Item is:",i),M(i)&&(!i.partition||i.partition===U.Service)&&(e[t]=i.default,this.logger.log("Setting service state for key:",t,"to",i.default))}),this.logger.log("Final service state is:",e),e}subscribeToInstanceReady(e){return this.logger.log("Subscribing to instance ready events"),this.instanceReadyListeners.push(e),this.instances.forEach((t,i)=>{let n=this.porter.getAgentById(i);n!=null&&n.info&&e(i,n.info)}),()=>{this.logger.log("Unsubscribing from instance ready events");let t=this.instanceReadyListeners.indexOf(e);t!==-1&&this.instanceReadyListeners.splice(t,1)}}notifyInstanceReady(e,t){if(this.instanceReadyListeners.length>0){let i=m(t);this.logger.withTag(i).log("Notifying instance ready listeners"),this.instanceReadyListeners.forEach(n=>{n(e,t)})}}extractActions(e){return Object.entries(e).filter(([t,i])=>N(i)).reduce((t,[i,n])=>{let o=n;return{...t,[i]:o}},{})}};D.instance=null,z([H],D.prototype,"setServiceState",1),z([H],D.prototype,"setInstanceState",1);var q=D;function ce(r,e){let t=q.getInstance(r,e);return{get:t.get.bind(t),set:t.set.bind(t),subscribe:t.subscribe.bind(t),onInstanceReady:t.subscribeToInstanceReady.bind(t),findInstance:t.findInstance.bind(t),queryAgents:t.queryAgents.bind(t),clear:t.clear.bind(t)}}function ge(r){return r&&typeof r=="object"&&"default"in r}import{connect as le}from"porter-source";var x={connected:!1},P=null,V=new Set,W=new Set;function G(r,e){let t=(e==null?void 0:e.debug)||!1,i=e==null?void 0:e.context;t&&b.setDebug(!0);let n=b.forContext("Agent"),o,c="unset",C=new Set;if(n.log("Initializing Crann Agent"+(i?` with context: ${i}`:"")),P&&x.connected)return n.log("We had an instance already and it's connected, returning"),n.log("Connect, calling onReady callback"),setTimeout(()=>{C.forEach(s=>s(x))},0),P;P&&!x.connected&&(n.log("We had an instance but it's disconnected, creating new connection"),P=null),n.log("No existing instance, creating a new one");let g=le({namespace:"crann",debug:!1});n.log("Porter connection created"),g.onDisconnect(()=>{n.log("Porter connection lost, updating connection status"),x={connected:!1},V.forEach(s=>{try{s()}catch(u){n.error("Error in disconnect callback:",u)}}),C.forEach(s=>{try{s(x)}catch(u){n.error("Error in onReady callback during disconnect:",u)}})}),g.onReconnect(s=>{n.log("Porter reconnected, updating connection status",s),x={connected:!0,agent:s},o=s,c=m(s),n.setTag(c),W.forEach(u=>{try{u(s)}catch(A){n.error("Error in reconnect callback:",A)}}),C.forEach(u=>{try{u(x)}catch(A){n.error("Error in onReady callback during reconnect:",A)}})});let a=Object.entries(r).filter(([s,u])=>N(u)).reduce((s,[u,A])=>{let R=A;return{...s,[u]:{type:"action",handler:R.handler,validate:R.validate}}},{}),f=$(()=>Z(r),a,g),l=!1;g.on({initialState:s=>{if(n.log("initialState received",{alreadyReceived:l,payload:s.payload}),l){n.log("Ignoring duplicate initialState message");return}l=!0,S=s.payload.state,o=s.payload.info,c=m(o),x={connected:!0,agent:o},n.setTag(c),n.log(`Initial state received and ${h.size} listeners notified`,{message:s}),C.forEach(u=>{n.log("Calling onReady callbacks"),u(x)}),h.forEach(u=>{u.callback(S)})},stateUpdate:s=>{p=s.payload.state,S={...S,...p},n.log("State updated:",{message:s,changes:p,_state:S}),p&&h.forEach(u=>{(u.keys===void 0||u.keys.some(R=>R in p))&&u.callback(p)})}}),n.log("Porter connected. Setting up state and listeners");let S=Z(r),p=null,h=new Set;n.log("Completed setup, returning instance");let v=()=>S,d=s=>{n.log("Calling post with setState",s),g.post({action:"setState",payload:{state:s}})},I=(s,u)=>{let A={keys:u,callback:s};return h.add(A),()=>{h.delete(A)}};return P={useCrann:s=>{let u=()=>v()[s],A=O=>d({[s]:O}),R=O=>{let Y=u();return I(ee=>{if(s in ee){let X=u(),te=v();O({current:X,previous:Y,state:te}),Y=X}},[s])};return[u(),A,R]},get:v,set:d,subscribe:I,getAgentInfo:()=>o,onReady:s=>(n.log("onReady callback added"),C.add(s),x.connected&&(n.log("calling onReady callback"),setTimeout(()=>{s(x)},0)),()=>C.delete(s)),callAction:async(s,...u)=>(n.log("Calling action",s,u),f[s](...u)),onDisconnect:s=>(n.log("onDisconnect callback added"),V.add(s),()=>{V.delete(s)}),onReconnect:s=>(n.log("onReconnect callback added"),W.add(s),()=>{W.delete(s)})},P}function fe(){return P!==null}function Z(r){let e={};return Object.keys(r).forEach(t=>{let i=r[t];M(i)&&(e[t]=i.default)}),e}import{useState as de,useEffect as _,useCallback as B,useMemo as ue,useRef as Ce}from"react";function pe(r){return function(t){let{useCrann:i,get:n,set:o,subscribe:c,callAction:C}=ue(()=>G(r),[t]),g=B(l=>{let[S,p]=de(n()[l]),h=Ce(S);_(()=>{h.current=S},[S]),_(()=>(p(n()[l]),c(I=>{l in I&&p(I[l])},[l])),[l]);let v=B(d=>{o({[l]:d})},[l]);return[S,v]},[n,o,c]),a=B(()=>n(),[n]),f=B(l=>{o(l)},[o]);return{useStateItem:g,getState:a,setState:f,useCrann:i,callAction:C}}}function Se(r){return r}export{q as Crann,U as Partition,oe as Persistence,G as connect,fe as connected,ce as create,Se as createConfig,pe as createCrannStateHook};
|
|
1
|
+
var Se=Object.defineProperty;var Ae=Object.getOwnPropertyDescriptor;var re=(i,e,t,n)=>{for(var o=n>1?void 0:n?Ae(e,t):e,r=i.length-1,s;r>=0;r--)(s=i[r])&&(o=(n?s(e,t,o):s(o))||o);return n&&o&&Se(e,t,o),o};import te from"webextension-polyfill";var z={Instance:"instance",Service:"service"},Te={Session:"session",Local:"local",None:"none"},U=i=>!("handler"in i),Q=i=>"handler"in i;import we from"webextension-polyfill";var B=(c=>(c.ContentScript="contentscript",c.Extension="extension",c.Popup="popup",c.Sidepanel="sidepanel",c.Devtools="devtools",c.Options="options",c.Unknown="unknown",c))(B||{});var C=class extends Error{constructor(t,n,o){super(n);this.type=t;this.details=o;this.name="PorterError"}};function he(){return typeof ServiceWorkerGlobalScope!="undefined"&&self instanceof ServiceWorkerGlobalScope}var y=class y{constructor(e){this.context=e}static getLevel(){var t,n,o;return((t=y.globalOptions)==null?void 0:t.level)!==void 0?y.globalOptions.level:typeof process!="undefined"&&(((n=process.env)==null?void 0:n.NODE_ENV)==="production"||((o=process.env)==null?void 0:o.PORTER_ENV)==="production")?1:4}static configure(e){y.globalOptions=e,e.level!==void 0&&(y.level=e.level),e.enabled!==void 0&&(y.enabled=e.enabled)}static getLogger(e){return this.instances.has(e)||this.instances.set(e,new y(e)),this.instances.get(e)}error(e,...t){y.enabled&&y.level>=0&&console.error(`[Porter:${this.context}] ${e}`,...t)}warn(e,...t){y.enabled&&y.level>=1&&console.warn(`[Porter:${this.context}] ${e}`,...t)}info(e,...t){y.enabled&&y.level>=2&&console.info(`[Porter:${this.context}] ${e}`,...t)}debug(e,...t){y.enabled&&y.level>=3&&console.debug(`[Porter:${this.context}] ${e}`,...t)}trace(e,...t){y.enabled&&y.level>=4&&console.trace(`[Porter:${this.context}] ${e}`,...t)}};y.level=y.getLevel(),y.enabled=!1,y.instances=new Map;var x=y;import Ie from"webextension-polyfill";import{v4 as Me}from"uuid";var V=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 u,p;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,r=n.tabId||-1,s=n.frameId||0;this.logger.debug("Determined context for new agent",{determinedContext:o,tabId:r,frameId:s});let c=Array.from(this.agentsInfo.values()).filter(f=>f.location.context===o&&f.location.tabId===r&&f.location.frameId===s);c.length>0&&this.logger.debug("Adding agent: Found existing similar agent.",{tabAgentsInfo:c});let g=((p=(u=this.getAgentByLocation({context:o,tabId:r,frameId:s}))==null?void 0:u.info)==null?void 0:p.id)||Me();this.logger.debug(`Adding agent with id: ${g}`),this.agents.set(g,e);let a={id:g,location:{context:o,tabId:r,frameId:s},createdAt:Date.now(),lastActiveAt:Date.now()};this.agentsInfo.set(g,a),this.logger.debug(`Constructed agent info: ${JSON.stringify(a)}`),e.onMessage.addListener(f=>this.emit("agentMessage",f,a));let l={port:e,info:a};return e.onDisconnect.addListener(()=>{this.emit("agentDisconnect",a),this.logger.debug("Agent disconnected, removing from manager. ",{agentInfo:a}),this.removeAgent(g)}),this.emit("agentSetup",l),this.logger.debug("Setup complete for adding agent. ",{agentInfo:a}),g}getAgentByLocation(e){let{context:t,tabId:n,frameId:o}=e,r=Array.from(this.agentsInfo.entries()).find(([a,l])=>l.location.context===t&&l.location.tabId===n&&l.location.frameId===o);if(r===void 0)return this.logger.error("No agent found for location. ",{location:e}),null;let s=r[0],c=this.agents.get(s),g=this.agentsInfo.get(s);return!c||!g?(this.logger.error("No agent found for location. ",{location:e}),null):{port:c,info:g}}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 r=e.context?o.location.context===e.context:!0,s=e.tabId?o.location.tabId===e.tabId:!0,c=e.frameId?o.location.frameId===e.frameId:!0;return r&&s&&c}).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 p,f,b,v,h,I,S;let t=e.sender;if(!t)return this.logger.error("Cannot add agent that did not have a sender"),null;let n=Ie.runtime.getManifest(),o=((p=n==null?void 0:n.side_panel)==null?void 0:p.default_path)||"",r=n.options_page||"",s=((f=n.action)==null?void 0:f.default_popup)||"",c=n.devtools_page||"",g=((b=n.chrome_url_overrides)==null?void 0:b.newtab)||"",a=((v=n.chrome_url_overrides)==null?void 0:v.bookmarks)||"",l=((h=n.chrome_url_overrides)==null?void 0:h.history)||"",u={sidepanel:o?o.split("/").pop():"sidepanel.html",options:r?r.split("/").pop():"options.html",popup:s?s.split("/").pop():"popup.html",devtools:c?c.split("/").pop():"devtools.html",newtab:g?g.split("/").pop():"newtab.html",bookmarks:a?a.split("/").pop():"bookmarks.html",history:l?l.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 L=new URL(t.url).pathname.split("/").pop();for(let[k,de]of Object.entries(u))if(L===de)return{context:k,tabId:((I=t.tab)==null?void 0:I.id)||0,frameId:t.frameId||0,url:t.url,portName:e.name};return{context:"unknown",tabId:((S=t.tab)==null?void 0:S.id)||0,frameId:t.frameId||0,url:t.url,portName:e.name}}return{context:"unknown",tabId:0,url:t.url,portName:e.name}}};var j=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 C("invalid-context","Port name not provided");if(!e.name||!e.name.startsWith(this.namespace+":"))throw new C("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 C("invalid-context","Missing context or connection ID. Message was: "+JSON.stringify(t));let o=this.agentOperations.addAgent(e);if(!o)throw new C("invalid-context","Failed to add agent");let r=this.agentOperations.getAgentById(o);r&&this.confirmConnection(r),this.agentOperations.printAgents()}catch(n){this.handleConnectionError(e,n)}}handleConnectionError(e,t){let n=t instanceof C?t:new C("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 C("invalid-port","Agent port is undefined when confirming connection");e.port.postMessage({action:"porter-handshake",payload:{info:e.info,currentConnections:this.agentOperations.getAllAgentsInfo()}})}};var W=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 s;if(!o||!o.id)return;let r=(s=this.agentOperations.getAgentById(o.id))==null?void 0:s.info;if(!r){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",r)}}}async post(e,t){return new Promise((n,o)=>{try{this.logger.debug("Post request received:",{action:e.action,target:t});let r=setTimeout(()=>{let s=new Error("Message posting timed out");this.logger.error("Post timeout:",s),o(s)},5e3);t===void 0?this.broadcastMessage(e):xe(t)?this.postToLocation(e,t):Pe(t)?this.postToContext(e,t):typeof t=="string"?this.postToId(e,t):this.postToTab(e,t),clearTimeout(r),n()}catch(r){let s=r instanceof Error?r.message:"Unknown error";this.logger.error("Failed to post message:",s),o(new Error(`Failed to post message: ${s}`))}})}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 C("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 C("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 C("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 C("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 C("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 r=e[o.message.action];if(r){this.logger.debug("onMessage, calling handler ",{event:o});let{message:s,...c}=o;r(s,c)}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,...r}=e;n(o,r);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 xe(i){return typeof i=="object"&&i!==null&&"context"in i&&"tabId"in i&&"frameId"in i}function Pe(i){return typeof i=="string"&&Object.values(B).includes(i)}var E=class E{constructor(e,t){if((t==null?void 0:t.debug)!==void 0&&x.configure({enabled:t.debug}),this.logger=x.getLogger("SW"),this.namespace=e||"porter",e||this.logger.error('No namespace provided, defaulting to "porter"'),this.agentManager=new V(this.logger),this.messageHandler=new W(this.agentManager,this.logger),this.connectionManager=new j(this.agentManager,this.namespace,this.logger),this.logger.info(`Constructing Porter with namespace: ${this.namespace}`),!he())throw new C("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)}),we.runtime.onConnect.addListener(this.connectionManager.handleConnection.bind(this.connectionManager))}static getInstance(e="porter",t){return E.staticLogger.debug(`Getting instance for namespace: ${e}`),E.instances.has(e)?(t==null?void 0:t.debug)!==void 0&&x.configure({enabled:t.debug}):(E.staticLogger.info(`Creating new instance for namespace: ${e}`),E.instances.set(e,new E(e,t))),E.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)}};E.instances=new Map,E.staticLogger=x.getLogger("SW");var ie=E;function F(i="porter",e){let t=ie.getInstance(i,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 Ee from"webextension-polyfill";var X=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 J=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.disconnectCallbacks=new Set;this.reconnectCallbacks=new Set;this.connectionId=`${Date.now()}-${Math.random().toString(36).substring(2,9)}`,this.logger=t,this.messageQueue=new X(t)}onDisconnect(e){return this.disconnectCallbacks.add(e),()=>{this.disconnectCallbacks.delete(e)}}onReconnect(e){return this.reconnectCallbacks.add(e),()=>{this.reconnectCallbacks.delete(e)}}emitDisconnect(){this.logger.debug("Emitting disconnect event",{callbackCount:this.disconnectCallbacks.size}),this.disconnectCallbacks.forEach(e=>{try{e()}catch(t){this.logger.error("Error in disconnect callback:",t)}})}emitReconnect(e){this.logger.debug("Emitting reconnect event",{callbackCount:this.reconnectCallbacks.size,info:e}),this.reconnectCallbacks.forEach(t=>{try{t(e)}catch(n){this.logger.error("Error in reconnect callback:",n)}})}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=Ee.runtime.connect({name:t});let n=new Promise((o,r)=>{var g;let s=setTimeout(()=>r(new C("connection-timeout","Connection timed out waiting for handshake")),this.CONNECTION_TIMEOUT),c=a=>{var l,u;a.action==="porter-handshake"?(this.logger.debug("Received handshake:",a),clearTimeout(s),this.agentInfo=a.payload.info,this.logger.debug("Handshake agent info:",{agentInfo:this.agentInfo}),(l=this.port)==null||l.onMessage.removeListener(c),o()):a.action==="porter-error"&&(clearTimeout(s),(u=this.port)==null||u.onMessage.removeListener(c),this.logger.error("Error:",a),r(new C(a.payload.type,a.payload.message,a.payload.details)))};(g=this.port)==null||g.onMessage.addListener(c)});(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{let o={...t};n&&(o.target=n),this.port.postMessage(o),this.logger.debug("Successfully resent queued message:",{message:o})}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.emitDisconnect(),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.reconnectTimer=null),this.logger.info("Reconnection successful",{attempts:this.reconnectAttemptCount,queuedMessages:this.messageQueue.isEmpty()?0:"some"}),this.agentInfo&&this.emitReconnect(this.agentInfo)}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 _=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(r=>r(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 C("message-failed","Failed to post message",{originalError:o,message:t,target:n})}}};var R=class R{constructor(e={}){var o,r;let t=(o=e.namespace)!=null?o:"porter",n=(r=e.agentContext)!=null?r:this.determineContext();e.debug!==void 0&&x.configure({enabled:e.debug}),this.logger=x.getLogger("Agent"),this.connectionManager=new J(t,this.logger),this.messageHandler=new _(this.logger),this.connectionManager.onReconnect(s=>{this.logger.info("Reconnected, re-wiring port listeners",{info:s}),this.setupPortListeners()}),this.logger.info("Initializing with options: ",{options:e,context:n}),this.initializeConnection()}static getInstance(e={}){return!R.instance||R.instance.connectionManager.getNamespace()!==e.namespace?R.instance=new R(e):e.debug!==void 0&&x.configure({enabled:e.debug}),R.instance}async initializeConnection(){await this.connectionManager.initializeConnection(),this.setupPortListeners()}setupPortListeners(){let e=this.connectionManager.getPort();e?(this.logger.debug("Setting up port listeners"),e.onMessage.addListener(t=>this.messageHandler.handleMessage(e,t)),e.onDisconnect.addListener(t=>this.connectionManager.handleDisconnect(t))):this.logger.warn("Cannot setup port listeners: no port available")}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}onDisconnect(e){return this.connectionManager.onDisconnect(e)}onReconnect(e){return this.connectionManager.onReconnect(e)}};R.instance=null;var se=R;function Y(i){let e=se.getInstance(i);return{type:"agent",post:e.post.bind(e),onMessage:e.onMessage.bind(e),on:e.on.bind(e),getAgentInfo:e.getAgentInfo.bind(e),onDisconnect:e.onDisconnect.bind(e),onReconnect:e.onReconnect.bind(e)}}import{useState as sn,useEffect as an,useCallback as gn,useRef as cn,useMemo as ln}from"react";function Z(i,e){if(i===e)return!0;if(i==null||typeof i!="object"||e==null||typeof e!="object")return!1;let t=Object.keys(i),n=Object.keys(e);if(t.length!==n.length)return!1;t.sort(),n.sort();for(let o=0;o<t.length;o++){let r=t[o];if(r!==n[o]||!Z(i[r],e[r]))return!1}return!0}var $=class ${static setDebug(e){$._debug=e}static isDebugEnabled(){return $._debug}};$._debug=!1;var K=$;function pe(){return K.isDebugEnabled()}function ae(i,e,t){let n=t.value;return t.value=function(...o){var r;if(pe()){let s=new Error().stack,g=(r=((s==null?void 0:s.split(`
|
|
2
|
+
`))||[])[3])==null?void 0:r.match(/at\s+(\S+)\s+/),a=g?g[1]:"unknown",l=o.length>1?o[1]:void 0,u={source:a,timestamp:Date.now(),instanceKey:o[0],changes:l};console.log("Crann State Change:",u)}return n.apply(this,o)},t}var A=class A{constructor(e){this.tag=null;this.context=e}static setDebug(e){A.debug=e}static setPrefix(e){A.prefix=e}setTag(e){this.tag=e}withTag(e){let t=new A(this.context);return t.setTag(e),t}getFullContext(){return this.tag?`${this.context}:${this.tag}`:this.context}createLogMethods(){let e=this.getFullContext(),t=`%c${A.prefix}%c [%c${e}%c]`,n="color: #3fcbff; font-weight: bold",o="color: #d58cff; font-weight: bold",r="";return A.debug?{debug:console.log.bind(console,t,n,r,o,r),log:console.log.bind(console,t,n,r,o,r),info:console.info.bind(console,t,n,r,o,r),warn:console.warn.bind(console,t,n,r,o,r),error:console.error.bind(console,t,n,r,o,r)}:{debug:A.noOp,log:A.noOp,info:A.noOp,warn:A.noOp,error:A.noOp}}get debug(){return this.createLogMethods().debug}get log(){return this.createLogMethods().log}get info(){return this.createLogMethods().info}get warn(){return this.createLogMethods().warn}get error(){return this.createLogMethods().error}static forContext(e,t){let n=new A(e);return t&&n.setTag(t),n}};A.debug=!1,A.prefix="CrannLogger",A.noOp=function(){};var M=A;function T(i,e={terse:!0}){let n=(o=>{let r=String(o);return r.length>=4?r.slice(-4):r.padStart(4,"0")})(i.location.tabId);return e!=null&&e.terse?`${n}:${i.location.frameId}`:`${i.location.context}:${n}:${i.location.frameId}`}function me(i,e,t,n,o){var l,u;let r=new Map,s=new Map,c=(l=i.context)!=null&&l.isServiceWorker?"Core":"Agent",g=M.forContext(`${c}:RPC`);return(u=i.context)!=null&&u.agentInfo&&g.setTag(T(i.context.agentInfo)),i.addEventListener("message",p=>{g.debug("Message received:",p);let[f,b]=p.data;if("call"in b&&"args"in b.call){g.debug("Processing call message:",b);let v=b.call,{id:h,args:I,target:S}=v,N=t[h];if(!N){i.postMessage([f,{error:{id:h,error:"Action not found",target:S}}]);return}try{if(N.validate&&N.validate(...I),!S){i.postMessage([f,{error:{id:h,error:"No target provided for action call",target:S}}]);return}let L=e();g.debug("Executing action with most current state:",L),Promise.resolve(N.handler(L,n,S,...I)).then(k=>{g.debug("Action handler result:",{result:k,target:S}),i.postMessage([f,{result:{id:h,result:k,target:S}}])},k=>{i.postMessage([f,{error:{id:h,error:k.message,target:S}}])})}catch(L){L instanceof Error?i.postMessage([f,{error:{id:h,error:L.message,target:S}}]):i.postMessage([f,{error:{id:h,error:"Unknown error occurred",target:S}}])}}else if("result"in b){let v=b.result,h=r.get(f);h&&(h(v.result),r.delete(f))}else if("error"in b){let v=b.error,h=r.get(f);h&&(h(Promise.reject(new Error(v.error))),r.delete(f))}else if("release"in b){let v=b.release,h=s.get(v.id);h&&(h.clear(),s.delete(v.id))}}),new Proxy({},{get(p,f){return(...b)=>{let v=Math.random();return new Promise((h,I)=>{r.set(v,S=>{S instanceof Promise?S.then(h,I):h(S)}),i.postMessage([v,{call:{id:f,args:b}}])})}}})}function ee(i,e,t,n){let o=t||F("crann"),r=o.type!=="agent",s=M.forContext(r?"Core:RPC":"Agent:RPC"),c={postMessage:(g,a)=>{if(r){s.debug("Posting message from service worker:",{message:g,transferables:a});let[,l]=g,u=De(l);if(!u){s.warn("No target specified for RPC response in service worker");return}o.post({action:"rpc",payload:{message:g,transferables:a||[]}},u)}else{let l=o.getAgentInfo();if(!l){s.warn("No agent info found for posting message",{agentInfo:l});return}let u=T(l),[,p]=g;"call"in p&&(p.call.target=l==null?void 0:l.location),s.withTag(u).debug("Sending RPC message from agent:",{rpcPayload:p,message:g}),o.post({action:"rpc",payload:{message:g,transferables:a||[]}})}},addEventListener:(g,a)=>{o.on({rpc:(l,u)=>{try{if(!u)s.debug("RPC message received:",{message:l,event:g});else{let h=T(u);s.withTag(h).debug("RPC message received:",{message:l,event:g})}let{payload:p}=l,{message:f,transferables:b=[]}=p,v=new MessageEvent("message",{data:f,ports:b.filter(h=>h instanceof MessagePort)||[]});a(v)}catch(p){s.error("Failed to parse RPC message payload:",p)}}})},removeEventListener:()=>{},context:{isServiceWorker:r,agentInfo:r?void 0:o.getAgentInfo()}};return me(c,i,e,n)}function De(i){if("result"in i)return i.result.target;if("error"in i)return i.error.target;if("call"in i)return i.call.target;if("release"in i)return i.release.target}var D=class D{constructor(e,t){this.config=e;this.instances=new Map;this.stateChangeListeners=[];this.instanceReadyListeners=[];this.storagePrefix="crann_";this.porter=F("crann",{debug:!1});var g;t!=null&&t.debug&&(K.setDebug(!0),M.setDebug(!0)),this.storagePrefix=(g=t==null?void 0:t.storagePrefix)!=null?g:this.storagePrefix,this.logger=M.forContext("Core"),this.logger.log("Constructing Crann with new logger"),this.defaultInstanceState=this.initializeInstanceDefault(),this.defaultServiceState=this.serviceState=this.initializeServiceDefault(),this.hydrate(),this.logger.log("Crann constructed, setting initial message handlers"),this.porter.on({setState:(a,l)=>{if(!l){this.logger.warn("setState message heard from unknown agent");return}let u=T(l);this.logger.withTag(u).log("Setting state:",a),this.set(a.payload.state,l.id)}});let n=new Set;this.porter.onMessagesSet(a=>{if(!a){this.logger.error("Messages set but no agent info.",{info:a});return}let l=T(a);if(this.logger.withTag(l).log("onMessagesSet received for agent:",{id:a.id,context:a.location.context,tabId:a.location.tabId,frameId:a.location.frameId,alreadyInitialized:n.has(a.id)}),n.has(a.id)){this.logger.withTag(l).log("Already sent initialState to agent, skipping:",a.id);return}n.add(a.id),this.logger.withTag(l).log("Messages set received. Sending initial state.",{info:a});let u=this.get(a.id);this.porter.post({action:"initialState",payload:{state:u,info:a}},a.location),this.notifyInstanceReady(a.id,a)}),this.porter.onConnect(a=>{if(!a){this.logger.error("Agent connected but no agent info.",{info:a});return}let l=T(a);this.logger.withTag(l).log("Agent connected",{info:a}),this.addInstance(a.id,l),this.porter.onDisconnect(u=>{this.logger.withTag(T(u)).log("Agent disconnect heard. Connection type, context and location:",{info:u}),this.removeInstance(u.id)})});let r=(a,l)=>l!==void 0?this.set(a,l):this.set(a),s=this.extractActions(e),c=()=>{let a=this.get();return this.logger.log("State getter called, returning current state:",a),a};this.rpcEndpoint=ee(c,s,this.porter,r)}static getInstance(e,t){return D.instance?t!=null&&t.debug&&M.forContext("Core").log("Instance requested and already existed, returning"):D.instance=new D(e,t),D.instance}async addInstance(e,t){if(this.instances.has(e))this.logger.withTag(t).log("Instance was already registered, ignoring request from key");else{this.logger.withTag(t).log("Adding instance from agent key");let n={...this.defaultInstanceState};this.instances.set(e,n)}}async removeInstance(e){this.instances.has(e)?(this.logger.withTag(e).log("Remove instance requested"),this.instances.delete(e)):this.logger.withTag(e).log("Remove instance requested but it did not exist!")}async setServiceState(e){this.logger.log("Request to set service state with update:",e),this.logger.log("Existing service state was ",this.serviceState);let t={...this.serviceState,...e};Z(this.serviceState,t)?this.logger.log("New state seems to be the same as existing, skipping"):(this.logger.log("Confirmed new state was different than existing so proceeding to persist then notify all connected instances."),this.serviceState=t,await this.persist(e),this.notify(e))}async setInstanceState(e,t){this.logger.withTag(e).log("Request to update instance state, update:",t);let n=this.instances.get(e)||this.defaultInstanceState,o={...n,...t};Z(n,o)?this.logger.withTag(e).log("Instance state update is not different, skipping update."):(this.logger.withTag(e).log("Instance state update is different, updating and notifying."),this.instances.set(e,o),this.notify(t,e))}async persist(e){this.logger.log("Persisting state");let t=!1;for(let n in e||this.serviceState){let r=this.config[n].persist||"none",s=e?e[n]:this.serviceState[n];switch(r){case"session":await te.storage.session.set({[this.storagePrefix+n]:s}),t=!0;break;case"local":await te.storage.local.set({[this.storagePrefix+n]:s}),t=!0;break;default:break}}t?this.logger.log("State was persisted"):this.logger.log("Nothing to persist")}async clear(){this.logger.log("Clearing state"),this.serviceState=this.defaultServiceState,this.instances.forEach((e,t)=>{this.instances.set(t,this.defaultInstanceState)}),await this.persist(),this.notify({})}subscribe(e){this.logger.log("Subscribing to state"),this.stateChangeListeners.push(e)}notify(e,t){let n=t?this.porter.getAgentById(t):void 0,o=t?this.get(t):this.get();this.stateChangeListeners.length>0&&(this.logger.log("Notifying state change listeners in source"),this.stateChangeListeners.forEach(r=>{r(o,e,n==null?void 0:n.info)})),t&&(n!=null&&n.info.location)?(this.logger.withTag(t).log("Notifying of state change."),this.porter.post({action:"stateUpdate",payload:{state:e}},n.info.location)):(this.logger.log("Notifying everyone"),this.instances.forEach((r,s)=>{this.porter.post({action:"stateUpdate",payload:{state:e}},s)}))}get(e){return e?{...this.serviceState,...this.instances.get(e)}:{...this.serviceState}}findInstance(e){let t=this.porter.getAgentByLocation(e);if(!t)return this.logger.log("Could not find agent for location:",{location:e}),null;for(let[n,o]of this.instances)if(n===t.info.id)return this.logger.log("Found instance for key:",n),n;return this.logger.log("Could not find instance for context and location:",{location:e}),null}queryAgents(e){return this.porter.queryAgents(e)}async set(e,t){let n={},o={};for(let r in e){let s=this.config[r];if(Re(s)){if(s.partition==="instance"){let c=r,g=e;n[c]=g[c]}else if(!s.partition||s.partition===z.Service){let c=r,g=e;o[c]=g[c]}}}t&&Object.keys(n).length>0&&(this.logger.withTag(t).log("Setting instance state:",n),this.setInstanceState(t,n)),Object.keys(o).length>0&&(this.logger.log("Setting service state:",o),this.setServiceState(o))}async hydrate(){this.logger.log("Hydrating state from storage.");let e=await te.storage.local.get(null),t=await te.storage.session.get(null),n={...e,...t};this.logger.log("Storage data is:",{local:e,session:t,combined:n});let o={},r=!1;for(let s in n){let c=this.removePrefix(s);if(this.logger.log(`Checking storage key ${s} -> ${c}`),this.config.hasOwnProperty(c)){let g=n[s];this.logger.log(`Found storage value for ${c}:`,g),o[c]=g,r=!0}}r?this.logger.log("Hydrated some items."):this.logger.log("No items found in storage."),this.serviceState={...this.defaultServiceState,...o}}removePrefix(e){return e.startsWith(this.storagePrefix)?e.replace(this.storagePrefix,""):e}initializeInstanceDefault(){let e={};return Object.keys(this.config).forEach(t=>{let n=this.config[t];U(n)&&n.partition==="instance"&&(e[t]=n.default)}),e}initializeServiceDefault(){this.logger.log("Initializing service default state"),this.logger.log("Config is:",this.config);let e={};return Object.keys(this.config).forEach(t=>{let n=this.config[t];this.logger.log("Item is:",n),U(n)&&(!n.partition||n.partition===z.Service)&&(e[t]=n.default,this.logger.log("Setting service state for key:",t,"to",n.default))}),this.logger.log("Final service state is:",e),e}subscribeToInstanceReady(e){return this.logger.log("Subscribing to instance ready events"),this.instanceReadyListeners.push(e),this.instances.forEach((t,n)=>{let o=this.porter.getAgentById(n);o!=null&&o.info&&e(n,o.info)}),()=>{this.logger.log("Unsubscribing from instance ready events");let t=this.instanceReadyListeners.indexOf(e);t!==-1&&this.instanceReadyListeners.splice(t,1)}}notifyInstanceReady(e,t){if(this.instanceReadyListeners.length>0){let n=T(t);this.logger.withTag(n).log("Notifying instance ready listeners"),this.instanceReadyListeners.forEach(o=>{o(e,t)})}}extractActions(e){return Object.entries(e).filter(([t,n])=>Q(n)).reduce((t,[n,o])=>{let r=o;return{...t,[n]:r}},{})}};D.instance=null,re([ae],D.prototype,"setServiceState",1),re([ae],D.prototype,"setInstanceState",1);var ne=D;function Le(i,e){let t=ne.getInstance(i,e);return{get:t.get.bind(t),set:t.set.bind(t),subscribe:t.subscribe.bind(t),onInstanceReady:t.subscribeToInstanceReady.bind(t),findInstance:t.findInstance.bind(t),queryAgents:t.queryAgents.bind(t),clear:t.clear.bind(t)}}function Re(i){return i&&typeof i=="object"&&"default"in i}var w={connected:!1},O=null,ge=new Set,ce=new Set;function le(i,e){let t=(e==null?void 0:e.debug)||!1,n=e==null?void 0:e.context;t&&M.setDebug(!0);let o=M.forContext("Agent"),r,s="unset",c=new Set;if(o.log("Initializing Crann Agent"+(n?` with context: ${n}`:"")),O&&w.connected)return o.log("We had an instance already and it's connected, returning"),o.log("Connect, calling onReady callback"),setTimeout(()=>{c.forEach(d=>d(w))},0),O;O&&!w.connected&&(o.log("We had an instance but it's disconnected, creating new connection"),O=null),o.log("No existing instance, creating a new one");let g=Y({namespace:"crann",debug:!1});o.log("Porter connection created"),g.onDisconnect(()=>{o.log("Porter connection lost, updating connection status"),w={connected:!1},ge.forEach(d=>{try{d()}catch(m){o.error("Error in disconnect callback:",m)}}),c.forEach(d=>{try{d(w)}catch(m){o.error("Error in onReady callback during disconnect:",m)}})}),g.onReconnect(d=>{o.log("Porter reconnected, updating connection status",d),w={connected:!0,agent:d},r=d,s=T(d),o.setTag(s),ce.forEach(m=>{try{m(d)}catch(P){o.error("Error in reconnect callback:",P)}}),c.forEach(m=>{try{m(w)}catch(P){o.error("Error in onReady callback during reconnect:",P)}})});let a=Object.entries(i).filter(([d,m])=>Q(m)).reduce((d,[m,P])=>{let q=P;return{...d,[m]:{type:"action",handler:q.handler,validate:q.validate}}},{}),l=ee(()=>be(i),a,g),u=!1;g.on({initialState:d=>{if(o.log("initialState received",{alreadyReceived:u,payload:d.payload}),u){o.log("Ignoring duplicate initialState message");return}u=!0,p=d.payload.state,r=d.payload.info,s=T(r),w={connected:!0,agent:r},o.setTag(s),o.log(`Initial state received and ${b.size} listeners notified`,{message:d}),c.forEach(m=>{o.log("Calling onReady callbacks"),m(w)}),b.forEach(m=>{m.callback(p)})},stateUpdate:d=>{f=d.payload.state,p={...p,...f},o.log("State updated:",{message:d,changes:f,_state:p}),f&&b.forEach(m=>{(m.keys===void 0||m.keys.some(q=>q in f))&&m.callback(f)})}}),o.log("Porter connected. Setting up state and listeners");let p=be(i),f=null,b=new Set;o.log("Completed setup, returning instance");let v=()=>p,h=d=>{o.log("Calling post with setState",d),g.post({action:"setState",payload:{state:d}})},I=(d,m)=>{let P={keys:m,callback:d};return b.add(P),()=>{b.delete(P)}};return O={useCrann:d=>{let m=()=>v()[d],P=H=>h({[d]:H}),q=H=>{let ue=m();return I(ve=>{if(d in ve){let fe=m(),ye=v();H({current:fe,previous:ue,state:ye}),ue=fe}},[d])};return[m(),P,q]},get:v,set:h,subscribe:I,getAgentInfo:()=>r,onReady:d=>(o.log("onReady callback added"),c.add(d),w.connected&&(o.log("calling onReady callback"),setTimeout(()=>{d(w)},0)),()=>c.delete(d)),callAction:async(d,...m)=>(o.log("Calling action",d,m),l[d](...m)),onDisconnect:d=>(o.log("onDisconnect callback added"),ge.add(d),()=>{ge.delete(d)}),onReconnect:d=>(o.log("onReconnect callback added"),ce.add(d),()=>{ce.delete(d)})},O}function ke(){return O!==null}function be(i){let e={};return Object.keys(i).forEach(t=>{let n=i[t];U(n)&&(e[t]=n.default)}),e}import{useState as Oe,useEffect as Ce,useCallback as oe,useMemo as Ne,useRef as qe}from"react";function Ue(i){return function(t){let{useCrann:n,get:o,set:r,subscribe:s,callAction:c}=Ne(()=>le(i),[t]),g=oe(u=>{let[p,f]=Oe(o()[u]),b=qe(p);Ce(()=>{b.current=p},[p]),Ce(()=>(f(o()[u]),s(I=>{u in I&&f(I[u])},[u])),[u]);let v=oe(h=>{r({[u]:h})},[u]);return[p,v]},[o,r,s]),a=oe(()=>o(),[o]),l=oe(u=>{r(u)},[r]);return{useStateItem:g,getState:a,setState:l,useCrann:n,callAction:c}}}function Be(i){return i}export{ne as Crann,z as Partition,Te as Persistence,le as connect,ke as connected,Le as create,Be as createConfig,Ue as createCrannStateHook};
|
|
3
3
|
//# sourceMappingURL=index.js.map
|