UNPKG

crann

Version:

Effortless State Synchronization for Web Extensions

4 lines 57.5 kB
"use strict";var nt=Object.create;var J=Object.defineProperty;var qe=Object.getOwnPropertyDescriptor;var it=Object.getOwnPropertyNames;var ot=Object.getPrototypeOf,rt=Object.prototype.hasOwnProperty;var st=(o,e)=>()=>(o&&(e=o(o=0)),e);var Ue=(o,e)=>{for(var t in e)J(o,t,{get:e[t],enumerable:!0})},Be=(o,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of it(e))!rt.call(o,i)&&i!==t&&J(o,i,{get:()=>e[i],enumerable:!(n=qe(e,i))||n.enumerable});return o};var Z=(o,e,t)=>(t=o!=null?nt(ot(o)):{},Be(e||!o||!o.__esModule?J(t,"default",{value:o,enumerable:!0}):t,o)),Ke=o=>Be(J({},"__esModule",{value:!0}),o),Ae=(o,e,t,n)=>{for(var i=n>1?void 0:n?qe(e,t):e,r=o.length-1,s;r>=0;r--)(s=o[r])&&(i=(n?s(e,t,i):s(i))||i);return n&&i&&J(e,t,i),i};var _e={};Ue(_e,{ActionError:()=>O,ConfigError:()=>te,ConnectionError:()=>xe,CrannError:()=>M,LifecycleError:()=>L,StorageCollisionError:()=>De,ValidationError:()=>H});var M,te,L,xe,O,H,De,G=st(()=>{"use strict";M=class extends Error{constructor(e){super(e),this.name="CrannError",Error.captureStackTrace&&Error.captureStackTrace(this,this.constructor)}},te=class extends M{constructor(t,n){super(n?`Config error in '${n}': ${t}`:`Config error: ${t}`);this.field=n;this.name="ConfigError"}},L=class extends M{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"}},xe=class extends M{constructor(t,n,i){super(`Connection error for store "${n}": ${t}`);this.storeName=n;this.reason=i;this.name="ConnectionError"}},O=class extends M{constructor(t,n,i){super(`Action "${t}" failed in store "${n}": ${i}`);this.actionName=t;this.storeName=n;this.reason=i;this.name="ActionError"}},H=class extends M{constructor(t,n,i){super(`Validation failed for action "${t}" in store "${n}": ${i}`);this.actionName=t;this.storeName=n;this.reason=i;this.name="ValidationError"}},De=class extends M{constructor(t){super(`Store name "${t}" is already in use. Each store must have a unique name. If you're trying to connect to an existing store, use connectStore() instead.`);this.storeName=t;this.name="StorageCollisionError"}}});var ft={};Ue(ft,{ActionError:()=>O,Agent:()=>X,ConfigError:()=>te,Crann:()=>ae,CrannError:()=>M,LifecycleError:()=>L,Partition:()=>ne,Persist:()=>P,Persistence:()=>Qe,Scope:()=>N,Store:()=>W,ValidationError:()=>H,clearOrphanedData:()=>Me,connect:()=>Ze,connectStore:()=>Ee,connected:()=>Ye,create:()=>Xe,createConfig:()=>we,createStore:()=>Pe});module.exports=Ke(ft);var ze=Z(require("webextension-polyfill"));var Y=(c=>(c.ContentScript="contentscript",c.Extension="extension",c.Popup="popup",c.Sidepanel="sidepanel",c.Devtools="devtools",c.Options="options",c.Unknown="unknown",c))(Y||{});var S=class extends Error{constructor(t,n,i){super(n);this.type=t;this.details=i;this.name="PorterError"}};function Fe(){return typeof ServiceWorkerGlobalScope<"u"&&self instanceof ServiceWorkerGlobalScope}var v=class v{constructor(e){this.context=e}static getLevel(){var t,n,i;return((t=v.globalOptions)==null?void 0:t.level)!==void 0?v.globalOptions.level:typeof process<"u"&&(((n=process.env)==null?void 0:n.NODE_ENV)==="production"||((i=process.env)==null?void 0:i.PORTER_ENV)==="production")?1:4}static configure(e){v.globalOptions=e,e.level!==void 0&&(v.level=e.level),e.enabled!==void 0&&(v.enabled=e.enabled)}static getLogger(e){return this.instances.has(e)||this.instances.set(e,new v(e)),this.instances.get(e)}error(e,...t){v.enabled&&v.level>=0&&console.error(`[Porter:${this.context}] ${e}`,...t)}warn(e,...t){v.enabled&&v.level>=1&&console.warn(`[Porter:${this.context}] ${e}`,...t)}info(e,...t){v.enabled&&v.level>=2&&console.info(`[Porter:${this.context}] ${e}`,...t)}debug(e,...t){v.enabled&&v.level>=3&&console.debug(`[Porter:${this.context}] ${e}`,...t)}trace(e,...t){v.enabled&&v.level>=4&&console.trace(`[Porter:${this.context}] ${e}`,...t)}};v.level=v.getLevel(),v.enabled=!1,v.instances=new Map;var D=v;var Ve=Z(require("webextension-polyfill")),He=require("uuid");var ge=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 p,m;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 i=n.context,r=n.tabId||-1,s=n.frameId||0;this.logger.debug("Determined context for new agent",{determinedContext:i,tabId:r,frameId:s});let c=Array.from(this.agentsInfo.values()).filter(f=>f.location.context===i&&f.location.tabId===r&&f.location.frameId===s);c.length>0&&this.logger.debug("Adding agent: Found existing similar agent.",{tabAgentsInfo:c});let a=((m=(p=this.getAgentByLocation({context:i,tabId:r,frameId:s}))==null?void 0:p.info)==null?void 0:m.id)||(0,He.v4)();this.logger.debug(`Adding agent with id: ${a}`),this.agents.set(a,e);let g={id:a,location:{context:i,tabId:r,frameId:s},createdAt:Date.now(),lastActiveAt:Date.now()};this.agentsInfo.set(a,g),this.logger.debug(`Constructed agent info: ${JSON.stringify(g)}`),e.onMessage.addListener(f=>this.emit("agentMessage",f,g));let d={port:e,info:g};return e.onDisconnect.addListener(()=>{this.emit("agentDisconnect",g),this.logger.debug("Agent disconnected, removing from manager. ",{agentInfo:g}),this.removeAgent(a)}),this.emit("agentSetup",d),this.logger.debug("Setup complete for adding agent. ",{agentInfo:g}),a}getAgentByLocation(e){let{context:t,tabId:n,frameId:i}=e,r=Array.from(this.agentsInfo.entries()).find(([g,d])=>d.location.context===t&&d.location.tabId===n&&d.location.frameId===i);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),a=this.agentsInfo.get(s);return!c||!a?(this.logger.error("No agent found for location. ",{location:e}),null):{port:c,info:a}}getAgentsByContext(e){return Array.from(this.agentsInfo.entries()).filter(([n,i])=>i.location.context===e).map(([n,i])=>({port:this.agents.get(n),info:i}))}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,i])=>{let r=e.context?i.location.context===e.context:!0,s=e.tabId?i.location.tabId===e.tabId:!0,c=e.frameId?i.location.frameId===e.frameId:!0;return r&&s&&c}).map(([n,i])=>({port:this.agents.get(n),info:i}))}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(i=>i(...t))}identifyConnectionSource(e){var m,f,y,C,u,k,b;let t=e.sender;if(!t)return this.logger.error("Cannot add agent that did not have a sender"),null;let n=Ve.default.runtime.getManifest(),i=((m=n==null?void 0:n.side_panel)==null?void 0:m.default_path)||"",r=n.options_page||"",s=((f=n.action)==null?void 0:f.default_popup)||"",c=n.devtools_page||"",a=((y=n.chrome_url_overrides)==null?void 0:y.newtab)||"",g=((C=n.chrome_url_overrides)==null?void 0:C.bookmarks)||"",d=((u=n.chrome_url_overrides)==null?void 0:u.history)||"",p={sidepanel:i?i.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:a?a.split("/").pop():"newtab.html",bookmarks:g?g.split("/").pop():"bookmarks.html",history:d?d.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 U=new URL(t.url).pathname.split("/").pop();for(let[F,Oe]of Object.entries(p))if(U===Oe)return{context:F,tabId:((k=t.tab)==null?void 0:k.id)||0,frameId:t.frameId||0,url:t.url,portName:e.name};return{context:"unknown",tabId:((b=t.tab)==null?void 0:b.id)||0,frameId:t.frameId||0,url:t.url,portName:e.name}}return{context:"unknown",tabId:0,url:t.url,portName:e.name}}};var le=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 S("invalid-context","Port name not provided");if(!e.name||!e.name.startsWith(this.namespace+":"))throw new S("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 S("invalid-context","Missing context or connection ID. Message was: "+JSON.stringify(t));let i=this.agentOperations.addAgent(e);if(!i)throw new S("invalid-context","Failed to add agent");let r=this.agentOperations.getAgentById(i);r&&this.confirmConnection(r),this.agentOperations.printAgents()}catch(n){this.handleConnectionError(e,n)}}handleConnectionError(e,t){let n=t instanceof S?t:new S("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(i){this.logger.error("Failed to send error message: ",{error:i})}try{e.disconnect()}catch(i){this.logger.error("Failed to disconnect port: ",{error:i})}}confirmConnection(e){if(this.logger.debug("Sending confirmation message back to initiator ",{agent:e}),!e.port)throw new S("invalid-port","Agent port is undefined when confirming connection");e.port.postMessage({action:"porter-handshake",payload:{info:e.info,currentConnections:this.agentOperations.getAllAgentsInfo()}})}};var fe=class{constructor(e,t){this.agentOperations=e;this.logger=t;this.eventListeners=new Map;this.messageListeners=new Set;this.initializationHandler={"porter-messages-established":(n,i)=>{var s;if(!i||!i.id)return;let r=(s=this.agentOperations.getAgentById(i.id))==null?void 0:s.info;if(!r){this.logger.error("No agent info found for agent id: ",i.id);return}this.logger.debug("internalHandlers, established message received: ",i.id,n),this.emitEvent("onMessagesSet",r)}}}async post(e,t){return new Promise((n,i)=>{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),i(s)},5e3);t===void 0?this.broadcastMessage(e):at(t)?this.postToLocation(e,t):ct(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),i(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 S("message-failed",`Failed to post message to tabId ${t}`,{originalError:e});return}n.forEach(i=>{i.port&&this.postToPort(e,i.port)})}postToLocation(e,t){this.agentOperations.queryAgents(t).forEach(i=>{if(!i.port)throw new S("invalid-target","No port found for agent",{agentInfo:i.info});this.postToPort(e,i.port)})}postToContext(e,t){this.agentOperations.queryAgents({context:t}).forEach(i=>{if(!i.port)throw new S("invalid-target","No port found for agent",{agentInfo:i.info});this.postToPort(e,i.port)})}postToPort(e,t){try{t.postMessage(e)}catch(n){throw new S("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 S("invalid-target",`No agent found for key: ${t}`);this.postToPort(e,n.port)}onMessage(e){Array.from(this.messageListeners).find(i=>JSON.stringify(i.config)===JSON.stringify(e))&&this.logger.warn(`Listener with same config already exists: ${JSON.stringify(e)}`);let n={config:e,listener:i=>{let r=e[i.message.action];if(r){this.logger.debug("onMessage, calling handler ",{event:i});let{message:s,...c}=i;r(s,c)}else this.logger.debug("onMessage, no handler found ",{event:i})}};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(i=>i(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:i,...r}=e;n(i,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:i}of this.messageListeners)i[e.message.action]&&(n(e),t++,this.logger.debug("Message handled by registered listener: ",{listener:n,config:i}));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 at(o){return typeof o=="object"&&o!==null&&"context"in o&&"tabId"in o&&"frameId"in o}function ct(o){return typeof o=="string"&&Object.values(Y).includes(o)}var R=class R{constructor(e,t){if((t==null?void 0:t.debug)!==void 0&&D.configure({enabled:t.debug}),this.logger=D.getLogger("SW"),this.namespace=e||"porter",e||this.logger.error('No namespace provided, defaulting to "porter"'),this.agentManager=new ge(this.logger),this.messageHandler=new fe(this.agentManager,this.logger),this.connectionManager=new le(this.agentManager,this.namespace,this.logger),this.logger.info(`Constructing Porter with namespace: ${this.namespace}`),!Fe())throw new S("invalid-context","Can only create in a service worker");this.agentManager.on("agentMessage",(n,i)=>{this.messageHandler.handleIncomingMessage(n,i)}),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)}),ze.default.runtime.onConnect.addListener(this.connectionManager.handleConnection.bind(this.connectionManager))}static getInstance(e="porter",t){return R.staticLogger.debug(`Getting instance for namespace: ${e}`),R.instances.has(e)?(t==null?void 0:t.debug)!==void 0&&D.configure({enabled:t.debug}):(R.staticLogger.info(`Creating new instance for namespace: ${e}`),R.instances.set(e,new R(e,t))),R.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)}};R.instances=new Map,R.staticLogger=D.getLogger("SW");var Te=R;function V(o="porter",e){let t=Te.getInstance(o,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)}}var je=Z(require("webextension-polyfill"));var ue=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 he=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 ue(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=je.default.runtime.connect({name:t});let n=new Promise((i,r)=>{var a;let s=setTimeout(()=>r(new S("connection-timeout","Connection timed out waiting for handshake")),this.CONNECTION_TIMEOUT),c=g=>{var d,p;g.action==="porter-handshake"?(this.logger.debug("Received handshake:",g),clearTimeout(s),this.agentInfo=g.payload.info,this.logger.debug("Handshake agent info:",{agentInfo:this.agentInfo}),(d=this.port)==null||d.onMessage.removeListener(c),i()):g.action==="porter-error"&&(clearTimeout(s),(p=this.port)==null||p.onMessage.removeListener(c),this.logger.error("Error:",g),r(new S(g.payload.type,g.payload.message,g.payload.details)))};(a=this.port)==null||a.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 i={...t};n&&(i.target=n),this.port.postMessage(i),this.logger.debug("Successfully resent queued message:",{message:i})}catch(i){this.logger.error("Failed to process queued message:",i),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 pe=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,i=this.handlers.get(n)||[];i.length>0?(this.logger.debug(`Found ${i.length} handlers for action: ${n}`),i.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(i){throw new S("message-failed","Failed to post message",{originalError:i,message:t,target:n})}}};var B=class B{constructor(e={}){let t=e.namespace??"porter",n=e.agentContext??this.determineContext();e.debug!==void 0&&D.configure({enabled:e.debug}),this.logger=D.getLogger("Agent"),this.connectionManager=new he(t,this.logger),this.messageHandler=new pe(this.logger),this.connectionManager.onReconnect(i=>{this.logger.info("Reconnected, re-wiring port listeners",{info:i}),this.setupPortListeners()}),this.logger.info("Initializing with options: ",{options:e,context:n}),this.initializeConnection()}static getInstance(e={}){return!B.instance||B.instance.connectionManager.getNamespace()!==e.namespace?B.instance=new B(e):e.debug!==void 0&&D.configure({enabled:e.debug}),B.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(i){this.logger.error("Failed to post message, queueing for retry",{error:i}),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)}};B.instance=null;var Ie=B;function Q(o){let e=Ie.getInstance(o);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 ee=require("react");var N={Shared:"shared",Agent:"agent"},P={Local:"local",Session:"session",None:"none"};function $(o){return typeof o=="object"&&o!==null&&"default"in o&&!("handler"in o)}function gt(o){return typeof o=="object"&&o!==null&&"handler"in o}function we(o){let{ConfigError:e}=(G(),Ke(_e));if(!o.name)throw new e("'name' is required","name");if(typeof o.name!="string")throw new e("'name' must be a string","name");if(!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(o.name))throw new e("'name' must start with a letter and contain only letters, numbers, underscores, and hyphens","name");if(o.version!==void 0&&(typeof o.version!="number"||!Number.isInteger(o.version)||o.version<1))throw new e("'version' must be a positive integer","version");for(let[t,n]of Object.entries(o))if(!(t==="name"||t==="version"||t==="actions")&&$(n)){if(n.scope!==void 0&&n.scope!==N.Shared&&n.scope!==N.Agent)throw new e(`Invalid scope '${n.scope}'. Must be 'shared' or 'agent'`,t);if(n.persist!==void 0&&n.persist!==P.Local&&n.persist!==P.Session&&n.persist!==P.None)throw new e(`Invalid persist '${n.persist}'. Must be 'local', 'session', or 'none'`,t)}if(o.actions!==void 0){if(typeof o.actions!="object"||o.actions===null)throw new e("'actions' must be an object","actions");for(let[t,n]of Object.entries(o.actions)){if(!gt(n))throw new e(`Action '${t}' must have a 'handler' function`,`actions.${t}`);if(typeof n.handler!="function")throw new e(`Action '${t}' handler must be a function`,`actions.${t}`)}}return{...o,version:o.version??1}}function K(o,e){if(o===e)return!0;if(o==null||typeof o!="object"||e==null||typeof e!="object")return!1;let t=Object.keys(o),n=Object.keys(e);if(t.length!==n.length)return!1;t.sort(),n.sort();for(let i=0;i<t.length;i++){let r=t[i];if(r!==n[i]||!K(o[r],e[r]))return!1}return!0}var me=class{constructor(e,t){this.agentStates=new Map;this.config=e,this.persistence=t,this.defaultSharedState=this.buildDefaultSharedState(),this.defaultAgentState=this.buildDefaultAgentState(),this.sharedState={...this.defaultSharedState}}getState(){return{...this.sharedState}}getSharedState(){return{...this.sharedState}}getAgentState(e){return this.agentStates.get(e)??{...this.defaultAgentState}}getFullStateForAgent(e){let t=this.getAgentState(e);return{...this.sharedState,...t}}setState(e,t){let n={},i={};for(let r of Object.keys(e)){let s=this.config[r];if(!$(s))continue;let c=e[r],a=s.scope===N.Agent;a&&t?i[r]=c:a||(n[r]=c)}if(Object.keys(n).length>0){let r={...this.sharedState,...n};K(this.sharedState,r)||(this.sharedState=r)}if(t&&Object.keys(i).length>0){let r=this.agentStates.get(t)??{...this.defaultAgentState},s={...r,...i};K(r,s)||this.agentStates.set(t,s)}return{sharedChanges:n,agentChanges:i}}setAgentState(e,t){let n=this.agentStates.get(e)??{...this.defaultAgentState},i={...n,...t};K(n,i)||this.agentStates.set(e,i)}hydrateSharedState(e){this.sharedState={...this.defaultSharedState,...e}}initializeAgentState(e){this.agentStates.has(e)||this.agentStates.set(e,{...this.defaultAgentState})}removeAgentState(e){this.agentStates.delete(e)}clear(){this.sharedState={...this.defaultSharedState},this.agentStates.clear()}buildDefaultSharedState(){let e={};for(let[t,n]of Object.entries(this.config))t==="name"||t==="version"||t==="actions"||$(n)&&(!n.scope||n.scope===N.Shared)&&(e[t]=n.default);return e}buildDefaultAgentState(){let e={};for(let[t,n]of Object.entries(this.config))t==="name"||t==="version"||t==="actions"||$(n)&&n.scope===N.Agent&&(e[t]=n.default);return e}};var I=Z(require("webextension-polyfill"));var ye=class{constructor(e,t={}){this.config=e,this.options=t}buildKey(e){return`crann:${this.config.name}:v${this.config.version}:${e}`}parseKey(e){let t=e.match(/^crann:([^:]+):v(\d+):(.+)$/);return t?{name:t[1],version:parseInt(t[2],10),key:t[3]}:null}getMetaKey(){return`crann:${this.config.name}:__meta`}async hydrate(){let[e,t]=await Promise.all([I.default.storage.local.get(null),I.default.storage.session.get(null)]),n={...e,...t},i={};for(let[r,s]of Object.entries(this.config)){if(r==="name"||r==="version"||r==="actions"||!$(s)||s.scope==="agent"||!s.persist||s.persist===P.None)continue;let c=this.buildKey(r);c in n&&(i[r]=n[c])}return await this.updateMetadata(),i}async persist(e){let t={},n={};for(let[r,s]of Object.entries(e)){let c=this.config[r];if(!$(c))continue;let a=this.buildKey(r);switch(c.persist){case P.Local:t[a]=s;break;case P.Session:n[a]=s;break}}let i=[];Object.keys(t).length>0&&i.push(I.default.storage.local.set(t)),Object.keys(n).length>0&&i.push(I.default.storage.session.set(n)),await Promise.all(i)}async clearAll(){let e=[];for(let[t,n]of Object.entries(this.config))t==="name"||t==="version"||t==="actions"||$(n)&&n.persist&&n.persist!==P.None&&e.push(this.buildKey(t));e.push(this.getMetaKey()),await Promise.all([I.default.storage.local.remove(e),I.default.storage.session.remove(e)])}async updateMetadata(){var i;let e=this.getMetaKey(),t=await I.default.storage.local.get(e),n={version:this.config.version,createdAt:((i=t[e])==null?void 0:i.createdAt)??Date.now(),lastAccessed:Date.now()};await I.default.storage.local.set({[e]:n})}};async function Me(o){let{keepStores:e,dryRun:t=!1}=o,[n,i]=await Promise.all([I.default.storage.local.get(null),I.default.storage.session.get(null)]),r=new Set([...Object.keys(n),...Object.keys(i)]),s=[];for(let c of r){if(!c.startsWith("crann:"))continue;let a=c.match(/^crann:([^:]+):/);if(!a)continue;let g=a[1];e.includes(g)||s.push(c)}return!t&&s.length>0&&await Promise.all([I.default.storage.local.remove(s),I.default.storage.session.remove(s)]),{keys:s,count:s.length}}var Se=class{constructor(){this.agents=new Map}add(e){let t={id:e.id,tabId:e.location.tabId,frameId:e.location.frameId,context:e.location.context,connectedAt:Date.now()};return this.agents.set(e.id,t),t}remove(e){return this.agents.delete(e)}get(e){return this.agents.get(e)}getAll(){return Array.from(this.agents.values())}query(e){return this.getAll().filter(t=>!(e.context!==void 0&&t.context!==e.context||e.tabId!==void 0&&t.tabId!==e.tabId||e.frameId!==void 0&&t.frameId!==e.frameId))}has(e){return this.agents.has(e)}get size(){return this.agents.size}clear(){this.agents.clear()}};G();var ve=class{constructor(e,t,n){this.config=e,this.getState=t,this.setState=n}async execute(e,t,n){let i=this.config.actions;if(!i)throw new O(e,this.config.name,"No actions defined");let r=i[e];if(!r)throw new O(e,this.config.name,"Unknown action");if(r.validate)try{r.validate(...t)}catch(c){throw new H(e,this.config.name,c instanceof Error?c.message:String(c))}let s={state:this.getState(),setState:async c=>{await this.setState(c,n.id)},agentId:n.id,agentLocation:n.location};try{return await r.handler(s,...t)}catch(c){throw new O(e,this.config.name,c instanceof Error?c.message:String(c))}}hasAction(e){var t;return((t=this.config.actions)==null?void 0:t[e])!==void 0}getActionNames(){return Object.keys(this.config.actions??{})}};G();var W=class{constructor(e,t={}){this.stateChangeListeners=new Set;this.agentConnectListeners=new Set;this.agentDisconnectListeners=new Set;this.isDestroyed=!1;this.config=e,this.options=t,this.porter=V(e.name,{debug:t.debug??!1}),this.persistence=new ye(e,t),this.stateManager=new me(e,this.persistence),this.agentRegistry=new Se,this.actionExecutor=new ve(e,()=>this.getState(),(n,i)=>this.setState(n,i)),this.setupMessageHandlers(),this.hydrate()}getState(){return this.assertNotDestroyed(),this.stateManager.getState()}getAgentState(e){return this.assertNotDestroyed(),this.stateManager.getAgentState(e)}async setState(e,t){this.assertNotDestroyed();let{sharedChanges:n,agentChanges:i}=this.stateManager.setState(e,t);Object.keys(n).length>0&&await this.persistence.persist(n),this.notifyStateChange(e,t)}async setAgentState(e,t){this.assertNotDestroyed(),this.stateManager.setAgentState(e,t),this.notifyStateChange(t,e)}async clear(){this.assertNotDestroyed(),this.stateManager.clear(),await this.persistence.clearAll(),this.notifyStateChange({})}subscribe(e,t){this.assertNotDestroyed();let n,i;typeof e=="function"?i=e:(n=e,i=t);let r={keys:n,callback:i};return this.stateChangeListeners.add(r),()=>{this.stateChangeListeners.delete(r)}}onAgentConnect(e){return this.assertNotDestroyed(),this.agentConnectListeners.add(e),()=>{this.agentConnectListeners.delete(e)}}onAgentDisconnect(e){return this.assertNotDestroyed(),this.agentDisconnectListeners.add(e),()=>{this.agentDisconnectListeners.delete(e)}}getAgents(e){return this.assertNotDestroyed(),e?this.porter.queryAgents(e).map(n=>this.agentRegistry.get(n.info.id)).filter(n=>n!==void 0):this.agentRegistry.getAll()}destroy(e){this.isDestroyed||(this.isDestroyed=!0,this.stateChangeListeners.clear(),this.agentConnectListeners.clear(),this.agentDisconnectListeners.clear(),this.agentRegistry.clear(),e!=null&&e.clearPersisted&&this.persistence.clearAll())}setupMessageHandlers(){let e=new Set;this.porter.on({setState:(t,n)=>{n&&this.setState(t.payload.state,n.id)}}),this.porter.onMessagesSet(t=>{if(!t||e.has(t.id))return;e.add(t.id);let n=this.stateManager.getFullStateForAgent(t.id);this.porter.post({action:"initialState",payload:{state:n,info:t}},t.location)}),this.porter.onConnect(t=>{if(!t)return;let n=this.agentRegistry.add(t);this.stateManager.initializeAgentState(t.id),this.agentConnectListeners.forEach(i=>i(n))}),this.porter.onDisconnect(t=>{if(!t)return;let n=this.agentRegistry.get(t.id);n&&(this.agentRegistry.remove(t.id),this.stateManager.removeAgentState(t.id),e.delete(t.id),this.agentDisconnectListeners.forEach(i=>i(n)))}),this.setupRpcHandlers()}setupRpcHandlers(){this.porter.on({rpc:async(e,t)=>{if(!t)return;let{callId:n,actionName:i,args:r}=e.payload;try{let s=await this.actionExecutor.execute(i,r,t);this.porter.post({action:"rpcResult",payload:{callId:n,actionName:i,result:s,success:!0}},t.location)}catch(s){this.porter.post({action:"rpcResult",payload:{callId:n,actionName:i,error:s instanceof Error?s.message:String(s),success:!1}},t.location)}}})}async hydrate(){let e=await this.persistence.hydrate();Object.keys(e).length>0&&this.stateManager.hydrateSharedState(e)}notifyStateChange(e,t){var r;let n=this.getState(),i=t?(r=this.porter.getAgentById(t))==null?void 0:r.info:void 0;if(this.stateChangeListeners.forEach(s=>{if(!(s.keys&&!s.keys.some(a=>a in e)))try{s.callback(n,e,i)}catch(c){console.error("[Crann Store] Error in subscriber callback:",c)}}),t){let s=this.porter.getAgentById(t);s!=null&&s.info.location&&this.porter.post({action:"stateUpdate",payload:{state:e}},s.info.location)}else for(let s of this.agentRegistry.getAll())this.porter.post({action:"stateUpdate",payload:{state:e}},s.id)}assertNotDestroyed(){if(this.isDestroyed)throw new L(this.config.name,"store")}};function Pe(o,e){return new W(o,e)}G();var X=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,i;typeof e=="function"?i=e:(n=e,i=t);let r={keys:n,callback:i};return this.subscribers.add(r),()=>{this.subscribers.delete(r)}}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(i=>{try{i(this._state)}catch(r){console.error("[Crann Agent] Error in onReady callback:",r)}}),this.notifySubscribers(t)},stateUpdate:e=>{let{state:t}=e.payload;this._state={...this._state,...t},this.notifySubscribers(t)}}),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(i=>i 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:i,result:r,error:s,success:c}=n.payload,a=e.get(i);a&&(e.delete(i),c?a.resolve(r):a.reject(new Error(s)))}}),new Proxy({},{get:(n,i)=>async(...r)=>{let s=`${t++}`;return new Promise((c,a)=>{e.set(s,{resolve:c,reject:a}),this.porter.post({action:"rpc",payload:{callId:s,actionName:i,args:r}})})}})}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 L(this.config.name,"agent")}};function Ee(o,e){return new X(o,e)}G();var se=Z(require("webextension-polyfill"));var ne={Instance:"instance",Service:"service"},Qe={Session:"session",Local:"local",None:"none"},ie=o=>!("handler"in o),Ce=o=>"handler"in o;var oe=class oe{static setDebug(e){oe._debug=e}static isDebugEnabled(){return oe._debug}};oe._debug=!1;var re=oe;function Ge(){return re.isDebugEnabled()}function ke(o,e,t){let n=t.value;return t.value=function(...i){var r;if(Ge()){let s=new Error().stack,a=(r=((s==null?void 0:s.split(`
`))||[])[3])==null?void 0:r.match(/at\s+(\S+)\s+/),g=a?a[1]:"unknown",d=i.length>1?i[1]:void 0,p={source:g,timestamp:Date.now(),instanceKey:i[0],changes:d};console.log("Crann State Change:",p)}return n.apply(this,i)},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",i="color: #d58cff; font-weight: bold",r="";return A.debug?{debug:console.log.bind(console,t,n,r,i,r),log:console.log.bind(console,t,n,r,i,r),info:console.info.bind(console,t,n,r,i,r),warn:console.warn.bind(console,t,n,r,i,r),error:console.error.bind(console,t,n,r,i,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 x=A;function T(o,e={terse:!0}){let n=(i=>{let r=String(i);return r.length>=4?r.slice(-4):r.padStart(4,"0")})(o.location.tabId);return e!=null&&e.terse?`${n}:${o.location.frameId}`:`${o.location.context}:${n}:${o.location.frameId}`}function We(o,e,t,n,i){var d,p;let r=new Map,s=new Map,c=(d=o.context)!=null&&d.isServiceWorker?"Core":"Agent",a=x.forContext(`${c}:RPC`);return(p=o.context)!=null&&p.agentInfo&&a.setTag(T(o.context.agentInfo)),o.addEventListener("message",m=>{a.debug("Message received:",m);let[f,y]=m.data;if("call"in y&&"args"in y.call){a.debug("Processing call message:",y);let C=y.call,{id:u,args:k,target:b}=C,j=t[u];if(!j){o.postMessage([f,{error:{id:u,error:"Action not found",target:b}}]);return}try{if(j.validate&&j.validate(...k),!b){o.postMessage([f,{error:{id:u,error:"No target provided for action call",target:b}}]);return}let U=e();a.debug("Executing action with most current state:",U),Promise.resolve(j.handler(U,n,b,...k)).then(F=>{a.debug("Action handler result:",{result:F,target:b}),o.postMessage([f,{result:{id:u,result:F,target:b}}])},F=>{o.postMessage([f,{error:{id:u,error:F.message,target:b}}])})}catch(U){U instanceof Error?o.postMessage([f,{error:{id:u,error:U.message,target:b}}]):o.postMessage([f,{error:{id:u,error:"Unknown error occurred",target:b}}])}}else if("result"in y){let C=y.result,u=r.get(f);u&&(u(C.result),r.delete(f))}else if("error"in y){let C=y.error,u=r.get(f);u&&(u(Promise.reject(new Error(C.error))),r.delete(f))}else if("release"in y){let C=y.release,u=s.get(C.id);u&&(u.clear(),s.delete(C.id))}}),new Proxy({},{get(m,f){return(...y)=>{let C=Math.random();return new Promise((u,k)=>{r.set(C,b=>{b instanceof Promise?b.then(u,k):u(b)}),o.postMessage([C,{call:{id:f,args:y}}])})}}})}function be(o,e,t,n){let i=t||V("crann"),r=i.type!=="agent",s=x.forContext(r?"Core:RPC":"Agent:RPC"),c={postMessage:(a,g)=>{if(r){s.debug("Posting message from service worker:",{message:a,transferables:g});let[,d]=a,p=lt(d);if(!p){s.warn("No target specified for RPC response in service worker");return}i.post({action:"rpc",payload:{message:a,transferables:g||[]}},p)}else{let d=i.getAgentInfo();if(!d){s.warn("No agent info found for posting message",{agentInfo:d});return}let p=T(d),[,m]=a;"call"in m&&(m.call.target=d==null?void 0:d.location),s.withTag(p).debug("Sending RPC message from agent:",{rpcPayload:m,message:a}),i.post({action:"rpc",payload:{message:a,transferables:g||[]}})}},addEventListener:(a,g)=>{i.on({rpc:(d,p)=>{try{if(!p)s.debug("RPC message received:",{message:d,event:a});else{let u=T(p);s.withTag(u).debug("RPC message received:",{message:d,event:a})}let{payload:m}=d,{message:f,transferables:y=[]}=m,C=new MessageEvent("message",{data:f,ports:y.filter(u=>u instanceof MessagePort)||[]});g(C)}catch(m){s.error("Failed to parse RPC message payload:",m)}}})},removeEventListener:()=>{},context:{isServiceWorker:r,agentInfo:r?void 0:i.getAgentInfo()}};return We(c,o,e,n)}function lt(o){if("result"in o)return o.result.target;if("error"in o)return o.error.target;if("call"in o)return o.call.target;if("release"in o)return o.release.target}var q=class q{constructor(e,t){this.config=e;this.instances=new Map;this.stateChangeListeners=[];this.instanceReadyListeners=[];this.storagePrefix="crann_";this.porter=V("crann",{debug:!1});t!=null&&t.debug&&(re.setDebug(!0),x.setDebug(!0)),this.storagePrefix=(t==null?void 0:t.storagePrefix)??this.storagePrefix,this.logger=x.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,g)=>{if(!g){this.logger.warn("setState message heard from unknown agent");return}let d=T(g);this.logger.withTag(d).log("Setting state:",a),this.set(a.payload.state,g.id)}});let n=new Set;this.porter.onMessagesSet(a=>{if(!a){this.logger.error("Messages set but no agent info.",{info:a});return}let g=T(a);if(this.logger.withTag(g).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(g).log("Already sent initialState to agent, skipping:",a.id);return}n.add(a.id),this.logger.withTag(g).log("Messages set received. Sending initial state.",{info:a});let d=this.get(a.id);this.porter.post({action:"initialState",payload:{state:d,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 g=T(a);this.logger.withTag(g).log("Agent connected",{info:a}),this.addInstance(a.id,g),this.porter.onDisconnect(d=>{this.logger.withTag(T(d)).log("Agent disconnect heard. Connection type, context and location:",{info:d}),this.removeInstance(d.id)})});let r=(a,g)=>g!==void 0?this.set(a,g):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=be(c,s,this.porter,r)}static getInstance(e,t){return q.instance?t!=null&&t.debug&&x.forContext("Core").log("Instance requested and already existed, returning"):q.instance=new q(e,t),q.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};K(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,i={...n,...t};K(n,i)?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,i),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 se.default.storage.session.set({[this.storagePrefix+n]:s}),t=!0;break;case"local":await se.default.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,i=t?this.get(t):this.get();this.stateChangeListeners.length>0&&(this.logger.log("Notifying state change listeners in source"),this.stateChangeListeners.forEach(r=>{r(i,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,i]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={},i={};for(let r in e){let s=this.config[r];if(dt(s)){if(s.partition==="instance"){let c=r,a=e;n[c]=a[c]}else if(!s.partition||s.partition===ne.Service){let c=r,a=e;i[c]=a[c]}}}t&&Object.keys(n).length>0&&(this.logger.withTag(t).log("Setting instance state:",n),this.setInstanceState(t,n)),Object.keys(i).length>0&&(this.logger.log("Setting service state:",i),this.setServiceState(i))}async hydrate(){this.logger.log("Hydrating state from storage.");let e=await se.default.storage.local.get(null),t=await se.default.storage.session.get(null),n={...e,...t};this.logger.log("Storage data is:",{local:e,session:t,combined:n});let i={},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 a=n[s];this.logger.log(`Found storage value for ${c}:`,a),i[c]=a,r=!0}}r?this.logger.log("Hydrated some items."):this.logger.log("No items found in storage."),this.serviceState={...this.defaultServiceState,...i}}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];ie(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),ie(n)&&(!n.partition||n.partition===ne.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 i=this.porter.getAgentById(n);i!=null&&i.info&&e(n,i.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(i=>{i(e,t)})}}extractActions(e){return Object.entries(e).filter(([t,n])=>Ce(n)).reduce((t,[n,i])=>{let r=i;return{...t,[n]:r}},{})}};q.instance=null,Ae([ke],q.prototype,"setServiceState",1),Ae([ke],q.prototype,"setInstanceState",1);var ae=q;function Xe(o,e){let t=ae.getInstance(o,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 dt(o){return o&&typeof o=="object"&&"default"in o}var E={connected:!1},z=null,Re=new Set,Le=new Set;function Ze(o,e){let t=(e==null?void 0:e.debug)||!1,n=e==null?void 0:e.context;t&&x.setDebug(!0);let i=x.forContext("Agent"),r,s="unset",c=new Set;if(i.log("Initializing Crann Agent"+(n?` with context: ${n}`:"")),z&&E.connected)return i.log("We had an instance already and it's connected, returning"),i.log("Connect, calling onReady callback"),setTimeout(()=>{c.forEach(l=>l(E))},0),z;z&&!E.connected&&(i.log("We had an instance but it's disconnected, creating new connection"),z=null),i.log("No existing instance, creating a new one");let a=Q({namespace:"crann",debug:!1});i.log("Porter connection created"),a.onDisconnect(()=>{i.log("Porter connection lost, updating connection status"),E={connected:!1},Re.forEach(l=>{try{l()}catch(h){i.error("Error in disconnect callback:",h)}}),c.forEach(l=>{try{l(E)}catch(h){i.error("Error in onReady callback during disconnect:",h)}})}),a.onReconnect(l=>{i.log("Porter reconnected, updating connection status",l),E={connected:!0,agent:l},r=l,s=T(l),i.setTag(s),Le.forEach(h=>{try{h(l)}catch(w){i.error("Error in reconnect callback:",w)}}),c.forEach(h=>{try{h(E)}catch(w){i.error("Error in onReady callback during reconnect:",w)}})});let g=Object.entries(o).filter(([l,h])=>Ce(h)).reduce((l,[h,w])=>{let _=w;return{...l,[h]:{type:"action",handler:_.handler,validate:_.validate}}},{}),d=be(()=>Je(o),g,a),p=!1;a.on({initialState:l=>{if(i.log("initialState received",{alreadyReceived:p,payload:l.payload}),p){i.log("Ignoring duplicate initialState message");return}p=!0,m=l.payload.state,r=l.payload.info,s=T(r),E={connected:!0,agent:r},i.setTag(s),i.log(`Initial state received and ${y.size} listeners notified`,{message:l}),c.forEach(h=>{i.log("Calling onReady callbacks"),h(E)}),y.forEach(h=>{h.callback(m)})},stateUpdate:l=>{f=l.payload.state,m={...m,...f},i.log("State updated:",{message:l,changes:f,_state:m}),f&&y.forEach(h=>{(h.keys===void 0||h.keys.some(_=>_ in f))&&h.callback(f)})}}),i.log("Porter connected. Setting up state and listeners");let m=Je(o),f=null,y=new Set;i.log("Completed setup, returning instance");let C=()=>m,u=l=>{i.log("Calling post with setState",l),a.post({action:"setState",payload:{state:l}})},k=(l,h)=>{let w={keys:h,callback:l};return y.add(w),()=>{y.delete(w)}};return z={useCrann:l=>{let h=()=>C()[l],w=ce=>u({[l]:ce}),_=ce=>{let Ne=h();return k(et=>{if(l in et){let $e=h(),tt=C();ce({current:$e,previous:Ne,state:tt}),Ne=$e}},[l])};return[h(),w,_]},get:C,set:u,subscribe:k,getAgentInfo:()=>r,onReady:l=>(i.log("onReady callback added"),c.add(l),E.connected&&(i.log("calling onReady callback"),setTimeout(()=>{l(E)},0)),()=>c.delete(l)),callAction:async(l,...h)=>(i.log("Calling action",l,h),d[l](...h)),onDisconnect:l=>(i.log("onDisconnect callback added"),Re.add(l),()=>{Re.delete(l)}),onReconnect:l=>(i.log("onReconnect callback added"),Le.add(l),()=>{Le.delete(l)})},z}function Ye(){return z!==null}function Je(o){let e={};return Object.keys(o).forEach(t=>{let n=o[t];ie(n)&&(e[t]=n.default)}),e}0&&(module.exports={ActionError,Agent,ConfigError,Crann,CrannError,LifecycleError,Partition,Persist,Persistence,Scope,Store,ValidationError,clearOrphanedData,connect,connectStore,connected,create,createConfig,createStore});
//# sourceMappingURL=index.js.map