crann 2.0.1 → 2.0.3

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 CHANGED
@@ -36,13 +36,13 @@
36
36
  import { createConfig, Persist } from "crann";
37
37
 
38
38
  export const config = createConfig({
39
- name: "myExtension", // Required: unique store name
40
- version: 1, // Optional: for migrations
41
-
39
+ name: "myExtension", // Required: unique store name
40
+ version: 1, // Optional: for migrations
41
+
42
42
  // Define your state
43
43
  isEnabled: { default: false },
44
44
  count: { default: 0, persist: Persist.Local },
45
-
45
+
46
46
  // Define actions (RPC)
47
47
  actions: {
48
48
  increment: {
@@ -79,10 +79,10 @@ const agent = connectStore(config);
79
79
 
80
80
  agent.onReady(() => {
81
81
  console.log("Connected! Current state:", agent.getState());
82
-
82
+
83
83
  // Update state
84
84
  agent.setState({ isEnabled: true });
85
-
85
+
86
86
  // Call actions
87
87
  agent.actions.increment(5);
88
88
  });
@@ -95,21 +95,18 @@ agent.onReady(() => {
95
95
  import { createCrannHooks } from "crann/react";
96
96
  import { config } from "./config";
97
97
 
98
- export const { useCrannState, useCrannActions, useCrannReady } = createCrannHooks(config);
98
+ export const { useCrannState, useCrannActions, useCrannReady } =
99
+ createCrannHooks(config);
99
100
 
100
101
  // Counter.tsx
101
102
  function Counter() {
102
- const count = useCrannState(s => s.count);
103
+ const count = useCrannState((s) => s.count);
103
104
  const { increment } = useCrannActions();
104
105
  const isReady = useCrannReady();
105
106
 
106
107
  if (!isReady) return <div>Loading...</div>;
107
108
 
108
- return (
109
- <button onClick={() => increment(1)}>
110
- Count: {count}
111
- </button>
112
- );
109
+ return <button onClick={() => increment(1)}>Count: {count}</button>;
113
110
  }
114
111
  ```
115
112
 
@@ -123,25 +120,25 @@ import { createConfig, Scope, Persist } from "crann";
123
120
  const config = createConfig({
124
121
  // Required: unique identifier for this store
125
122
  name: "myStore",
126
-
123
+
127
124
  // Optional: version number for migrations (default: 1)
128
125
  version: 1,
129
-
126
+
130
127
  // State definitions
131
128
  count: { default: 0 },
132
-
129
+
133
130
  // With persistence
134
- theme: {
131
+ theme: {
135
132
  default: "light" as "light" | "dark",
136
- persist: Persist.Local, // Persist.Local | Persist.Session | Persist.None
133
+ persist: Persist.Local, // Persist.Local | Persist.Session | Persist.None
137
134
  },
138
-
135
+
139
136
  // Agent-scoped state (each tab/frame gets its own copy)
140
137
  selectedElement: {
141
138
  default: null as HTMLElement | null,
142
- scope: Scope.Agent, // Scope.Shared (default) | Scope.Agent
139
+ scope: Scope.Agent, // Scope.Shared (default) | Scope.Agent
143
140
  },
144
-
141
+
145
142
  // Actions (RPC handlers)
146
143
  actions: {
147
144
  doSomething: {
@@ -167,7 +164,7 @@ The Store runs in the service worker and manages all state:
167
164
  import { createStore } from "crann";
168
165
 
169
166
  const store = createStore(config, {
170
- debug: true, // Enable debug logging
167
+ debug: true, // Enable debug logging
171
168
  });
172
169
 
173
170
  // Get current state
@@ -275,8 +272,8 @@ Two patterns for reading state:
275
272
 
276
273
  ```typescript
277
274
  // Selector pattern - returns selected value
278
- const count = useCrannState(s => s.count);
279
- const theme = useCrannState(s => s.settings.theme);
275
+ const count = useCrannState((s) => s.count);
276
+ const theme = useCrannState((s) => s.settings.theme);
280
277
 
281
278
  // Key pattern - returns [value, setValue] tuple
282
279
  const [count, setCount] = useCrannState("count");
@@ -331,20 +328,20 @@ Actions execute in the service worker but can be called from any context:
331
328
  const config = createConfig({
332
329
  name: "myStore",
333
330
  count: { default: 0 },
334
-
331
+
335
332
  actions: {
336
333
  increment: {
337
334
  handler: async (ctx, amount: number = 1) => {
338
335
  const newCount = ctx.state.count + amount;
339
336
  // Option 1: Return state updates
340
337
  return { count: newCount };
341
-
338
+
342
339
  // Option 2: Use ctx.setState
343
340
  // await ctx.setState({ count: newCount });
344
341
  // return { success: true };
345
342
  },
346
343
  },
347
-
344
+
348
345
  fetchUser: {
349
346
  handler: async (ctx, userId: string) => {
350
347
  // Runs in service worker - can make network requests
@@ -376,10 +373,10 @@ Action handlers receive a context object:
376
373
 
377
374
  ```typescript
378
375
  interface ActionContext<TState> {
379
- state: TState; // Current state snapshot
380
- setState: (partial: Partial<TState>) => Promise<void>; // Update state
381
- agentId: string; // Calling agent's ID
382
- agentLocation: BrowserLocation; // Tab/frame info
376
+ state: TState; // Current state snapshot
377
+ setState: (partial: Partial<TState>) => Promise<void>; // Update state
378
+ agentId: string; // Calling agent's ID
379
+ agentLocation: BrowserLocation; // Tab/frame info
383
380
  }
384
381
  ```
385
382
 
@@ -392,16 +389,16 @@ import { createConfig, Persist } from "crann";
392
389
 
393
390
  const config = createConfig({
394
391
  name: "myStore",
395
-
392
+
396
393
  // No persistence (default) - resets on service worker restart
397
394
  volatile: { default: null },
398
-
395
+
399
396
  // Local storage - persists across browser sessions
400
- preferences: {
397
+ preferences: {
401
398
  default: { theme: "light" },
402
399
  persist: Persist.Local,
403
400
  },
404
-
401
+
405
402
  // Session storage - persists until browser closes
406
403
  sessionData: {
407
404
  default: {},
@@ -420,16 +417,16 @@ This prevents collisions and enables clean migrations.
420
417
 
421
418
  ### Key Changes
422
419
 
423
- | v1 | v2 |
424
- |----|----|
425
- | `create()` | `createStore()` |
426
- | `connect()` | `connectStore()` |
427
- | `Partition.Instance` | `Scope.Agent` |
428
- | `Partition.Service` | `Scope.Shared` |
429
- | `crann.set()` | `store.setState()` |
430
- | `crann.get()` | `store.getState()` |
420
+ | v1 | v2 |
421
+ | ------------------------- | ------------------------- |
422
+ | `create()` | `createStore()` |
423
+ | `connect()` | `connectStore()` |
424
+ | `Partition.Instance` | `Scope.Agent` |
425
+ | `Partition.Service` | `Scope.Shared` |
426
+ | `crann.set()` | `store.setState()` |
427
+ | `crann.get()` | `store.getState()` |
431
428
  | `callAction("name", arg)` | `agent.actions.name(arg)` |
432
- | Config object literal | `createConfig()` |
429
+ | Config object literal | `createConfig()` |
433
430
 
434
431
  ### Migration Steps
435
432
 
@@ -443,7 +440,7 @@ const crann = create({
443
440
 
444
441
  // After (v2)
445
442
  const config = createConfig({
446
- name: "myStore", // Required in v2
443
+ name: "myStore", // Required in v2
447
444
  count: { default: 0 },
448
445
  });
449
446
 
@@ -454,10 +451,10 @@ const store = createStore(config);
454
451
 
455
452
  ```typescript
456
453
  // Before (v1)
457
- partition: Partition.Instance
454
+ partition: Partition.Instance;
458
455
 
459
456
  // After (v2)
460
- scope: Scope.Agent
457
+ scope: Scope.Agent;
461
458
  ```
462
459
 
463
460
  3. **Update React hooks:**
@@ -484,16 +481,25 @@ await agent.actions.increment(5);
484
481
 
485
482
  ## Why Crann?
486
483
 
487
- Browser extensions have multiple isolated contexts that need to share state:
484
+ Browser extensions have multiple isolated contexts (content scripts, popup, devtools, sidepanel) that need to share state. The traditional approach using `sendMessage`/`onMessage` forces a painful pattern:
485
+
486
+ [![Message Router Problem](https://mermaid.ink/img/pako:eNp9k2tvmzAUhv-KZSlikyjCEBLgwyJK2FapbSJwVbVhihi4BDXYyJi2WZT_PodcmtvmT4f3PK998DlewpRlBLqw01kWtBAuWCpiRkqiuEDJEv6qrFadTkxf5uw9nSVcgNswpkCuuvmd86SaAS8nVNSTGLYB8BkV5EPUMfy1AdfLj9CkTUggSnlRCYCO0sZp2jhIj0eTMaua6kAa4smQvAnG5vVWJTSL6Ulp0aMsKyL8rUgJeGT8lfCjsrKCk1QUjAJ8_amGowcchNJ5R-o6yQkIWSNOnJ_RwWE32P8pbfV7IdLZl7LONbGoyNcj47-PbS8CSX-a1AQoPwI8jbCHA-XM7xt7KvoPZe6pMLj1nqZ4NMXe9QWwuwe_B_IXpkMPexcwa489REE49Xx8M7q_wPUkp2naeeJ-t8FgMDjKtr07aaIcGXB19W3bjZ1mnGnj0Zk0xGfSJm7lTZ-gCkvCy6TI5PAv11AM28GPoSvD9ejHMKYrySWNYNGCptAVvCEq5KzJZ9B9Sea1_GqqLBFkWCRyCsq9WiX0mbFyZ5Gf0F3CD-gamqMjByHLNsyurfdtU4UL6CK9r3Wdfh_pes80bKuPVir80-6ga47joC6ykWEaXcswbRXmfF339iwub41wnzVUQLdnWSokWSEYv9u87PaBr_4CI3YYvA?type=png)](https://mermaid.live/edit#pako:eNp9k2tvmzAUhv-KZSlikyjCEBLgwyJK2FapbSJwVbVhihi4BDXYyJi2WZT_PodcmtvmT4f3PK998DlewpRlBLqw01kWtBAuWCpiRkqiuEDJEv6qrFadTkxf5uw9nSVcgNswpkCuuvmd86SaAS8nVNSTGLYB8BkV5EPUMfy1AdfLj9CkTUggSnlRCYCO0sZp2jhIj0eTMaua6kAa4smQvAnG5vVWJTSL6Ulp0aMsKyL8rUgJeGT8lfCjsrKCk1QUjAJ8_amGowcchNJ5R-o6yQkIWSNOnJ_RwWE32P8pbfV7IdLZl7LONbGoyNcj47-PbS8CSX-a1AQoPwI8jbCHA-XM7xt7KvoPZe6pMLj1nqZ4NMXe9QWwuwe_B_IXpkMPexcwa489REE49Xx8M7q_wPUkp2naeeJ-t8FgMDjKtr07aaIcGXB19W3bjZ1mnGnj0Zk0xGfSJm7lTZ-gCkvCy6TI5PAv11AM28GPoSvD9ejHMKYrySWNYNGCptAVvCEq5KzJZ9B9Sea1_GqqLBFkWCRyCsq9WiX0mbFyZ5Gf0F3CD-gamqMjByHLNsyurfdtU4UL6CK9r3Wdfh_pes80bKuPVir80-6ga47joC6ykWEaXcswbRXmfF339iwub41wnzVUQLdnWSokWSEYv9u87PaBr_4CI3YYvA)
487
+
488
+ **The problem with `sendMessage` / `onMessage`:**
488
489
 
489
- - **Service Worker** - Background logic and events
490
- - **Content Scripts** - Injected into web pages
491
- - **Popup** - Extension icon click UI
492
- - **Side Panels, DevTools** - Other specialized contexts
490
+ - Agents can't message each other directly—everything routes through the service worker
491
+ - Your service worker becomes a message router with growing `switch/case` statements
492
+ - Every new feature means more message types, more handlers, more coupling
493
+ - Manual async handling (`return true` in Chrome, different in Firefox)
494
+ - Hand-rolled TypeScript types that may or may not stay in sync
493
495
 
494
- Traditionally, this requires complex `chrome.runtime.sendMessage` / `onMessage` patterns. Crann eliminates this boilerplate by providing a central state hub that all contexts can connect to.
496
+ **With Crann:**
495
497
 
496
- ![Traditional vs Crann Architecture](img/with_crann.png)
498
+ - Define your state and actions in one place
499
+ - Agents sync automatically through the central store
500
+ - Full TypeScript inference—no manual type definitions
501
+ - No message routing, no relay logic, no `return true`
502
+ - Focus on your features, not the plumbing
497
503
 
498
504
  ---
499
505
 
package/dist/cjs/index.js CHANGED
@@ -1,3 +1,3 @@
1
- "use strict";var Oe=Object.create;var B=Object.defineProperty;var be=Object.getOwnPropertyDescriptor;var Ne=Object.getOwnPropertyNames;var qe=Object.getPrototypeOf,Ue=Object.prototype.hasOwnProperty;var Be=(i,e)=>{for(var t in e)B(i,t,{get:e[t],enumerable:!0})},Ce=(i,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let o of Ne(e))!Ue.call(i,o)&&o!==t&&B(i,o,{get:()=>e[o],enumerable:!(n=be(e,o))||n.enumerable});return i};var X=(i,e,t)=>(t=i!=null?Oe(qe(i)):{},Ce(e||!i||!i.__esModule?B(t,"default",{value:i,enumerable:!0}):t,i)),Fe=i=>Ce(B({},"__esModule",{value:!0}),i),ge=(i,e,t,n)=>{for(var o=n>1?void 0:n?be(e,t):e,r=i.length-1,a;r>=0;r--)(a=i[r])&&(o=(n?a(e,t,o):a(o))||o);return n&&o&&B(e,t,o),o};var Qe={};Be(Qe,{Crann:()=>G,Partition:()=>F,Persistence:()=>ve,connect:()=>ae,connected:()=>Ee,create:()=>Pe,createConfig:()=>Le,createCrannStateHook:()=>De});module.exports=Fe(Qe);var j=X(require("webextension-polyfill"));var F={Instance:"instance",Service:"service"},ve={Session:"session",Local:"local",None:"none"},$=i=>!("handler"in i),J=i=>"handler"in i;var Te=X(require("webextension-polyfill"));var K=(g=>(g.ContentScript="contentscript",g.Extension="extension",g.Popup="popup",g.Sidepanel="sidepanel",g.Devtools="devtools",g.Options="options",g.Unknown="unknown",g))(K||{});var C=class extends Error{constructor(t,n,o){super(n);this.type=t;this.details=o;this.name="PorterError"}};function ye(){return typeof ServiceWorkerGlobalScope<"u"&&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<"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){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 P=y;var Se=X(require("webextension-polyfill")),Ae=require("uuid");var _=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,a=n.frameId||0;this.logger.debug("Determined context for new agent",{determinedContext:o,tabId:r,frameId:a});let g=Array.from(this.agentsInfo.values()).filter(f=>f.location.context===o&&f.location.tabId===r&&f.location.frameId===a);g.length>0&&this.logger.debug("Adding agent: Found existing similar agent.",{tabAgentsInfo:g});let s=((p=(u=this.getAgentByLocation({context:o,tabId:r,frameId:a}))==null?void 0:u.info)==null?void 0:p.id)||(0,Ae.v4)();this.logger.debug(`Adding agent with id: ${s}`),this.agents.set(s,e);let c={id:s,location:{context:o,tabId:r,frameId:a},createdAt:Date.now(),lastActiveAt:Date.now()};this.agentsInfo.set(s,c),this.logger.debug(`Constructed agent info: ${JSON.stringify(c)}`),e.onMessage.addListener(f=>this.emit("agentMessage",f,c));let d={port:e,info:c};return e.onDisconnect.addListener(()=>{this.emit("agentDisconnect",c),this.logger.debug("Agent disconnected, removing from manager. ",{agentInfo:c}),this.removeAgent(s)}),this.emit("agentSetup",d),this.logger.debug("Setup complete for adding agent. ",{agentInfo:c}),s}getAgentByLocation(e){let{context:t,tabId:n,frameId:o}=e,r=Array.from(this.agentsInfo.entries()).find(([c,d])=>d.location.context===t&&d.location.tabId===n&&d.location.frameId===o);if(r===void 0)return this.logger.error("No agent found for location. ",{location:e}),null;let a=r[0],g=this.agents.get(a),s=this.agentsInfo.get(a);return!g||!s?(this.logger.error("No agent found for location. ",{location:e}),null):{port:g,info:s}}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,a=e.tabId?o.location.tabId===e.tabId:!0,g=e.frameId?o.location.frameId===e.frameId:!0;return r&&a&&g}).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,M,S;let t=e.sender;if(!t)return this.logger.error("Cannot add agent that did not have a sender"),null;let n=Se.default.runtime.getManifest(),o=((p=n==null?void 0:n.side_panel)==null?void 0:p.default_path)||"",r=n.options_page||"",a=((f=n.action)==null?void 0:f.default_popup)||"",g=n.devtools_page||"",s=((b=n.chrome_url_overrides)==null?void 0:b.newtab)||"",c=((v=n.chrome_url_overrides)==null?void 0:v.bookmarks)||"",d=((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:a?a.split("/").pop():"popup.html",devtools:g?g.split("/").pop():"devtools.html",newtab:s?s.split("/").pop():"newtab.html",bookmarks:c?c.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 R=new URL(t.url).pathname.split("/").pop();for(let[O,he]of Object.entries(u))if(R===he)return{context:O,tabId:((M=t.tab)==null?void 0:M.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 Y=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 ee=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 a;if(!o||!o.id)return;let r=(a=this.agentOperations.getAgentById(o.id))==null?void 0:a.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 a=new Error("Message posting timed out");this.logger.error("Post timeout:",a),o(a)},5e3);t===void 0?this.broadcastMessage(e):$e(t)?this.postToLocation(e,t):Ke(t)?this.postToContext(e,t):typeof t=="string"?this.postToId(e,t):this.postToTab(e,t),clearTimeout(r),n()}catch(r){let a=r instanceof Error?r.message:"Unknown error";this.logger.error("Failed to post message:",a),o(new Error(`Failed to post message: ${a}`))}})}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:a,...g}=o;r(a,g)}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 $e(i){return typeof i=="object"&&i!==null&&"context"in i&&"tabId"in i&&"frameId"in i}function Ke(i){return typeof i=="string"&&Object.values(K).includes(i)}var D=class D{constructor(e,t){if((t==null?void 0:t.debug)!==void 0&&P.configure({enabled:t.debug}),this.logger=P.getLogger("SW"),this.namespace=e||"porter",e||this.logger.error('No namespace provided, defaulting to "porter"'),this.agentManager=new _(this.logger),this.messageHandler=new ee(this.agentManager,this.logger),this.connectionManager=new Y(this.agentManager,this.namespace,this.logger),this.logger.info(`Constructing Porter with namespace: ${this.namespace}`),!ye())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)}),Te.default.runtime.onConnect.addListener(this.connectionManager.handleConnection.bind(this.connectionManager))}static getInstance(e="porter",t){return D.staticLogger.debug(`Getting instance for namespace: ${e}`),D.instances.has(e)?(t==null?void 0:t.debug)!==void 0&&P.configure({enabled:t.debug}):(D.staticLogger.info(`Creating new instance for namespace: ${e}`),D.instances.set(e,new D(e,t))),D.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)}};D.instances=new Map,D.staticLogger=P.getLogger("SW");var ce=D;function H(i="porter",e){let t=ce.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)}}var Ie=X(require("webextension-polyfill"));var te=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 ne=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 te(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=Ie.default.runtime.connect({name:t});let n=new Promise((o,r)=>{var s;let a=setTimeout(()=>r(new C("connection-timeout","Connection timed out waiting for handshake")),this.CONNECTION_TIMEOUT),g=c=>{var d,u;c.action==="porter-handshake"?(this.logger.debug("Received handshake:",c),clearTimeout(a),this.agentInfo=c.payload.info,this.logger.debug("Handshake agent info:",{agentInfo:this.agentInfo}),(d=this.port)==null||d.onMessage.removeListener(g),o()):c.action==="porter-error"&&(clearTimeout(a),(u=this.port)==null||u.onMessage.removeListener(g),this.logger.error("Error:",c),r(new C(c.payload.type,c.payload.message,c.payload.details)))};(s=this.port)==null||s.onMessage.addListener(g)});(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 oe=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 k=class k{constructor(e={}){let t=e.namespace??"porter",n=e.agentContext??this.determineContext();e.debug!==void 0&&P.configure({enabled:e.debug}),this.logger=P.getLogger("Agent"),this.connectionManager=new ne(t,this.logger),this.messageHandler=new oe(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!k.instance||k.instance.connectionManager.getNamespace()!==e.namespace?k.instance=new k(e):e.debug!==void 0&&P.configure({enabled:e.debug}),k.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)}};k.instance=null;var le=k;function re(i){let e=le.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)}}var z=require("react");function ie(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]||!ie(i[r],e[r]))return!1}return!0}var Q=class Q{static setDebug(e){Q._debug=e}static isDebugEnabled(){return Q._debug}};Q._debug=!1;var V=Q;function Me(){return V.isDebugEnabled()}function de(i,e,t){let n=t.value;return t.value=function(...o){var r;if(Me()){let a=new Error().stack,s=(r=((a==null?void 0:a.split(`
2
- `))||[])[3])==null?void 0:r.match(/at\s+(\S+)\s+/),c=s?s[1]:"unknown",d=o.length>1?o[1]:void 0,u={source:c,timestamp:Date.now(),instanceKey:o[0],changes:d};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 x=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 xe(i,e,t,n,o){var d,u;let r=new Map,a=new Map,g=(d=i.context)!=null&&d.isServiceWorker?"Core":"Agent",s=x.forContext(`${g}:RPC`);return(u=i.context)!=null&&u.agentInfo&&s.setTag(T(i.context.agentInfo)),i.addEventListener("message",p=>{s.debug("Message received:",p);let[f,b]=p.data;if("call"in b&&"args"in b.call){s.debug("Processing call message:",b);let v=b.call,{id:h,args:M,target:S}=v,q=t[h];if(!q){i.postMessage([f,{error:{id:h,error:"Action not found",target:S}}]);return}try{if(q.validate&&q.validate(...M),!S){i.postMessage([f,{error:{id:h,error:"No target provided for action call",target:S}}]);return}let R=e();s.debug("Executing action with most current state:",R),Promise.resolve(q.handler(R,n,S,...M)).then(O=>{s.debug("Action handler result:",{result:O,target:S}),i.postMessage([f,{result:{id:h,result:O,target:S}}])},O=>{i.postMessage([f,{error:{id:h,error:O.message,target:S}}])})}catch(R){R instanceof Error?i.postMessage([f,{error:{id:h,error:R.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=a.get(v.id);h&&(h.clear(),a.delete(v.id))}}),new Proxy({},{get(p,f){return(...b)=>{let v=Math.random();return new Promise((h,M)=>{r.set(v,S=>{S instanceof Promise?S.then(h,M):h(S)}),i.postMessage([v,{call:{id:f,args:b}}])})}}})}function se(i,e,t,n){let o=t||H("crann"),r=o.type!=="agent",a=x.forContext(r?"Core:RPC":"Agent:RPC"),g={postMessage:(s,c)=>{if(r){a.debug("Posting message from service worker:",{message:s,transferables:c});let[,d]=s,u=He(d);if(!u){a.warn("No target specified for RPC response in service worker");return}o.post({action:"rpc",payload:{message:s,transferables:c||[]}},u)}else{let d=o.getAgentInfo();if(!d){a.warn("No agent info found for posting message",{agentInfo:d});return}let u=T(d),[,p]=s;"call"in p&&(p.call.target=d==null?void 0:d.location),a.withTag(u).debug("Sending RPC message from agent:",{rpcPayload:p,message:s}),o.post({action:"rpc",payload:{message:s,transferables:c||[]}})}},addEventListener:(s,c)=>{o.on({rpc:(d,u)=>{try{if(!u)a.debug("RPC message received:",{message:d,event:s});else{let h=T(u);a.withTag(h).debug("RPC message received:",{message:d,event:s})}let{payload:p}=d,{message:f,transferables:b=[]}=p,v=new MessageEvent("message",{data:f,ports:b.filter(h=>h instanceof MessagePort)||[]});c(v)}catch(p){a.error("Failed to parse RPC message payload:",p)}}})},removeEventListener:()=>{},context:{isServiceWorker:r,agentInfo:r?void 0:o.getAgentInfo()}};return xe(g,i,e,n)}function He(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 L=class L{constructor(e,t){this.config=e;this.instances=new Map;this.stateChangeListeners=[];this.instanceReadyListeners=[];this.storagePrefix="crann_";this.porter=H("crann",{debug:!1});t!=null&&t.debug&&(V.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:(s,c)=>{if(!c){this.logger.warn("setState message heard from unknown agent");return}let d=T(c);this.logger.withTag(d).log("Setting state:",s),this.set(s.payload.state,c.id)}});let n=new Set;this.porter.onMessagesSet(s=>{if(!s){this.logger.error("Messages set but no agent info.",{info:s});return}let c=T(s);if(this.logger.withTag(c).log("onMessagesSet received for agent:",{id:s.id,context:s.location.context,tabId:s.location.tabId,frameId:s.location.frameId,alreadyInitialized:n.has(s.id)}),n.has(s.id)){this.logger.withTag(c).log("Already sent initialState to agent, skipping:",s.id);return}n.add(s.id),this.logger.withTag(c).log("Messages set received. Sending initial state.",{info:s});let d=this.get(s.id);this.porter.post({action:"initialState",payload:{state:d,info:s}},s.location),this.notifyInstanceReady(s.id,s)}),this.porter.onConnect(s=>{if(!s){this.logger.error("Agent connected but no agent info.",{info:s});return}let c=T(s);this.logger.withTag(c).log("Agent connected",{info:s}),this.addInstance(s.id,c),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=(s,c)=>c!==void 0?this.set(s,c):this.set(s),a=this.extractActions(e),g=()=>{let s=this.get();return this.logger.log("State getter called, returning current state:",s),s};this.rpcEndpoint=se(g,a,this.porter,r)}static getInstance(e,t){return L.instance?t!=null&&t.debug&&x.forContext("Core").log("Instance requested and already existed, returning"):L.instance=new L(e,t),L.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};ie(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};ie(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",a=e?e[n]:this.serviceState[n];switch(r){case"session":await j.default.storage.session.set({[this.storagePrefix+n]:a}),t=!0;break;case"local":await j.default.storage.local.set({[this.storagePrefix+n]:a}),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,a)=>{this.porter.post({action:"stateUpdate",payload:{state:e}},a)}))}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 a=this.config[r];if(ze(a)){if(a.partition==="instance"){let g=r,s=e;n[g]=s[g]}else if(!a.partition||a.partition===F.Service){let g=r,s=e;o[g]=s[g]}}}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 j.default.storage.local.get(null),t=await j.default.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 a in n){let g=this.removePrefix(a);if(this.logger.log(`Checking storage key ${a} -> ${g}`),this.config.hasOwnProperty(g)){let s=n[a];this.logger.log(`Found storage value for ${g}:`,s),o[g]=s,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];$(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),$(n)&&(!n.partition||n.partition===F.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])=>J(n)).reduce((t,[n,o])=>{let r=o;return{...t,[n]:r}},{})}};L.instance=null,ge([de],L.prototype,"setServiceState",1),ge([de],L.prototype,"setInstanceState",1);var G=L;function Pe(i,e){let t=G.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 ze(i){return i&&typeof i=="object"&&"default"in i}var E={connected:!1},N=null,ue=new Set,fe=new Set;function ae(i,e){let t=(e==null?void 0:e.debug)||!1,n=e==null?void 0:e.context;t&&x.setDebug(!0);let o=x.forContext("Agent"),r,a="unset",g=new Set;if(o.log("Initializing Crann Agent"+(n?` with context: ${n}`:"")),N&&E.connected)return o.log("We had an instance already and it's connected, returning"),o.log("Connect, calling onReady callback"),setTimeout(()=>{g.forEach(l=>l(E))},0),N;N&&!E.connected&&(o.log("We had an instance but it's disconnected, creating new connection"),N=null),o.log("No existing instance, creating a new one");let s=re({namespace:"crann",debug:!1});o.log("Porter connection created"),s.onDisconnect(()=>{o.log("Porter connection lost, updating connection status"),E={connected:!1},ue.forEach(l=>{try{l()}catch(m){o.error("Error in disconnect callback:",m)}}),g.forEach(l=>{try{l(E)}catch(m){o.error("Error in onReady callback during disconnect:",m)}})}),s.onReconnect(l=>{o.log("Porter reconnected, updating connection status",l),E={connected:!0,agent:l},r=l,a=T(l),o.setTag(a),fe.forEach(m=>{try{m(l)}catch(w){o.error("Error in reconnect callback:",w)}}),g.forEach(m=>{try{m(E)}catch(w){o.error("Error in onReady callback during reconnect:",w)}})});let c=Object.entries(i).filter(([l,m])=>J(m)).reduce((l,[m,w])=>{let U=w;return{...l,[m]:{type:"action",handler:U.handler,validate:U.validate}}},{}),d=se(()=>we(i),c,s),u=!1;s.on({initialState:l=>{if(o.log("initialState received",{alreadyReceived:u,payload:l.payload}),u){o.log("Ignoring duplicate initialState message");return}u=!0,p=l.payload.state,r=l.payload.info,a=T(r),E={connected:!0,agent:r},o.setTag(a),o.log(`Initial state received and ${b.size} listeners notified`,{message:l}),g.forEach(m=>{o.log("Calling onReady callbacks"),m(E)}),b.forEach(m=>{m.callback(p)})},stateUpdate:l=>{f=l.payload.state,p={...p,...f},o.log("State updated:",{message:l,changes:f,_state:p}),f&&b.forEach(m=>{(m.keys===void 0||m.keys.some(U=>U in f))&&m.callback(f)})}}),o.log("Porter connected. Setting up state and listeners");let p=we(i),f=null,b=new Set;o.log("Completed setup, returning instance");let v=()=>p,h=l=>{o.log("Calling post with setState",l),s.post({action:"setState",payload:{state:l}})},M=(l,m)=>{let w={keys:m,callback:l};return b.add(w),()=>{b.delete(w)}};return N={useCrann:l=>{let m=()=>v()[l],w=W=>h({[l]:W}),U=W=>{let pe=m();return M(Re=>{if(l in Re){let me=m(),ke=v();W({current:me,previous:pe,state:ke}),pe=me}},[l])};return[m(),w,U]},get:v,set:h,subscribe:M,getAgentInfo:()=>r,onReady:l=>(o.log("onReady callback added"),g.add(l),E.connected&&(o.log("calling onReady callback"),setTimeout(()=>{l(E)},0)),()=>g.delete(l)),callAction:async(l,...m)=>(o.log("Calling action",l,m),d[l](...m)),onDisconnect:l=>(o.log("onDisconnect callback added"),ue.add(l),()=>{ue.delete(l)}),onReconnect:l=>(o.log("onReconnect callback added"),fe.add(l),()=>{fe.delete(l)})},N}function Ee(){return N!==null}function we(i){let e={};return Object.keys(i).forEach(t=>{let n=i[t];$(n)&&(e[t]=n.default)}),e}var I=require("react");function De(i){return function(t){let{useCrann:n,get:o,set:r,subscribe:a,callAction:g}=(0,I.useMemo)(()=>ae(i),[t]),s=(0,I.useCallback)(u=>{let[p,f]=(0,I.useState)(o()[u]),b=(0,I.useRef)(p);(0,I.useEffect)(()=>{b.current=p},[p]),(0,I.useEffect)(()=>(f(o()[u]),a(M=>{u in M&&f(M[u])},[u])),[u]);let v=(0,I.useCallback)(h=>{r({[u]:h})},[u]);return[p,v]},[o,r,a]),c=(0,I.useCallback)(()=>o(),[o]),d=(0,I.useCallback)(u=>{r(u)},[r]);return{useStateItem:s,getState:c,setState:d,useCrann:n,callAction:g}}}function Le(i){return i}0&&(module.exports={Crann,Partition,Persistence,connect,connected,create,createConfig,createCrannStateHook});
1
+ "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=(r,e)=>()=>(r&&(e=r(r=0)),e);var Ue=(r,e)=>{for(var t in e)J(r,t,{get:e[t],enumerable:!0})},Ke=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of it(e))!rt.call(r,i)&&i!==t&&J(r,i,{get:()=>e[i],enumerable:!(n=qe(e,i))||n.enumerable});return r};var Z=(r,e,t)=>(t=r!=null?nt(ot(r)):{},Ke(e||!r||!r.__esModule?J(t,"default",{value:r,enumerable:!0}):t,r)),Be=r=>Ke(J({},"__esModule",{value:!0}),r),Ae=(r,e,t,n)=>{for(var i=n>1?void 0:n?qe(e,t):e,o=r.length-1,s;o>=0;o--)(s=r[o])&&(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=Be(ft);var ze=Z(require("webextension-polyfill"));var Y=(g=>(g.ContentScript="contentscript",g.Extension="extension",g.Popup="popup",g.Sidepanel="sidepanel",g.Devtools="devtools",g.Options="options",g.Unknown="unknown",g))(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 ce=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,o=n.tabId||-1,s=n.frameId||0;this.logger.debug("Determined context for new agent",{determinedContext:i,tabId:o,frameId:s});let g=Array.from(this.agentsInfo.values()).filter(f=>f.location.context===i&&f.location.tabId===o&&f.location.frameId===s);g.length>0&&this.logger.debug("Adding agent: Found existing similar agent.",{tabAgentsInfo:g});let a=((m=(p=this.getAgentByLocation({context:i,tabId:o,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 c={id:a,location:{context:i,tabId:o,frameId:s},createdAt:Date.now(),lastActiveAt:Date.now()};this.agentsInfo.set(a,c),this.logger.debug(`Constructed agent info: ${JSON.stringify(c)}`),e.onMessage.addListener(f=>this.emit("agentMessage",f,c));let d={port:e,info:c};return e.onDisconnect.addListener(()=>{this.emit("agentDisconnect",c),this.logger.debug("Agent disconnected, removing from manager. ",{agentInfo:c}),this.removeAgent(a)}),this.emit("agentSetup",d),this.logger.debug("Setup complete for adding agent. ",{agentInfo:c}),a}getAgentByLocation(e){let{context:t,tabId:n,frameId:i}=e,o=Array.from(this.agentsInfo.entries()).find(([c,d])=>d.location.context===t&&d.location.tabId===n&&d.location.frameId===i);if(o===void 0)return this.logger.error("No agent found for location. ",{location:e}),null;let s=o[0],g=this.agents.get(s),a=this.agentsInfo.get(s);return!g||!a?(this.logger.error("No agent found for location. ",{location:e}),null):{port:g,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 o=e.context?i.location.context===e.context:!0,s=e.tabId?i.location.tabId===e.tabId:!0,g=e.frameId?i.location.frameId===e.frameId:!0;return o&&s&&g}).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,R,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)||"",o=n.options_page||"",s=((f=n.action)==null?void 0:f.default_popup)||"",g=n.devtools_page||"",a=((y=n.chrome_url_overrides)==null?void 0:y.newtab)||"",c=((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:o?o.split("/").pop():"options.html",popup:s?s.split("/").pop():"popup.html",devtools:g?g.split("/").pop():"devtools.html",newtab:a?a.split("/").pop():"newtab.html",bookmarks:c?c.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:((R=t.tab)==null?void 0:R.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 o=this.agentOperations.getAgentById(i);o&&this.confirmConnection(o),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 o=(s=this.agentOperations.getAgentById(i.id))==null?void 0:s.info;if(!o){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",o)}}}async post(e,t){return new Promise((n,i)=>{try{this.logger.debug("Post request received:",{action:e.action,target:t});let o=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):gt(t)?this.postToContext(e,t):typeof t=="string"?this.postToId(e,t):this.postToTab(e,t),clearTimeout(o),n()}catch(o){let s=o instanceof Error?o.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 o=e[i.message.action];if(o){this.logger.debug("onMessage, calling handler ",{event:i});let{message:s,...g}=i;o(s,g)}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,...o}=e;n(i,o);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(r){return typeof r=="object"&&r!==null&&"context"in r&&"tabId"in r&&"frameId"in r}function gt(r){return typeof r=="string"&&Object.values(Y).includes(r)}var k=class k{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 ce(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 k.staticLogger.debug(`Getting instance for namespace: ${e}`),k.instances.has(e)?(t==null?void 0:t.debug)!==void 0&&D.configure({enabled:t.debug}):(k.staticLogger.info(`Creating new instance for namespace: ${e}`),k.instances.set(e,new k(e,t))),k.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)}};k.instances=new Map,k.staticLogger=D.getLogger("SW");var Te=k;function V(r="porter",e){let t=Te.getInstance(r,e);return{type:"source",post:t.post.bind(t),onMessage:t.onMessage.bind(t),on:t.on.bind(t),onConnect:t.onConnect.bind(t),onDisconnect:t.onDisconnect.bind(t),onMessagesSet:t.onMessagesSet.bind(t),getAgentById:t.getAgentById.bind(t),getAgentByLocation:t.getAgentByLocation.bind(t),queryAgents:t.queryAgents.bind(t)}}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,o)=>{var a;let s=setTimeout(()=>o(new S("connection-timeout","Connection timed out waiting for handshake")),this.CONNECTION_TIMEOUT),g=c=>{var d,p;c.action==="porter-handshake"?(this.logger.debug("Received handshake:",c),clearTimeout(s),this.agentInfo=c.payload.info,this.logger.debug("Handshake agent info:",{agentInfo:this.agentInfo}),(d=this.port)==null||d.onMessage.removeListener(g),i()):c.action==="porter-error"&&(clearTimeout(s),(p=this.port)==null||p.onMessage.removeListener(g),this.logger.error("Error:",c),o(new S(c.payload.type,c.payload.message,c.payload.details)))};(a=this.port)==null||a.onMessage.addListener(g)});(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(o=>o(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 K=class K{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!K.instance||K.instance.connectionManager.getNamespace()!==e.namespace?K.instance=new K(e):e.debug!==void 0&&D.configure({enabled:e.debug}),K.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)}};K.instance=null;var Ie=K;function Q(r){let e=Ie.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 ee=require("react");var N={Shared:"shared",Agent:"agent"},P={Local:"local",Session:"session",None:"none"};function $(r){return typeof r=="object"&&r!==null&&"default"in r&&!("handler"in r)}function ct(r){return typeof r=="object"&&r!==null&&"handler"in r}function we(r){let{ConfigError:e}=(G(),Be(_e));if(!r.name)throw new e("'name' is required","name");if(typeof r.name!="string")throw new e("'name' must be a string","name");if(!/^[a-zA-Z][a-zA-Z0-9_-]*$/.test(r.name))throw new e("'name' must start with a letter and contain only letters, numbers, underscores, and hyphens","name");if(r.version!==void 0&&(typeof r.version!="number"||!Number.isInteger(r.version)||r.version<1))throw new e("'version' must be a positive integer","version");for(let[t,n]of Object.entries(r))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(r.actions!==void 0){if(typeof r.actions!="object"||r.actions===null)throw new e("'actions' must be an object","actions");for(let[t,n]of Object.entries(r.actions)){if(!ct(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{...r,version:r.version??1}}function B(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),n=Object.keys(e);if(t.length!==n.length)return!1;t.sort(),n.sort();for(let i=0;i<t.length;i++){let o=t[i];if(o!==n[i]||!B(r[o],e[o]))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 o of Object.keys(e)){let s=this.config[o];if(!$(s))continue;let g=e[o],a=s.scope===N.Agent;a&&t?i[o]=g:a||(n[o]=g)}if(Object.keys(n).length>0){let o={...this.sharedState,...n};B(this.sharedState,o)||(this.sharedState=o)}if(t&&Object.keys(i).length>0){let o=this.agentStates.get(t)??{...this.defaultAgentState},s={...o,...i};B(o,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};B(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[o,s]of Object.entries(this.config)){if(o==="name"||o==="version"||o==="actions"||!$(s)||s.scope==="agent"||!s.persist||s.persist===P.None)continue;let g=this.buildKey(o);g in n&&(i[o]=n[g])}return await this.updateMetadata(),i}async persist(e){let t={},n={};for(let[o,s]of Object.entries(e)){let g=this.config[o];if(!$(g))continue;let a=this.buildKey(o);switch(g.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(r){let{keepStores:e,dryRun:t=!1}=r,[n,i]=await Promise.all([I.default.storage.local.get(null),I.default.storage.session.get(null)]),o=new Set([...Object.keys(n),...Object.keys(i)]),s=[];for(let g of o){if(!g.startsWith("crann:"))continue;let a=g.match(/^crann:([^:]+):/);if(!a)continue;let c=a[1];e.includes(c)||s.push(g)}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 o=i[e];if(!o)throw new O(e,this.config.name,"Unknown action");if(o.validate)try{o.validate(...t)}catch(g){throw new H(e,this.config.name,g instanceof Error?g.message:String(g))}let s={state:this.getState(),setState:async g=>{await this.setState(g,n.id)},agentId:n.id,agentLocation:n.location};try{return await o.handler(s,...t)}catch(g){throw new O(e,this.config.name,g instanceof Error?g.message:String(g))}}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){return this.assertNotDestroyed(),this.stateChangeListeners.add(e),()=>{this.stateChangeListeners.delete(e)}}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{actionName:n,args:i}=e.payload;try{let o=await this.actionExecutor.execute(n,i,t);this.porter.post({action:"rpcResult",payload:{actionName:n,result:o,success:!0}},t.location)}catch(o){this.porter.post({action:"rpcResult",payload:{actionName:n,error:o instanceof Error?o.message:String(o),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 o;let n=this.getState(),i=t?(o=this.porter.getAgentById(t))==null?void 0:o.info:void 0;if(this.stateChangeListeners.forEach(s=>{s(n,e,i)}),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(r,e){return new W(r,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 o={keys:n,callback:i};return this.subscribers.add(o),()=>{this.subscribers.delete(o)}}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(o){console.error("[Crann Agent] Error in onReady callback:",o)}}),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(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:o,error:s,success:g}=n.payload,a=e.get(i);a&&(e.delete(i),g?a.resolve(o):a.reject(new Error(s)))}}),new Proxy({},{get:(n,i)=>async(...o)=>{let s=`${t++}`;return new Promise((g,a)=>{e.set(s,{resolve:g,reject:a}),this.porter.post({action:"rpc",payload:{callId:s,actionName:i,args:o}})})}})}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(r,e){return new X(r,e)}G();var se=Z(require("webextension-polyfill"));var ne={Instance:"instance",Service:"service"},Qe={Session:"session",Local:"local",None:"none"},ie=r=>!("handler"in r),Ce=r=>"handler"in r;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 Re(r,e,t){let n=t.value;return t.value=function(...i){var o;if(Ge()){let s=new Error().stack,a=(o=((s==null?void 0:s.split(`
2
+ `))||[])[3])==null?void 0:o.match(/at\s+(\S+)\s+/),c=a?a[1]:"unknown",d=i.length>1?i[1]:void 0,p={source:c,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",o="";return A.debug?{debug:console.log.bind(console,t,n,o,i,o),log:console.log.bind(console,t,n,o,i,o),info:console.info.bind(console,t,n,o,i,o),warn:console.warn.bind(console,t,n,o,i,o),error:console.error.bind(console,t,n,o,i,o)}:{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(r,e={terse:!0}){let n=(i=>{let o=String(i);return o.length>=4?o.slice(-4):o.padStart(4,"0")})(r.location.tabId);return e!=null&&e.terse?`${n}:${r.location.frameId}`:`${r.location.context}:${n}:${r.location.frameId}`}function We(r,e,t,n,i){var d,p;let o=new Map,s=new Map,g=(d=r.context)!=null&&d.isServiceWorker?"Core":"Agent",a=x.forContext(`${g}:RPC`);return(p=r.context)!=null&&p.agentInfo&&a.setTag(T(r.context.agentInfo)),r.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:R,target:b}=C,j=t[u];if(!j){r.postMessage([f,{error:{id:u,error:"Action not found",target:b}}]);return}try{if(j.validate&&j.validate(...R),!b){r.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,...R)).then(F=>{a.debug("Action handler result:",{result:F,target:b}),r.postMessage([f,{result:{id:u,result:F,target:b}}])},F=>{r.postMessage([f,{error:{id:u,error:F.message,target:b}}])})}catch(U){U instanceof Error?r.postMessage([f,{error:{id:u,error:U.message,target:b}}]):r.postMessage([f,{error:{id:u,error:"Unknown error occurred",target:b}}])}}else if("result"in y){let C=y.result,u=o.get(f);u&&(u(C.result),o.delete(f))}else if("error"in y){let C=y.error,u=o.get(f);u&&(u(Promise.reject(new Error(C.error))),o.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,R)=>{o.set(C,b=>{b instanceof Promise?b.then(u,R):u(b)}),r.postMessage([C,{call:{id:f,args:y}}])})}}})}function be(r,e,t,n){let i=t||V("crann"),o=i.type!=="agent",s=x.forContext(o?"Core:RPC":"Agent:RPC"),g={postMessage:(a,c)=>{if(o){s.debug("Posting message from service worker:",{message:a,transferables:c});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:c||[]}},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:c||[]}})}},addEventListener:(a,c)=>{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)||[]});c(C)}catch(m){s.error("Failed to parse RPC message payload:",m)}}})},removeEventListener:()=>{},context:{isServiceWorker:o,agentInfo:o?void 0:i.getAgentInfo()}};return We(g,r,e,n)}function lt(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 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,c)=>{if(!c){this.logger.warn("setState message heard from unknown agent");return}let d=T(c);this.logger.withTag(d).log("Setting state:",a),this.set(a.payload.state,c.id)}});let n=new Set;this.porter.onMessagesSet(a=>{if(!a){this.logger.error("Messages set but no agent info.",{info:a});return}let c=T(a);if(this.logger.withTag(c).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(c).log("Already sent initialState to agent, skipping:",a.id);return}n.add(a.id),this.logger.withTag(c).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 c=T(a);this.logger.withTag(c).log("Agent connected",{info:a}),this.addInstance(a.id,c),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 o=(a,c)=>c!==void 0?this.set(a,c):this.set(a),s=this.extractActions(e),g=()=>{let a=this.get();return this.logger.log("State getter called, returning current state:",a),a};this.rpcEndpoint=be(g,s,this.porter,o)}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};B(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};B(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 o=this.config[n].persist||"none",s=e?e[n]:this.serviceState[n];switch(o){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(o=>{o(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((o,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 o in e){let s=this.config[o];if(dt(s)){if(s.partition==="instance"){let g=o,a=e;n[g]=a[g]}else if(!s.partition||s.partition===ne.Service){let g=o,a=e;i[g]=a[g]}}}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={},o=!1;for(let s in n){let g=this.removePrefix(s);if(this.logger.log(`Checking storage key ${s} -> ${g}`),this.config.hasOwnProperty(g)){let a=n[s];this.logger.log(`Found storage value for ${g}:`,a),i[g]=a,o=!0}}o?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 o=i;return{...t,[n]:o}},{})}};q.instance=null,Ae([Re],q.prototype,"setServiceState",1),Ae([Re],q.prototype,"setInstanceState",1);var ae=q;function Xe(r,e){let t=ae.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 dt(r){return r&&typeof r=="object"&&"default"in r}var E={connected:!1},z=null,ke=new Set,Le=new Set;function Ze(r,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"),o,s="unset",g=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(()=>{g.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},ke.forEach(l=>{try{l()}catch(h){i.error("Error in disconnect callback:",h)}}),g.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},o=l,s=T(l),i.setTag(s),Le.forEach(h=>{try{h(l)}catch(w){i.error("Error in reconnect callback:",w)}}),g.forEach(h=>{try{h(E)}catch(w){i.error("Error in onReady callback during reconnect:",w)}})});let c=Object.entries(r).filter(([l,h])=>Ce(h)).reduce((l,[h,w])=>{let _=w;return{...l,[h]:{type:"action",handler:_.handler,validate:_.validate}}},{}),d=be(()=>Je(r),c,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,o=l.payload.info,s=T(o),E={connected:!0,agent:o},i.setTag(s),i.log(`Initial state received and ${y.size} listeners notified`,{message:l}),g.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(r),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}})},R=(l,h)=>{let w={keys:h,callback:l};return y.add(w),()=>{y.delete(w)}};return z={useCrann:l=>{let h=()=>C()[l],w=ge=>u({[l]:ge}),_=ge=>{let Ne=h();return R(et=>{if(l in et){let $e=h(),tt=C();ge({current:$e,previous:Ne,state:tt}),Ne=$e}},[l])};return[h(),w,_]},get:C,set:u,subscribe:R,getAgentInfo:()=>o,onReady:l=>(i.log("onReady callback added"),g.add(l),E.connected&&(i.log("calling onReady callback"),setTimeout(()=>{l(E)},0)),()=>g.delete(l)),callAction:async(l,...h)=>(i.log("Calling action",l,h),d[l](...h)),onDisconnect:l=>(i.log("onDisconnect callback added"),ke.add(l),()=>{ke.delete(l)}),onReconnect:l=>(i.log("onReconnect callback added"),Le.add(l),()=>{Le.delete(l)})},z}function Ye(){return z!==null}function Je(r){let e={};return Object.keys(r).forEach(t=>{let n=r[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});
3
3
  //# sourceMappingURL=index.js.map