crann 2.0.3 → 2.0.4

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/react/index.ts", "../../src/react/hooks.tsx", "../../src/transport/core/PorterSource.ts", "../../src/transport/porter.model.ts", "../../src/transport/porter.utils.ts", "../../src/transport/managers/AgentManager.ts", "../../src/transport/managers/ConnectionManager.ts", "../../src/transport/managers/MessageHandler.ts", "../../src/transport/managers/AgentConnectionManager.ts", "../../src/transport/managers/MessageQueue.ts", "../../src/transport/managers/AgentMessageHandler.ts", "../../src/transport/core/PorterAgent.ts", "../../src/transport/react/usePorter.ts", "../../src/errors.ts", "../../src/agent/Agent.ts"],
4
- "sourcesContent": ["/**\n * Crann React Integration\n *\n * Provides React hooks for connecting to and using a Crann store.\n *\n * @example\n * import { createCrannHooks } from 'crann/react';\n * import { config } from './config';\n *\n * export const { useCrannState, useCrannActions, useCrannReady } = createCrannHooks(config);\n */\n\nexport { createCrannHooks } from \"./hooks\";\n\nexport type {\n CrannHooks,\n CreateCrannHooksOptions,\n UseCrannStateSelector,\n UseCrannStateTuple,\n} from \"./types\";\n\n", "/**\n * Crann React Hooks Implementation\n *\n * Provides React hooks for connecting to and using a Crann store.\n */\n\nimport {\n createContext,\n useContext,\n useState,\n useEffect,\n useRef,\n useCallback,\n useMemo,\n type ReactNode,\n type FC,\n} from \"react\";\nimport { connectStore } from \"../agent\";\nimport type { AgentAPI } from \"../agent/types\";\nimport type {\n ConfigSchema,\n ValidatedConfig,\n DerivedState,\n DerivedActions,\n} from \"../store/types\";\nimport type { CrannHooks, CreateCrannHooksOptions, UseCrannStateTuple } from \"./types\";\n\n/**\n * Creates a set of React hooks for a Crann store.\n *\n * This is the main entry point for React integration. Call once at module\n * level with your config, then use the returned hooks in your components.\n *\n * @param config - Validated config from createConfig()\n * @param options - Optional hook options\n * @returns Object containing all Crann React hooks\n *\n * @example\n * // hooks.ts\n * import { createConfig } from 'crann';\n * import { createCrannHooks } from 'crann/react';\n *\n * const config = createConfig({\n * name: 'myFeature',\n * count: { default: 0 },\n * actions: {\n * increment: { handler: async (ctx) => ctx.setState({ count: ctx.state.count + 1 }) },\n * },\n * });\n *\n * export const { useCrannState, useCrannActions, useCrannReady } = createCrannHooks(config);\n *\n * // MyComponent.tsx\n * function Counter() {\n * const count = useCrannState(s => s.count);\n * const { increment } = useCrannActions();\n * const isReady = useCrannReady();\n *\n * if (!isReady) return <div>Loading...</div>;\n *\n * return <button onClick={() => increment()}>Count: {count}</button>;\n * }\n */\nexport function createCrannHooks<TConfig extends ConfigSchema>(\n config: ValidatedConfig<TConfig>,\n options: CreateCrannHooksOptions = {}\n): CrannHooks<TConfig> {\n // Module-level agent instance (created lazily)\n let moduleAgent: AgentAPI<TConfig> | null = null;\n\n // Create React context for optional provider override\n const AgentContext = createContext<AgentAPI<TConfig> | null>(null);\n\n /**\n * Get or create the agent instance.\n */\n function getAgent(): AgentAPI<TConfig> {\n if (!moduleAgent) {\n moduleAgent = connectStore(config, { debug: options.debug });\n }\n return moduleAgent;\n }\n\n /**\n * Hook to get the current agent (from context or module-level).\n */\n function useAgent(): AgentAPI<TConfig> | null {\n const contextAgent = useContext(AgentContext);\n const [agent, setAgent] = useState<AgentAPI<TConfig> | null>(\n contextAgent ?? moduleAgent\n );\n\n useEffect(() => {\n if (!agent) {\n setAgent(getAgent());\n }\n }, [agent]);\n\n return contextAgent ?? agent;\n }\n\n /**\n * Hook to check if connection is ready.\n */\n function useCrannReady(): boolean {\n const agent = useAgent();\n const [isReady, setIsReady] = useState(false);\n\n useEffect(() => {\n if (!agent) return;\n\n // Check if already ready\n const info = agent.getInfo();\n if (info) {\n setIsReady(true);\n }\n\n // Subscribe to ready event\n const unsubReady = agent.onReady(() => {\n setIsReady(true);\n });\n\n // Subscribe to disconnect/reconnect\n const unsubDisconnect = agent.onDisconnect(() => {\n setIsReady(false);\n });\n\n const unsubReconnect = agent.onReconnect(() => {\n setIsReady(true);\n });\n\n return () => {\n unsubReady();\n unsubDisconnect();\n unsubReconnect();\n };\n }, [agent]);\n\n return isReady;\n }\n\n /**\n * Hook to read state with selector or key pattern.\n */\n function useCrannState<TSelected>(\n selector: (state: DerivedState<TConfig>) => TSelected\n ): TSelected;\n function useCrannState<K extends keyof DerivedState<TConfig>>(\n key: K\n ): UseCrannStateTuple<TConfig, K>;\n function useCrannState<TSelected, K extends keyof DerivedState<TConfig>>(\n selectorOrKey: ((state: DerivedState<TConfig>) => TSelected) | K\n ): TSelected | UseCrannStateTuple<TConfig, K> {\n const agent = useAgent();\n const isFunction = typeof selectorOrKey === \"function\";\n\n // For selector pattern\n const selector = isFunction\n ? (selectorOrKey as (state: DerivedState<TConfig>) => TSelected)\n : (state: DerivedState<TConfig>) => state[selectorOrKey as K] as unknown as TSelected;\n\n // Track selected value\n const [selectedValue, setSelectedValue] = useState<TSelected>(() => {\n if (!agent) {\n // Return default from config\n const defaultState = buildDefaultState(config);\n return selector(defaultState);\n }\n return selector(agent.getState());\n });\n\n // Store previous selected value for comparison\n const prevSelectedRef = useRef(selectedValue);\n\n useEffect(() => {\n if (!agent) return;\n\n // Update with current state\n const currentState = agent.getState();\n const newSelected = selector(currentState);\n if (!shallowEqual(newSelected, prevSelectedRef.current)) {\n prevSelectedRef.current = newSelected;\n setSelectedValue(newSelected);\n }\n\n // Subscribe to changes\n const unsubscribe = agent.subscribe((changes, state) => {\n const newSelected = selector(state);\n if (!shallowEqual(newSelected, prevSelectedRef.current)) {\n prevSelectedRef.current = newSelected;\n setSelectedValue(newSelected);\n }\n });\n\n return unsubscribe;\n }, [agent, selector]);\n\n // For key pattern, return tuple\n if (!isFunction) {\n const key = selectorOrKey as K;\n const setValue = useCallback(\n async (value: DerivedState<TConfig>[K]) => {\n if (agent) {\n await agent.setState({ [key]: value } as Partial<DerivedState<TConfig>>);\n }\n },\n [agent, key]\n );\n\n return [selectedValue as DerivedState<TConfig>[K], setValue] as UseCrannStateTuple<TConfig, K>;\n }\n\n return selectedValue;\n }\n\n /**\n * Hook to get typed actions with stable references.\n */\n function useCrannActions(): DerivedActions<TConfig> {\n const agent = useAgent();\n\n // Store agent in ref so proxy can access current value\n const agentRef = useRef<AgentAPI<TConfig> | null>(null);\n agentRef.current = agent;\n\n // Return stable proxy that delegates to agent.actions\n const actionsRef = useRef<DerivedActions<TConfig> | null>(null);\n\n if (!actionsRef.current) {\n actionsRef.current = new Proxy({} as DerivedActions<TConfig>, {\n get: (_target, actionName: string) => {\n return async (...args: unknown[]) => {\n const currentAgent = agentRef.current;\n if (!currentAgent) {\n throw new Error(\n `Cannot call action \"${actionName}\" before agent is connected`\n );\n }\n return (currentAgent.actions as any)[actionName](...args);\n };\n },\n });\n }\n\n return actionsRef.current;\n }\n\n /**\n * Optional provider for dependency injection.\n */\n const CrannProvider: FC<{ agent?: AgentAPI<TConfig>; children: ReactNode }> = ({\n agent,\n children,\n }) => {\n const value = agent ?? getAgent();\n return <AgentContext.Provider value={value}>{children}</AgentContext.Provider>;\n };\n\n return {\n useCrannState,\n useCrannActions,\n useCrannReady,\n useAgent,\n CrannProvider,\n };\n}\n\n// =============================================================================\n// Utilities\n// =============================================================================\n\n/**\n * Build default state from config.\n */\nfunction buildDefaultState<TConfig extends ConfigSchema>(\n config: ValidatedConfig<TConfig>\n): DerivedState<TConfig> {\n const state: Record<string, unknown> = {};\n\n for (const [key, item] of Object.entries(config)) {\n if (key === \"name\" || key === \"version\" || key === \"actions\") continue;\n if (typeof item === \"object\" && item !== null && \"default\" in item) {\n state[key] = item.default;\n }\n }\n\n return state as DerivedState<TConfig>;\n}\n\n/**\n * Shallow equality check.\n */\nfunction shallowEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (typeof a !== \"object\" || typeof b !== \"object\") return false;\n if (a === null || b === null) return false;\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if ((a as any)[key] !== (b as any)[key]) return false;\n }\n\n return true;\n}\n\n", "import browser, { Runtime } from 'webextension-polyfill';\nimport {\n AgentInfo,\n Listener,\n Message,\n MessageConfig,\n PorterError,\n PorterErrorType,\n BrowserLocation,\n Unsubscribe,\n AgentId,\n MessageTarget,\n} from '../porter.model';\nimport { Agent } from '../porter.model';\nimport { isServiceWorker } from '../porter.utils';\nimport { Logger } from '../porter.utils';\nimport { AgentManager } from '../managers/AgentManager';\nimport { ConnectionManager } from '../managers/ConnectionManager';\nimport { MessageHandler } from '../managers/MessageHandler';\n\nexport interface PorterSourceOptions {\n namespace?: string;\n debug?: boolean;\n}\n\nexport class PorterSource {\n private static instances: Map<string, PorterSource> = new Map();\n private readonly agentManager: AgentManager;\n private readonly messageHandler: MessageHandler;\n private readonly connectionManager: ConnectionManager;\n private readonly logger: Logger;\n private static staticLogger = Logger.getLogger('SW');\n private namespace: string;\n\n private constructor(namespace?: string, options?: PorterSourceOptions) {\n // Configure logger if debug option is provided\n if (options?.debug !== undefined) {\n Logger.configure({ enabled: options.debug });\n }\n\n this.logger = Logger.getLogger(`SW`);\n this.namespace = namespace || 'porter';\n if (!namespace) {\n this.logger.error('No namespace provided, defaulting to \"porter\"');\n }\n\n this.agentManager = new AgentManager(this.logger);\n this.messageHandler = new MessageHandler(this.agentManager, this.logger);\n this.connectionManager = new ConnectionManager(\n this.agentManager,\n this.namespace,\n this.logger\n );\n this.logger.info(`Constructing Porter with namespace: ${this.namespace}`);\n\n if (!isServiceWorker()) {\n throw new PorterError(\n PorterErrorType.INVALID_CONTEXT,\n 'Can only create in a service worker'\n );\n }\n\n // Wire up event handlers\n this.agentManager.on(\n 'agentMessage',\n (message: any, metadata: AgentInfo) => {\n this.messageHandler.handleIncomingMessage(message, metadata);\n }\n );\n\n this.agentManager.on('agentDisconnect', (metadata: AgentInfo) => {\n this.messageHandler.handleDisconnect(metadata);\n });\n\n this.agentManager.on('agentSetup', (agent: Agent) => {\n this.logger.debug(`Handling agent setup`, { agent });\n this.messageHandler.handleConnect(agent.info);\n this.connectionManager.confirmConnection(agent);\n });\n\n browser.runtime.onConnect.addListener(\n this.connectionManager.handleConnection.bind(this.connectionManager)\n );\n }\n\n public static getInstance(\n namespace: string = 'porter',\n options?: PorterSourceOptions\n ): PorterSource {\n PorterSource.staticLogger.debug(\n `Getting instance for namespace: ${namespace}`\n );\n if (!PorterSource.instances.has(namespace)) {\n PorterSource.staticLogger.info(\n `Creating new instance for namespace: ${namespace}`\n );\n PorterSource.instances.set(\n namespace,\n new PorterSource(namespace, options)\n );\n } else if (options?.debug !== undefined) {\n // If instance exists but debug setting changed, configure logger\n Logger.configure({ enabled: options.debug });\n }\n return PorterSource.instances.get(namespace)!;\n }\n\n // Public API methods that will be exposed via the source function\n public post(message: Message<any>, target?: MessageTarget): Promise<void> {\n return this.messageHandler.post(message, target);\n }\n\n public onMessage(config: MessageConfig): Unsubscribe {\n return this.messageHandler.onMessage(config);\n }\n\n public on(config: MessageConfig): Unsubscribe {\n return this.messageHandler.on(config);\n }\n\n public onConnect(listener: Listener<'onConnect'>): Unsubscribe {\n return this.messageHandler.onConnect(listener);\n }\n\n public onDisconnect(listener: Listener<'onDisconnect'>): Unsubscribe {\n return this.messageHandler.onDisconnect(listener);\n }\n\n public onMessagesSet(listener: Listener<'onMessagesSet'>): Unsubscribe {\n return this.messageHandler.onMessagesSet(listener);\n }\n\n // Utility methods that might be needed externally\n public getInfo(key: string): AgentInfo | null {\n return this.agentManager.getAgentById(key)?.info || null;\n }\n\n // Utility methods that might be needed externally\n public getAgentById(agentId: AgentId): Agent | null {\n return this.agentManager.getAgentById(agentId);\n }\n\n public getAgentByLocation(location: BrowserLocation): Agent | null {\n return this.agentManager.getAgentByLocation(location);\n }\n\n public queryAgents(location: Partial<BrowserLocation>): Agent[] {\n return this.agentManager.queryAgents(location);\n }\n}\n\nexport interface PorterAPI {\n type: 'source';\n post: (message: Message<any>, target?: MessageTarget) => Promise<void>;\n onMessage: (config: MessageConfig) => Unsubscribe;\n on: (config: MessageConfig) => Unsubscribe;\n onConnect: (listener: Listener<'onConnect'>) => Unsubscribe;\n onDisconnect: (listener: Listener<'onDisconnect'>) => Unsubscribe;\n onMessagesSet: (listener: Listener<'onMessagesSet'>) => Unsubscribe;\n getAgentById: (id: AgentId) => Agent | null;\n getAgentByLocation: (location: BrowserLocation) => Agent | null;\n queryAgents: (location: Partial<BrowserLocation>) => Agent[];\n}\n\nexport function source(\n namespace: string = 'porter',\n options?: PorterSourceOptions\n): PorterAPI {\n const instance = PorterSource.getInstance(namespace, options);\n return {\n type: 'source',\n post: instance.post.bind(instance),\n onMessage: instance.onMessage.bind(instance),\n on: instance.on.bind(instance),\n onConnect: instance.onConnect.bind(instance),\n onDisconnect: instance.onDisconnect.bind(instance),\n onMessagesSet: instance.onMessagesSet.bind(instance),\n getAgentById: instance.getAgentById.bind(instance),\n getAgentByLocation: instance.getAgentByLocation.bind(instance),\n queryAgents: instance.queryAgents.bind(instance),\n };\n}\n", "import browser from 'webextension-polyfill';\n\nexport type AgentId = string;\n\nexport enum PorterContext {\n ContentScript = 'contentscript',\n Extension = 'extension',\n Popup = 'popup',\n Sidepanel = 'sidepanel',\n Devtools = 'devtools',\n Options = 'options',\n Unknown = 'unknown',\n}\n\n// Describes how the connection was established\nexport enum ConnectionType {\n NewTab = 'new-tab',\n NewFrame = 'new-frame',\n Refresh = 'refresh',\n NewExtensionContext = 'new-extension-context',\n}\n\n// export interface AgentLocation {\n// context: PorterContext;\n// tabId?: number;\n// frameId?: number;\n// customIdentifier?: string;\n// }\n\nexport interface AgentInfo {\n id: AgentId;\n location: BrowserLocation;\n createdAt: number;\n lastActiveAt: number;\n}\n\nexport type Agent = {\n port?: browser.Runtime.Port;\n info: AgentInfo;\n};\n\n// export type AgentTarget = {\n// id?: AgentId;\n// context?: PorterContext;\n// location?: AgentLocation;\n// };\n\nexport type BrowserLocation = {\n context: PorterContext;\n tabId: number;\n frameId: number;\n};\n\nexport type MessageTarget =\n | BrowserLocation // Target a specific location\n | PorterContext // Target all agents in a specific context\n | string // Target agent by unique id (advanced)\n | number; // Target a content script by tabId (all frames)\n\nexport type Unsubscribe = () => void;\n\nexport type Message<K extends keyof MessageAction> = {\n action: K;\n target?: MessageTarget;\n payload?: MessageAction[K];\n};\n\nexport type MessageAction = {\n [key: string]: any;\n};\n\nexport type Listener<T extends keyof PorterEvent> = (\n arg: PorterEvent[T]\n) => void;\n\nexport type MessageListener = {\n config: MessageConfig;\n listener: Listener<'onMessage'>;\n};\n\nexport type MessageConfig = {\n [K in keyof MessageAction]: (message: Message<K>, info?: AgentInfo) => void;\n};\n\n// export type GetAgentOptions = {\n// context?: PorterContext;\n// index?: number;\n// subIndex?: number;\n// };\n\nexport interface PorterEvent {\n onConnect: AgentInfo;\n onDisconnect: AgentInfo;\n onMessagesSet: AgentInfo;\n onMessage: AgentInfo & { message: Message<any> };\n}\n\nexport enum PorterErrorType {\n CONNECTION_FAILED = 'connection-failed',\n CONNECTION_TIMEOUT = 'connection-timeout',\n INVALID_TARGET = 'invalid-target',\n MESSAGE_FAILED = 'message-failed',\n INVALID_CONTEXT = 'invalid-context',\n INVALID_PORT = 'invalid-port',\n}\n\nexport class PorterError extends Error {\n constructor(\n public type: PorterErrorType,\n message: string,\n public details?: any\n ) {\n super(message);\n this.name = 'PorterError';\n }\n}\n", "import { Runtime } from 'webextension-polyfill';\n\nfunction isServiceWorker() {\n return (\n typeof ServiceWorkerGlobalScope !== 'undefined' &&\n self instanceof ServiceWorkerGlobalScope\n );\n}\n\nfunction isValidPort(port: Runtime.Port): port is Runtime.Port & {\n sender: Runtime.MessageSender & { tab: { id: number }; frameId: number };\n} {\n return !!port && !!port.sender && isValidSender(port.sender);\n}\n\nfunction isValidSender(\n sender: Runtime.MessageSender\n): sender is Runtime.MessageSender & { tab: { id: number }; frameId: number } {\n return !(\n !sender ||\n !sender.tab ||\n sender.frameId === undefined ||\n sender.tab.id === undefined\n );\n}\n\nexport enum LogLevel {\n ERROR = 0,\n WARN = 1,\n INFO = 2,\n DEBUG = 3,\n TRACE = 4,\n}\n\nexport interface LoggerOptions {\n level?: LogLevel;\n enabled?: boolean;\n}\n\nexport class Logger {\n private static level: LogLevel = Logger.getLevel();\n private static enabled: boolean = false;\n private static instances: Map<string, Logger> = new Map();\n private static globalOptions?: LoggerOptions;\n\n private static getLevel(): LogLevel {\n if (Logger.globalOptions?.level !== undefined) {\n return Logger.globalOptions.level;\n }\n const isProd =\n typeof process !== 'undefined' &&\n (process.env?.NODE_ENV === 'production' ||\n process.env?.PORTER_ENV === 'production');\n return isProd ? LogLevel.WARN : LogLevel.TRACE;\n }\n // Add a configure method to set global options\n static configure(options: LoggerOptions) {\n Logger.globalOptions = options;\n if (options.level !== undefined) {\n Logger.level = options.level;\n }\n if (options.enabled !== undefined) {\n Logger.enabled = options.enabled;\n }\n }\n\n // Factory method to get or create logger instance\n static getLogger(context: string): Logger {\n if (!this.instances.has(context)) {\n this.instances.set(context, new Logger(context));\n }\n return this.instances.get(context)!;\n }\n\n private constructor(private context: string) {}\n\n error(message: string, ...args: any[]) {\n if (!Logger.enabled) return;\n if (Logger.level >= LogLevel.ERROR) {\n console.error(`[Porter:${this.context}] ${message}`, ...args);\n }\n }\n\n warn(message: string, ...args: any[]) {\n if (!Logger.enabled) return;\n if (Logger.level >= LogLevel.WARN) {\n console.warn(`[Porter:${this.context}] ${message}`, ...args);\n }\n }\n\n info(message: string, ...args: any[]) {\n if (!Logger.enabled) return;\n if (Logger.level >= LogLevel.INFO) {\n console.info(`[Porter:${this.context}] ${message}`, ...args);\n }\n }\n\n debug(message: string, ...args: any[]) {\n if (!Logger.enabled) return;\n if (Logger.level >= LogLevel.DEBUG) {\n console.debug(`[Porter:${this.context}] ${message}`, ...args);\n }\n }\n\n trace(message: string, ...args: any[]) {\n if (!Logger.enabled) return;\n if (Logger.level >= LogLevel.TRACE) {\n console.trace(`[Porter:${this.context}] ${message}`, ...args);\n }\n }\n}\n\nexport { isValidPort, isValidSender, isServiceWorker };\n", "import browser, { Runtime } from 'webextension-polyfill';\nimport { v4 as uuidv4 } from 'uuid';\nimport {\n Agent,\n AgentInfo,\n ConnectionType,\n PorterContext,\n MessageTarget,\n AgentId,\n BrowserLocation,\n} from '../porter.model';\nimport { Logger } from '../porter.utils';\n\nexport interface AgentOperations {\n addAgent(port: Runtime.Port, context?: PorterContext): AgentId | undefined;\n queryAgents(location: Partial<BrowserLocation>): Agent[];\n getAgentById(id: AgentId): Agent | null;\n getAgentsByContext(context: PorterContext): Agent[];\n getAgentByLocation(location: BrowserLocation): Agent | null;\n getAllAgents(): Agent[];\n getAllAgentsInfo(): AgentInfo[];\n hasPort(port: Runtime.Port): boolean;\n removeAgent(agentId: AgentId): void;\n printAgents(): void;\n}\n\nexport interface AgentEventEmitter {\n on(event: 'agentSetup', handler: (agent: Agent) => void): void;\n on(\n event: 'agentMessage',\n handler: (message: any, info: AgentInfo) => void\n ): void;\n on(event: 'agentDisconnect', handler: (info: AgentInfo) => void): void;\n}\n\nexport class AgentManager implements AgentOperations, AgentEventEmitter {\n private agents: Map<AgentId, Runtime.Port> = new Map();\n private agentsInfo: Map<AgentId, AgentInfo> = new Map();\n private eventHandlers: Map<string, Set<Function>> = new Map();\n\n constructor(private logger: Logger) {\n this.eventHandlers.set('agentSetup', new Set());\n this.eventHandlers.set('agentMessage', new Set());\n this.eventHandlers.set('agentDisconnect', new Set());\n }\n\n public addAgent(\n port: Runtime.Port,\n context?: PorterContext\n ): AgentId | undefined {\n this.logger.debug(`Adding agent`, { context, port });\n const connectionSource = this.identifyConnectionSource(port);\n if (!connectionSource) {\n this.logger.error(`Cannot add agent that did not have a sender`);\n return;\n }\n\n const determinedContext = connectionSource.context;\n const tabId = connectionSource.tabId || -1;\n const frameId = connectionSource.frameId || 0;\n\n this.logger.debug(`Determined context for new agent`, {\n determinedContext,\n tabId,\n frameId,\n });\n // Find agents in the same tab or under the same extension context\n const tabAgentsInfo = Array.from(this.agentsInfo.values()).filter(\n (info) => {\n return (\n info.location.context === determinedContext &&\n info.location.tabId === tabId &&\n info.location.frameId === frameId\n );\n }\n );\n\n if (tabAgentsInfo.length > 0) {\n this.logger.debug('Adding agent: Found existing similar agent.', {\n tabAgentsInfo,\n });\n }\n\n const agentId =\n this.getAgentByLocation({ context: determinedContext, tabId, frameId })\n ?.info?.id || (uuidv4() as AgentId);\n\n this.logger.debug(`Adding agent with id: ${agentId}`);\n\n this.agents.set(agentId, port);\n\n const agentInfo: AgentInfo = {\n id: agentId,\n location: { context: determinedContext, tabId, frameId },\n createdAt: Date.now(),\n lastActiveAt: Date.now(),\n };\n this.agentsInfo.set(agentId, agentInfo);\n\n this.logger.debug(`Constructed agent info: ${JSON.stringify(agentInfo)}`);\n port.onMessage.addListener((message: any) =>\n this.emit('agentMessage', message, agentInfo)\n );\n\n const agent: Agent = { port, info: agentInfo };\n port.onDisconnect.addListener(() => {\n this.emit('agentDisconnect', agentInfo);\n this.logger.debug('Agent disconnected, removing from manager. ', {\n agentInfo,\n });\n this.removeAgent(agentId);\n });\n\n this.emit('agentSetup', agent);\n this.logger.debug('Setup complete for adding agent. ', {\n agentInfo,\n });\n return agentId;\n }\n\n public getAgentByLocation(location: BrowserLocation): Agent | null {\n const { context, tabId, frameId } = location;\n\n const infoEntry: [AgentId, AgentInfo] | undefined = Array.from(\n this.agentsInfo.entries()\n ).find(\n ([key, info]) =>\n info.location.context === context &&\n info.location.tabId === tabId &&\n info.location.frameId === frameId\n );\n if (infoEntry === undefined) {\n this.logger.error('No agent found for location. ', {\n location,\n });\n return null;\n }\n const agentId = infoEntry[0];\n let port = this.agents.get(agentId);\n let info = this.agentsInfo.get(agentId);\n if (!port || !info) {\n this.logger.error('No agent found for location. ', {\n location,\n });\n return null;\n }\n return { port, info };\n }\n\n public getAgentsByContext(context: PorterContext): Agent[] {\n let infoForAgents = Array.from(this.agentsInfo.entries()).filter(\n ([key, value]) => value.location.context === context\n );\n return infoForAgents.map(([key, value]) => ({\n port: this.agents.get(key),\n info: value,\n }));\n }\n\n public getAllAgents(): Agent[] {\n let allInfo = Array.from(this.agentsInfo.entries());\n return allInfo.map(([key, value]) => ({\n port: this.agents.get(key),\n info: value,\n }));\n }\n\n public queryAgents(location: Partial<BrowserLocation>): Agent[] {\n let infoForAgents = Array.from(this.agentsInfo.entries()).filter(\n ([key, value]) => {\n const hasContext = location.context\n ? value.location.context === location.context\n : true;\n const hasTabId = location.tabId\n ? value.location.tabId === location.tabId\n : true;\n const hasFrameId = location.frameId\n ? value.location.frameId === location.frameId\n : true;\n return hasContext && hasTabId && hasFrameId;\n }\n );\n return infoForAgents.map(([key, value]) => ({\n port: this.agents.get(key),\n info: value,\n }));\n }\n\n public getAgentById(id: AgentId): Agent | null {\n let port = this.agents.get(id);\n let info = this.agentsInfo.get(id);\n if (!port || !info) {\n this.logger.error('No agent found for agentId. ', {\n id,\n });\n return null;\n }\n return { port, info };\n }\n\n public getAllAgentsInfo(): AgentInfo[] {\n return Array.from(this.agentsInfo.values());\n }\n\n public hasPort(port: Runtime.Port): boolean {\n const matchingPort = Array.from(this.agents.values()).find(\n (p) => p.name === port.name\n );\n return !!matchingPort;\n }\n\n public removeAgent(agentId: AgentId) {\n if (this.agents.has(agentId) && this.agentsInfo.has(agentId)) {\n this.agents.delete(agentId);\n this.agentsInfo.delete(agentId);\n } else {\n this.logger.error('No agent found to remove. ', {\n agentId,\n });\n }\n }\n\n public printAgents() {\n const allAgents = Array.from(this.agents.entries());\n const allAgentsInfo = Array.from(this.agentsInfo.entries());\n this.logger.debug('Current agents:', {\n allAgents,\n allAgentsInfo,\n });\n }\n\n // private isContentScript(port: Runtime.Port) {\n // if (!port.sender) return false;\n // const hasFrame =\n // port.sender.tab &&\n // port.sender.tab.id !== undefined &&\n // port.sender.frameId !== undefined;\n // if (!hasFrame) return false;\n // if (!(port.sender as any).origin) return false;\n\n // const contentPage =\n // !(port.sender as any)!.origin.startsWith('chrome-extension://') &&\n // !(port.sender as any)!.tab!.url?.startsWith('moz-extension://');\n // return contentPage;\n // }\n\n public on(event: string, handler: Function) {\n const handlers = this.eventHandlers.get(event);\n if (handlers) {\n handlers.add(handler);\n }\n }\n\n private emit(event: string, ...args: any[]) {\n const handlers = this.eventHandlers.get(event);\n handlers?.forEach((handler) => handler(...args));\n }\n\n private identifyConnectionSource(port: Runtime.Port): {\n context: PorterContext;\n tabId?: number;\n frameId?: number;\n url?: string;\n portName?: string;\n } | null {\n const sender = port.sender;\n if (!sender) {\n this.logger.error(`Cannot add agent that did not have a sender`);\n return null;\n }\n // Cache the manifest data\n const manifest = browser.runtime.getManifest();\n\n // Extract page URLs from manifest\n const sidePanel = (manifest as any)?.side_panel?.default_path || '';\n const optionsPage = (manifest as any).options_page || '';\n const popupPage = (manifest as any).action?.default_popup || '';\n const devtoolsPage = (manifest as any).devtools_page || '';\n const newTabOverride = (manifest as any).chrome_url_overrides?.newtab || '';\n const bookmarksOverride =\n (manifest as any).chrome_url_overrides?.bookmarks || '';\n const historyOverride =\n (manifest as any).chrome_url_overrides?.history || '';\n\n // Create URL endings for matching\n // (handles both full paths and just filenames)\n const pageMatchers = {\n sidepanel: sidePanel ? sidePanel.split('/').pop() : 'sidepanel.html',\n options: optionsPage ? optionsPage.split('/').pop() : 'options.html',\n popup: popupPage ? popupPage.split('/').pop() : 'popup.html',\n devtools: devtoolsPage ? devtoolsPage.split('/').pop() : 'devtools.html',\n newtab: newTabOverride ? newTabOverride.split('/').pop() : 'newtab.html',\n bookmarks: bookmarksOverride\n ? bookmarksOverride.split('/').pop()\n : 'bookmarks.html',\n history: historyOverride\n ? historyOverride.split('/').pop()\n : 'history.html',\n };\n\n // Content scripts (web pages)\n if (sender.tab && sender.url && !sender.url.includes('extension://')) {\n return {\n context: PorterContext.ContentScript,\n tabId: sender.tab.id,\n frameId: sender.frameId || 0,\n url: sender.url,\n portName: port.name,\n };\n }\n\n // Extension pages\n if (sender.url && sender.url.includes('extension://')) {\n const urlPath = new URL(sender.url).pathname;\n const filename = urlPath.split('/').pop();\n\n // Check against our manifest-derived page matchers\n for (const [pageType, pageMatcher] of Object.entries(pageMatchers)) {\n if (filename === pageMatcher) {\n // It's a main extension page\n // Different handling based on presence of tab\n\n return {\n context: pageType as PorterContext,\n tabId: sender.tab?.id || 0,\n frameId: sender.frameId || 0,\n url: sender.url,\n portName: port.name,\n };\n }\n }\n\n // It's some other extension page not specifically listed in our matchers\n\n return {\n context: PorterContext.Unknown,\n tabId: sender.tab?.id || 0,\n frameId: sender.frameId || 0,\n url: sender.url,\n portName: port.name,\n };\n }\n\n // Fallback for unknown sources\n return {\n context: PorterContext.Unknown,\n tabId: 0,\n url: sender.url,\n portName: port.name,\n };\n }\n}\n", "import { Runtime } from 'webextension-polyfill';\nimport { AgentOperations } from './AgentManager';\nimport {\n Agent,\n AgentInfo,\n PorterError,\n PorterErrorType,\n} from '../porter.model';\nimport { Logger } from '../porter.utils';\n\nexport class ConnectionManager {\n constructor(\n private agentOperations: AgentOperations,\n private namespace: string,\n private logger: Logger\n ) {}\n\n public handleConnection(port: Runtime.Port) {\n try {\n this.logger.info('New connection request:', port.name);\n if (!port.name) {\n throw new PorterError(\n PorterErrorType.INVALID_CONTEXT,\n 'Port name not provided'\n );\n }\n\n if (!port.name || !port.name.startsWith(this.namespace + ':')) {\n throw new PorterError(\n PorterErrorType.INVALID_CONTEXT,\n `Invalid namespace or port name format. port name was ${port?.name || 'port undefined'} but namespace is ${this.namespace}`\n );\n }\n\n port.onMessage.addListener(this.handleInitMessage.bind(this, port));\n\n setTimeout(() => {\n if (!this.agentOperations.hasPort(port)) {\n try {\n port.disconnect();\n } catch (e) {\n this.logger.error('Failed to disconnect port:', e);\n }\n }\n }, 5000);\n } catch (error) {\n this.handleConnectionError(port, error as Error);\n }\n }\n\n private handleInitMessage(port: Runtime.Port, message: any): void {\n // Process only the init message\n if (message.action !== 'porter-init') {\n return;\n }\n\n try {\n // Remove this listener since we only need it once\n port.onMessage.removeListener(this.handleInitMessage.bind(this, port));\n\n const { connectionId } = message.payload;\n\n if (!connectionId) {\n throw new PorterError(\n PorterErrorType.INVALID_CONTEXT,\n 'Missing context or connection ID. Message was: ' +\n JSON.stringify(message)\n );\n }\n\n // Now add the agent with the provided context\n const agentId = this.agentOperations.addAgent(port);\n\n if (!agentId) {\n throw new PorterError(\n PorterErrorType.INVALID_CONTEXT,\n 'Failed to add agent'\n );\n }\n\n // Get the agent info to send back\n const agent = this.agentOperations.getAgentById(agentId);\n\n if (agent) {\n this.confirmConnection(agent);\n }\n\n this.agentOperations.printAgents();\n } catch (error) {\n this.handleConnectionError(port, error as Error);\n }\n }\n\n private handleConnectionError(port: Runtime.Port, error: Error): void {\n const porterError =\n error instanceof PorterError\n ? error\n : new PorterError(\n PorterErrorType.CONNECTION_FAILED,\n error instanceof Error ? error.message : 'Unknown connection error',\n { originalError: error }\n );\n this.logger.error('Connection handling failed: ', {\n porterError,\n });\n try {\n port.postMessage({\n action: 'porter-error',\n payload: { error: porterError },\n });\n } catch (e) {\n this.logger.error('Failed to send error message: ', {\n error: e,\n });\n }\n\n try {\n port.disconnect();\n } catch (e) {\n this.logger.error('Failed to disconnect port: ', {\n error: e,\n });\n }\n }\n\n public confirmConnection(agent: Agent) {\n this.logger.debug('Sending confirmation message back to initiator ', {\n agent,\n });\n if (!agent.port) {\n throw new PorterError(\n PorterErrorType.INVALID_PORT,\n 'Agent port is undefined when confirming connection'\n );\n }\n agent.port.postMessage({\n action: 'porter-handshake',\n payload: {\n info: agent.info,\n currentConnections: this.agentOperations.getAllAgentsInfo(),\n },\n });\n }\n}\n", "import { Runtime } from 'webextension-polyfill';\nimport { AgentOperations } from './AgentManager';\nimport {\n PorterEvent,\n Listener,\n MessageListener,\n Message,\n PorterErrorType,\n MessageConfig,\n PorterContext,\n PorterError,\n AgentInfo,\n MessageTarget,\n BrowserLocation,\n AgentId,\n} from '../porter.model';\nimport { Logger } from '../porter.utils';\n\nexport class MessageHandler {\n private eventListeners: Map<\n keyof PorterEvent,\n Set<Listener<keyof PorterEvent>>\n > = new Map();\n private messageListeners: Set<MessageListener> = new Set();\n private initializationHandler: MessageConfig;\n\n constructor(\n private agentOperations: AgentOperations,\n private logger: Logger\n ) {\n this.initializationHandler = {\n 'porter-messages-established': (\n message: Message<any>,\n agent?: AgentInfo\n ) => {\n if (!agent || !agent.id) return;\n const agentInfo = this.agentOperations.getAgentById(agent.id)?.info;\n if (!agentInfo) {\n this.logger.error('No agent info found for agent id: ', agent.id);\n return;\n }\n this.logger.debug(\n 'internalHandlers, established message received: ',\n agent.id,\n message\n );\n this.emitEvent('onMessagesSet', agentInfo);\n },\n };\n }\n\n public async post(\n message: Message<any>,\n target?: MessageTarget\n ): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n this.logger.debug('Post request received:', {\n action: message.action,\n target,\n });\n\n const timeoutId = setTimeout(() => {\n const error = new Error('Message posting timed out');\n this.logger.error('Post timeout:', error);\n reject(error);\n }, 5000);\n\n if (target === undefined) {\n this.broadcastMessage(message);\n // how to tell if target is BrowserLocation type?\n } else if (isBrowserLocation(target)) {\n this.postToLocation(message, target);\n } else if (isPorterContext(target)) {\n this.postToContext(message, target);\n } else if (typeof target === 'string') {\n this.postToId(message, target);\n } else {\n this.postToTab(message, target);\n }\n\n clearTimeout(timeoutId);\n resolve();\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error';\n this.logger.error('Failed to post message:', errorMessage);\n reject(new Error(`Failed to post message: ${errorMessage}`));\n }\n });\n }\n\n private broadcastMessage(message: Message<any>): void {\n this.logger.info('Broadcasting message to all agents: ', message);\n this.agentOperations.getAllAgents().forEach((agent) => {\n if (agent.port) {\n agent.port.postMessage(message);\n }\n });\n }\n\n // Post to all frames in a tab\n private postToTab(message: Message<any>, tabId: number): void {\n // const key = `${PorterContext.ContentScript}:${tabId}:0`;\n const agents = this.agentOperations.queryAgents({\n context: PorterContext.ContentScript,\n tabId,\n });\n if (agents.length === 0) {\n this.logger.warn('post: No agents found for tab: ', tabId);\n throw new PorterError(\n PorterErrorType.MESSAGE_FAILED,\n `Failed to post message to tabId ${tabId}`,\n { originalError: message }\n );\n return;\n }\n agents.forEach((agent) => {\n if (agent.port) {\n this.postToPort(message, agent.port);\n }\n });\n }\n\n private postToLocation(\n message: Message<any>,\n location: BrowserLocation\n ): void {\n const agents = this.agentOperations.queryAgents(location);\n agents.forEach((agent) => {\n if (!agent.port) {\n throw new PorterError(\n PorterErrorType.INVALID_TARGET,\n `No port found for agent`,\n { agentInfo: agent.info }\n );\n return;\n }\n this.postToPort(message, agent.port);\n });\n }\n\n private postToContext(message: Message<any>, context: PorterContext): void {\n const agents = this.agentOperations.queryAgents({\n context,\n });\n agents.forEach((agent) => {\n if (!agent.port) {\n throw new PorterError(\n PorterErrorType.INVALID_TARGET,\n `No port found for agent`,\n { agentInfo: agent.info }\n );\n return;\n }\n this.postToPort(message, agent.port);\n });\n }\n\n private postToPort(message: Message<any>, port: Runtime.Port): void {\n try {\n port.postMessage(message);\n } catch (error) {\n throw new PorterError(\n PorterErrorType.MESSAGE_FAILED,\n `Failed to post message to port`,\n { originalError: error, message }\n );\n }\n }\n\n private postToId(message: Message<any>, agentId: AgentId): void {\n const agent = this.agentOperations.getAgentById(agentId);\n if (!agent?.port) {\n throw new PorterError(\n PorterErrorType.INVALID_TARGET,\n `No agent found for key: ${agentId}`\n );\n }\n this.postToPort(message, agent.port);\n }\n\n public onMessage(config: MessageConfig) {\n // Optionally: Check for existing listeners with same config\n const existingListener = Array.from(this.messageListeners).find(\n (listener) => JSON.stringify(listener.config) === JSON.stringify(config)\n );\n\n if (existingListener) {\n this.logger.warn(\n `Listener with same config already exists: ${JSON.stringify(config)}`\n );\n }\n\n const messageListener: MessageListener = {\n config,\n listener: (event: PorterEvent['onMessage']) => {\n const handler = config[event.message.action];\n if (handler) {\n this.logger.debug('onMessage, calling handler ', { event });\n const { message, ...info } = event;\n handler(message, info);\n } else {\n this.logger.debug('onMessage, no handler found ', { event });\n }\n },\n };\n this.messageListeners.add(messageListener);\n\n return () => {\n this.messageListeners.delete(messageListener);\n };\n }\n\n // Adding new 'on' method that works the same way as onMessage\n public on(config: MessageConfig) {\n return this.onMessage(config);\n }\n\n // Handles messages incomng from ports\n public handleIncomingMessage(message: any, info: AgentInfo) {\n this.logger.debug(`Received message`, {\n message,\n info,\n });\n\n this.emitMessage({ ...info, message });\n }\n\n private emitEvent<T extends keyof PorterEvent>(\n event: T,\n arg: PorterEvent[T]\n ) {\n this.logger.debug('emitting event: ', event, arg);\n this.eventListeners\n .get(event)\n ?.forEach((listener) => (listener as Listener<T>)(arg));\n }\n\n // Dispatches incoming messages, either to a registered listener on the source, or to a specific agent\n // if a target was specified (calling this a relay)\n private emitMessage(messageEvent: PorterEvent['onMessage']) {\n this.logger.debug('Dispatching incoming message to subscribers', {\n messageEvent,\n });\n\n if (messageEvent.message.action.startsWith('porter-')) {\n const handler = this.initializationHandler[messageEvent.message.action];\n if (handler) {\n this.logger.debug('Internal message being handled', {\n messageEvent,\n });\n const { message, ...info } = messageEvent;\n handler(message, info);\n return;\n }\n }\n\n // Handle relaying to a target\n if (!!messageEvent.message.target) {\n this.logger.debug(\n 'Relaying message to target:',\n messageEvent.message.target\n );\n this.post(messageEvent.message, messageEvent.message.target);\n }\n\n let handlerCount = 0;\n\n this.logger.trace('Processing message with registered handlers');\n for (const { listener, config } of this.messageListeners) {\n if (config[messageEvent.message.action]) {\n listener(messageEvent as PorterEvent['onMessage']);\n handlerCount++;\n this.logger.debug('Message handled by registered listener: ', {\n listener,\n config,\n });\n }\n }\n\n if (handlerCount === 0) {\n this.logger.warn(\n 'No handler found for message:',\n messageEvent.message.action\n );\n } else {\n this.logger.debug(\n `Message handled by ${handlerCount} registered listeners`\n );\n }\n }\n\n public addListener<T extends keyof PorterEvent>(\n event: T,\n listener: Listener<T>\n ) {\n if (!this.eventListeners.has(event)) {\n this.eventListeners.set(event, new Set());\n }\n this.eventListeners\n .get(event)!\n .add(listener as Listener<keyof PorterEvent>);\n\n return () => {\n this.eventListeners\n .get(event)\n ?.delete(listener as Listener<keyof PorterEvent>);\n };\n }\n\n public handleDisconnect(info: AgentInfo) {\n // Remove all message listeners for this agent\n this.messageListeners.forEach((messageListener) => {\n if (messageListener.config[info.id]) {\n this.messageListeners.delete(messageListener);\n }\n });\n this.logger.info('Agent disconnected:', { info });\n this.emitEvent('onDisconnect', info);\n }\n\n public handleConnect(info: AgentInfo) {\n this.logger.info('Agent connected:', { info });\n this.emitEvent('onConnect', info);\n }\n\n public onConnect(listener: Listener<'onConnect'>) {\n return this.addListener('onConnect', listener);\n }\n\n public onMessagesSet(listener: Listener<'onMessagesSet'>) {\n return this.addListener('onMessagesSet', listener);\n }\n\n public onDisconnect(listener: Listener<'onDisconnect'>) {\n return this.addListener('onDisconnect', listener);\n }\n}\n\n// Type guard for BrowserLocation\nfunction isBrowserLocation(target: MessageTarget): target is BrowserLocation {\n return (\n typeof target === 'object' &&\n target !== null &&\n 'context' in target &&\n 'tabId' in target &&\n 'frameId' in target\n );\n}\n\nfunction isPorterContext(target: MessageTarget): target is PorterContext {\n return (\n typeof target === 'string' &&\n Object.values(PorterContext).includes(target as PorterContext)\n );\n}\n", "import browser, { Runtime } from 'webextension-polyfill';\nimport { AgentInfo, PorterError, PorterErrorType } from '../porter.model';\nimport { Logger } from '../porter.utils';\nimport { MessageQueue } from './MessageQueue';\n\nexport type DisconnectCallback = () => void;\nexport type ReconnectCallback = (info: AgentInfo) => void;\n\nexport class AgentConnectionManager {\n private readonly CONNECTION_TIMEOUT = 5000;\n private readonly RECONNECT_INTERVAL = 1000; // 1 second\n private connectionTimer: NodeJS.Timeout | null = null;\n private reconnectTimer: NodeJS.Timeout | null = null;\n private agentInfo: AgentInfo | null = null;\n private port: Runtime.Port | null = null;\n private readonly logger: Logger;\n private readonly connectionId: string;\n private readonly messageQueue: MessageQueue;\n private isReconnecting: boolean = false;\n private reconnectAttemptCount: number = 0;\n\n // Event callbacks\n private disconnectCallbacks: Set<DisconnectCallback> = new Set();\n private reconnectCallbacks: Set<ReconnectCallback> = new Set();\n\n constructor(\n private readonly namespace: string,\n logger: Logger\n ) {\n this.connectionId = `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;\n this.logger = logger;\n this.messageQueue = new MessageQueue(logger);\n }\n\n /**\n * Register a callback to be called when the connection is lost\n * @returns Unsubscribe function\n */\n public onDisconnect(callback: DisconnectCallback): () => void {\n this.disconnectCallbacks.add(callback);\n return () => {\n this.disconnectCallbacks.delete(callback);\n };\n }\n\n /**\n * Register a callback to be called when reconnection succeeds\n * @returns Unsubscribe function\n */\n public onReconnect(callback: ReconnectCallback): () => void {\n this.reconnectCallbacks.add(callback);\n return () => {\n this.reconnectCallbacks.delete(callback);\n };\n }\n\n private emitDisconnect(): void {\n this.logger.debug('Emitting disconnect event', {\n callbackCount: this.disconnectCallbacks.size,\n });\n this.disconnectCallbacks.forEach((callback) => {\n try {\n callback();\n } catch (error) {\n this.logger.error('Error in disconnect callback:', error);\n }\n });\n }\n\n private emitReconnect(info: AgentInfo): void {\n this.logger.debug('Emitting reconnect event', {\n callbackCount: this.reconnectCallbacks.size,\n info,\n });\n this.reconnectCallbacks.forEach((callback) => {\n try {\n callback(info);\n } catch (error) {\n this.logger.error('Error in reconnect callback:', error);\n }\n });\n }\n\n public async initializeConnection(): Promise<void> {\n try {\n if (this.connectionTimer) {\n clearTimeout(this.connectionTimer);\n }\n\n const portName = `${this.namespace}:${this.connectionId}`;\n this.logger.debug('Connecting new port with name: ', { portName });\n this.port = browser.runtime.connect({ name: portName });\n\n const handshakePromise = new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(\n () =>\n reject(\n new PorterError(\n PorterErrorType.CONNECTION_TIMEOUT,\n 'Connection timed out waiting for handshake'\n )\n ),\n this.CONNECTION_TIMEOUT\n );\n\n const onMessage = (message: any) => {\n if (message.action === 'porter-handshake') {\n this.logger.debug('Received handshake:', message);\n clearTimeout(timeout);\n this.agentInfo = message.payload.info;\n this.logger.debug('Handshake agent info:', {\n agentInfo: this.agentInfo,\n });\n this.port?.onMessage.removeListener(onMessage);\n resolve();\n } else if (message.action === 'porter-error') {\n clearTimeout(timeout);\n this.port?.onMessage.removeListener(onMessage);\n this.logger.error('Error:', message);\n reject(\n new PorterError(\n message.payload.type,\n message.payload.message,\n message.payload.details\n )\n );\n }\n };\n\n this.port?.onMessage.addListener(onMessage);\n });\n\n this.port?.postMessage({\n action: 'porter-init',\n payload: {\n info: this.agentInfo,\n connectionId: this.connectionId,\n },\n });\n\n await handshakePromise;\n\n // After successful connection, process any queued messages\n await this.processQueuedMessages();\n } catch (error) {\n this.logger.error('Connection initialization failed:', error);\n this.handleDisconnect(this.port!);\n throw error;\n }\n }\n\n private async processQueuedMessages(): Promise<void> {\n if (!this.port || this.messageQueue.isEmpty()) {\n return;\n }\n\n const messages = this.messageQueue.dequeue();\n this.logger.info(\n `Processing ${messages.length} queued messages after reconnection`\n );\n\n for (const { message, target } of messages) {\n try {\n // Send message in the same format as normal messages\n const messageToSend = { ...message };\n if (target) {\n messageToSend.target = target;\n }\n this.port.postMessage(messageToSend);\n this.logger.debug('Successfully resent queued message:', {\n message: messageToSend,\n });\n } catch (error) {\n this.logger.error('Failed to process queued message:', error);\n // Re-queue the message if it fails\n this.messageQueue.enqueue(message, target);\n this.logger.debug('Re-queued failed message for retry');\n }\n }\n }\n\n public getPort(): Runtime.Port | null {\n return this.port;\n }\n\n public getAgentInfo(): AgentInfo | null {\n return this.agentInfo;\n }\n\n public getNamespace(): string {\n return this.namespace;\n }\n\n public handleDisconnect(port: Runtime.Port) {\n this.logger.info('Port disconnected', {\n portName: port.name,\n connectionId: this.connectionId,\n queuedMessages: this.messageQueue.isEmpty() ? 0 : 'some',\n });\n this.port = null;\n this.agentInfo = null;\n\n // Notify listeners of disconnection\n this.emitDisconnect();\n\n // Start reconnection attempts if not already reconnecting\n if (!this.isReconnecting) {\n this.startReconnectionAttempts();\n }\n }\n\n private startReconnectionAttempts(): void {\n this.isReconnecting = true;\n this.reconnectAttemptCount = 0;\n\n if (this.reconnectTimer) {\n clearInterval(this.reconnectTimer);\n }\n\n this.logger.info('Starting reconnection attempts', {\n interval: this.RECONNECT_INTERVAL,\n queuedMessages: this.messageQueue.isEmpty() ? 0 : 'some',\n });\n\n this.reconnectTimer = setInterval(async () => {\n this.reconnectAttemptCount++;\n try {\n this.logger.debug(`Reconnection attempt ${this.reconnectAttemptCount}`);\n await this.initializeConnection();\n this.isReconnecting = false;\n if (this.reconnectTimer) {\n clearInterval(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n this.logger.info('Reconnection successful', {\n attempts: this.reconnectAttemptCount,\n queuedMessages: this.messageQueue.isEmpty() ? 0 : 'some',\n });\n\n // Notify listeners of successful reconnection\n if (this.agentInfo) {\n this.emitReconnect(this.agentInfo);\n }\n } catch (error) {\n this.logger.debug(\n `Reconnection attempt ${this.reconnectAttemptCount} failed:`,\n error\n );\n }\n }, this.RECONNECT_INTERVAL);\n }\n\n public queueMessage(message: any, target?: any): void {\n this.messageQueue.enqueue(message, target);\n this.logger.debug('Message queued for retry', {\n message,\n target,\n queueSize: this.messageQueue.isEmpty() ? 0 : 'some',\n });\n }\n}\n", "import { Message, BrowserLocation } from '../porter.model';\nimport { Logger } from '../porter.utils';\n\ninterface QueuedMessage {\n message: Message<any>;\n target?: BrowserLocation;\n timestamp: number;\n}\n\nexport class MessageQueue {\n private queue: QueuedMessage[] = [];\n private readonly logger: Logger;\n private readonly maxQueueSize: number = 1000; // Prevent memory issues\n private readonly maxMessageAge: number = 5 * 60 * 1000; // 5 minutes\n\n constructor(logger: Logger) {\n this.logger = logger;\n this.logger.debug('MessageQueue initialized', {\n maxQueueSize: this.maxQueueSize,\n maxMessageAge: `${this.maxMessageAge / 1000} seconds`,\n });\n }\n\n public enqueue(message: Message<any>, target?: BrowserLocation): void {\n // Remove old messages\n const oldCount = this.queue.length;\n this.cleanup();\n if (oldCount !== this.queue.length) {\n this.logger.debug(\n `Cleaned up ${oldCount - this.queue.length} old messages`\n );\n }\n\n // Check if queue is full\n if (this.queue.length >= this.maxQueueSize) {\n this.logger.warn('Message queue is full, dropping oldest message', {\n queueSize: this.queue.length,\n maxSize: this.maxQueueSize,\n });\n this.queue.shift();\n }\n\n this.queue.push({\n message,\n target,\n timestamp: Date.now(),\n });\n\n this.logger.debug('Message queued', {\n queueSize: this.queue.length,\n message,\n target,\n timestamp: new Date().toISOString(),\n });\n }\n\n public dequeue(): QueuedMessage[] {\n const messages = [...this.queue];\n this.queue = [];\n this.logger.info(`Dequeued ${messages.length} messages`, {\n oldestMessage: messages[0]\n ? new Date(messages[0].timestamp).toISOString()\n : null,\n newestMessage: messages[messages.length - 1]\n ? new Date(messages[messages.length - 1].timestamp).toISOString()\n : null,\n });\n return messages;\n }\n\n public isEmpty(): boolean {\n return this.queue.length === 0;\n }\n\n private cleanup(): void {\n const now = Date.now();\n const oldCount = this.queue.length;\n this.queue = this.queue.filter(\n (item) => now - item.timestamp < this.maxMessageAge\n );\n if (oldCount !== this.queue.length) {\n this.logger.debug(\n `Cleaned up ${oldCount - this.queue.length} expired messages`,\n {\n remaining: this.queue.length,\n maxAge: `${this.maxMessageAge / 1000} seconds`,\n }\n );\n }\n }\n}\n", "import { Runtime } from 'webextension-polyfill';\nimport {\n BrowserLocation,\n Message,\n MessageConfig,\n PorterError,\n PorterErrorType,\n} from '../porter.model';\nimport { Logger } from '../porter.utils';\n\nexport class AgentMessageHandler {\n private readonly MAX_QUEUE_SIZE = 1000;\n private readonly MESSAGE_TIMEOUT = 30000;\n private messageQueue: Array<{ message: Message<any>; timestamp: number }> =\n [];\n private handlers: Map<string, Array<Function>> = new Map();\n\n constructor(private readonly logger: Logger) {}\n\n public handleMessage(port: Runtime.Port, message: any) {\n this.logger.debug('handleMessage, message: ', message);\n if (this.handlers.size === 0) {\n if (this.messageQueue.length >= this.MAX_QUEUE_SIZE) {\n this.logger.warn('Message queue full, dropping message:', message);\n return;\n }\n this.logger.warn(\n 'No message handlers configured yet, queueing message: ',\n message\n );\n this.messageQueue.push({ message, timestamp: Date.now() });\n return;\n }\n this.processMessage(port, message);\n }\n\n // Legacy method - internally uses the new system\n public onMessage(config: MessageConfig) {\n this.logger.debug('Setting message handlers from config: ', config);\n // Clear previous handlers to maintain backward compatibility\n this.handlers.clear();\n this.on(config);\n\n this.processQueuedMessages();\n }\n\n public on(config: MessageConfig) {\n this.logger.debug('Adding message handlers from config: ', config);\n\n Object.entries(config).forEach(([action, handler]) => {\n if (!this.handlers.has(action)) {\n this.handlers.set(action, []);\n }\n this.handlers.get(action)!.push(handler);\n });\n\n this.processQueuedMessages();\n }\n\n private processQueuedMessages() {\n while (this.messageQueue.length > 0) {\n const item = this.messageQueue[0];\n if (Date.now() - item.timestamp > this.MESSAGE_TIMEOUT) {\n this.logger.warn(\n 'Message timeout, dropping message: ',\n this.messageQueue.shift()\n );\n continue;\n }\n this.processMessage(null!, item.message);\n this.messageQueue.shift();\n }\n }\n\n private processMessage(port: Runtime.Port, message: any) {\n const action = message.action;\n const actionHandlers = this.handlers.get(action) || [];\n\n if (actionHandlers.length > 0) {\n this.logger.debug(\n `Found ${actionHandlers.length} handlers for action: ${action}`\n );\n actionHandlers.forEach((handler) => handler(message));\n } else {\n this.logger.debug(`No handlers for message with action: ${action}`);\n }\n }\n\n public post(\n port: Runtime.Port,\n message: Message<any>,\n target?: BrowserLocation\n ) {\n this.logger.debug(`Sending message`, {\n action: message.action,\n target,\n hasPayload: !!message.payload,\n });\n\n try {\n if (target) {\n message.target = target;\n }\n port.postMessage(message);\n } catch (error) {\n throw new PorterError(\n PorterErrorType.MESSAGE_FAILED,\n 'Failed to post message',\n { originalError: error, message, target }\n );\n }\n }\n}\n", "import {\n AgentInfo,\n BrowserLocation,\n Message,\n MessageConfig,\n PorterContext,\n Unsubscribe,\n} from '../porter.model';\nimport {\n AgentConnectionManager,\n DisconnectCallback,\n ReconnectCallback,\n} from '../managers/AgentConnectionManager';\nimport { AgentMessageHandler } from '../managers/AgentMessageHandler';\nimport { Logger } from '../porter.utils';\n\nexport interface AgentAPI {\n type: 'agent';\n post: (message: Message<any>, target?: BrowserLocation) => void;\n onMessage: (config: MessageConfig) => void;\n on: (config: MessageConfig) => void;\n getAgentInfo: () => AgentInfo | null;\n onDisconnect: (callback: DisconnectCallback) => Unsubscribe;\n onReconnect: (callback: ReconnectCallback) => Unsubscribe;\n}\n\nexport interface PorterAgentOptions {\n agentContext?: PorterContext;\n namespace?: string;\n debug?: boolean;\n}\n\nexport class PorterAgent {\n private static instance: PorterAgent | null = null;\n private readonly connectionManager: AgentConnectionManager;\n private readonly messageHandler: AgentMessageHandler;\n private readonly logger: Logger;\n\n private constructor(options: PorterAgentOptions = {}) {\n const namespace = options.namespace ?? 'porter';\n const context = options.agentContext ?? this.determineContext();\n\n if (options.debug !== undefined) {\n Logger.configure({ enabled: options.debug });\n }\n\n this.logger = Logger.getLogger('Agent');\n\n this.connectionManager = new AgentConnectionManager(namespace, this.logger);\n this.messageHandler = new AgentMessageHandler(this.logger);\n\n // Listen for reconnection events to re-wire port listeners\n this.connectionManager.onReconnect((info) => {\n this.logger.info('Reconnected, re-wiring port listeners', { info });\n this.setupPortListeners();\n });\n\n this.logger.info('Initializing with options: ', { options, context });\n this.initializeConnection();\n }\n\n public static getInstance(options: PorterAgentOptions = {}): PorterAgent {\n if (\n !PorterAgent.instance ||\n PorterAgent.instance.connectionManager.getNamespace() !==\n options.namespace\n ) {\n PorterAgent.instance = new PorterAgent(options);\n } else if (options.debug !== undefined) {\n Logger.configure({ enabled: options.debug });\n }\n return PorterAgent.instance;\n }\n\n private async initializeConnection(): Promise<void> {\n await this.connectionManager.initializeConnection();\n this.setupPortListeners();\n }\n\n /**\n * Set up message and disconnect listeners on the current port.\n * Called after initial connection and after each reconnection.\n */\n private setupPortListeners(): void {\n const port = this.connectionManager.getPort();\n if (port) {\n this.logger.debug('Setting up port listeners');\n port.onMessage.addListener((message: any) =>\n this.messageHandler.handleMessage(port, message)\n );\n port.onDisconnect.addListener((p) =>\n this.connectionManager.handleDisconnect(p)\n );\n } else {\n this.logger.warn('Cannot setup port listeners: no port available');\n }\n }\n\n public onMessage(config: MessageConfig) {\n this.messageHandler.onMessage(config);\n const port = this.connectionManager.getPort();\n port?.postMessage({ action: 'porter-messages-established' });\n }\n\n public on(config: MessageConfig) {\n this.messageHandler.on(config);\n const port = this.connectionManager.getPort();\n port?.postMessage({ action: 'porter-messages-established' });\n }\n\n public post(message: Message<any>, target?: BrowserLocation) {\n const port = this.connectionManager.getPort();\n this.logger.debug('Posting message', { message, target, port });\n\n if (port) {\n try {\n this.messageHandler.post(port, message, target);\n } catch (error) {\n this.logger.error('Failed to post message, queueing for retry', {\n error,\n });\n this.connectionManager.queueMessage(message, target);\n }\n } else {\n this.logger.debug('No port found, queueing message', { message, target });\n this.connectionManager.queueMessage(message, target);\n }\n }\n\n private determineContext(): PorterContext {\n if (!window.location.protocol.includes('extension')) {\n return PorterContext.ContentScript;\n }\n return PorterContext.Extension;\n }\n\n public getAgentInfo(): AgentInfo | null {\n return this.connectionManager.getAgentInfo() || null;\n }\n\n /**\n * Register a callback to be called when the connection to the service worker is lost\n * @returns Unsubscribe function\n */\n public onDisconnect(callback: DisconnectCallback): Unsubscribe {\n return this.connectionManager.onDisconnect(callback);\n }\n\n /**\n * Register a callback to be called when reconnection to the service worker succeeds\n * @returns Unsubscribe function\n */\n public onReconnect(callback: ReconnectCallback): Unsubscribe {\n return this.connectionManager.onReconnect(callback);\n }\n}\n\nexport function connect(options?: PorterAgentOptions): AgentAPI {\n const porterInstance = PorterAgent.getInstance(options);\n return {\n type: 'agent',\n post: porterInstance.post.bind(porterInstance),\n onMessage: porterInstance.onMessage.bind(porterInstance),\n on: porterInstance.on.bind(porterInstance),\n getAgentInfo: porterInstance.getAgentInfo.bind(porterInstance),\n onDisconnect: porterInstance.onDisconnect.bind(porterInstance),\n onReconnect: porterInstance.onReconnect.bind(porterInstance),\n };\n}\n", "import { useState, useEffect, useCallback, useRef, useMemo } from 'react';\nimport { connect, AgentAPI } from '../core/PorterAgent';\nimport {\n AgentInfo,\n Message,\n MessageConfig,\n PorterContext,\n Unsubscribe,\n} from '../porter.model';\n\ninterface UsePorterResult {\n post: (message: Message<any>) => void;\n on: (handlers: MessageConfig) => void;\n isConnected: boolean;\n isReconnecting: boolean;\n error: Error | null;\n agentInfo: AgentInfo | null;\n}\n\nexport function usePorter(options?: {\n agentContext?: PorterContext;\n namespace?: string;\n debug?: boolean;\n onDisconnect?: () => void;\n onReconnect?: (info: AgentInfo) => void;\n}): UsePorterResult {\n const [isConnected, setIsConnected] = useState<boolean>(false);\n const [isReconnecting, setIsReconnecting] = useState<boolean>(false);\n const [error, setError] = useState<Error | null>(null);\n const [agentInfo, setAgentInfo] = useState<AgentInfo | null>(null);\n const postRef = useRef<((message: Message<any>) => void) | null>(null);\n const onRef = useRef<((handlers: MessageConfig) => void) | null>(null);\n const getAgentInfoRef = useRef<(() => AgentInfo | null) | null>(null);\n const unsubscribeRefs = useRef<Unsubscribe[]>([]);\n\n const memoizedOptions = useMemo(\n () => ({\n agentContext: options?.agentContext,\n namespace: options?.namespace,\n debug: options?.debug,\n }),\n [options?.agentContext, options?.namespace, options?.debug]\n );\n\n // Store callbacks in refs to avoid re-running effect when they change\n const onDisconnectRef = useRef(options?.onDisconnect);\n const onReconnectRef = useRef(options?.onReconnect);\n onDisconnectRef.current = options?.onDisconnect;\n onReconnectRef.current = options?.onReconnect;\n\n useEffect(() => {\n let isMounted = true;\n\n const initializePorter = async () => {\n try {\n const { post, on, getAgentInfo, onDisconnect, onReconnect } =\n connect(memoizedOptions);\n\n if (isMounted) {\n postRef.current = post;\n onRef.current = on;\n getAgentInfoRef.current = getAgentInfo;\n setIsConnected(true);\n setIsReconnecting(false);\n setError(null);\n\n // Set up disconnect handler\n const unsubDisconnect = onDisconnect(() => {\n if (isMounted) {\n setIsConnected(false);\n setIsReconnecting(true);\n setAgentInfo(null);\n onDisconnectRef.current?.();\n }\n });\n unsubscribeRefs.current.push(unsubDisconnect);\n\n // Set up reconnect handler\n const unsubReconnect = onReconnect((info: AgentInfo) => {\n if (isMounted) {\n setIsConnected(true);\n setIsReconnecting(false);\n setAgentInfo(info);\n onReconnectRef.current?.(info);\n }\n });\n unsubscribeRefs.current.push(unsubReconnect);\n\n // Set up internal porter-handshake handler\n on({\n 'porter-handshake': (message: Message<any>) => {\n if (isMounted) {\n setAgentInfo(message.payload.info);\n }\n },\n });\n }\n } catch (err) {\n if (isMounted) {\n console.error('[PORTER] initializePorter error ', err);\n setError(\n err instanceof Error\n ? err\n : new Error('Failed to connect to Porter')\n );\n setIsConnected(false);\n setIsReconnecting(false);\n }\n }\n };\n\n initializePorter();\n\n return () => {\n isMounted = false;\n // Clean up all subscriptions\n unsubscribeRefs.current.forEach((unsub) => unsub());\n unsubscribeRefs.current = [];\n };\n }, [memoizedOptions]);\n\n const post = useCallback((message: Message<any>) => {\n if (postRef.current) {\n try {\n postRef.current(message);\n } catch (err) {\n setError(\n err instanceof Error ? err : new Error('Failed to send message')\n );\n }\n } else {\n setError(new Error('Porter is not connected'));\n }\n }, []);\n\n const on = useCallback((handlers: MessageConfig) => {\n if (onRef.current) {\n try {\n onRef.current(handlers);\n } catch (err) {\n setError(\n err instanceof Error\n ? err\n : new Error('Failed to set message handlers')\n );\n }\n } else {\n setError(new Error('Porter is not connected'));\n }\n }, []);\n\n return { post, on, isConnected, isReconnecting, error, agentInfo };\n}\n", "/**\n * Crann Error Classes\n *\n * Custom error types for better debugging and error handling.\n */\n\n/**\n * Base error class for all Crann errors.\n */\nexport class CrannError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"CrannError\";\n // Maintains proper stack trace in V8 environments\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Thrown when there's an issue with the config schema.\n */\nexport class ConfigError extends CrannError {\n constructor(message: string, public readonly field?: string) {\n super(field ? `Config error in '${field}': ${message}` : `Config error: ${message}`);\n this.name = \"ConfigError\";\n }\n}\n\n/**\n * Thrown when a store or agent has been destroyed/disconnected.\n */\nexport class LifecycleError extends CrannError {\n constructor(\n public readonly storeName: string,\n public readonly entity: \"store\" | \"agent\"\n ) {\n super(\n `${entity === \"store\" ? \"Store\" : \"Agent\"} \"${storeName}\" has been ${\n entity === \"store\" ? \"destroyed\" : \"disconnected\"\n } and cannot be used.`\n );\n this.name = \"LifecycleError\";\n }\n}\n\n/**\n * Thrown when there's a connection issue.\n */\nexport class ConnectionError extends CrannError {\n constructor(\n message: string,\n public readonly storeName: string,\n public readonly reason?: string\n ) {\n super(`Connection error for store \"${storeName}\": ${message}`);\n this.name = \"ConnectionError\";\n }\n}\n\n/**\n * Thrown when an action execution fails.\n */\nexport class ActionError extends CrannError {\n constructor(\n public readonly actionName: string,\n public readonly storeName: string,\n public readonly reason: string\n ) {\n super(`Action \"${actionName}\" failed in store \"${storeName}\": ${reason}`);\n this.name = \"ActionError\";\n }\n}\n\n/**\n * Thrown when action validation fails.\n */\nexport class ValidationError extends CrannError {\n constructor(\n public readonly actionName: string,\n public readonly storeName: string,\n public readonly reason: string\n ) {\n super(\n `Validation failed for action \"${actionName}\" in store \"${storeName}\": ${reason}`\n );\n this.name = \"ValidationError\";\n }\n}\n\n/**\n * Thrown when there's a storage key collision.\n */\nexport class StorageCollisionError extends CrannError {\n constructor(public readonly storeName: string) {\n super(\n `Store name \"${storeName}\" is already in use. Each store must have a unique name. ` +\n `If you're trying to connect to an existing store, use connectStore() instead.`\n );\n this.name = \"StorageCollisionError\";\n }\n}\n\n", "/**\n * Crann v2 Agent\n *\n * Client-side connection to a Crann store.\n * Runs in content scripts, popup, sidepanel, etc.\n */\n\nimport { connect as connectPorter } from \"../transport\";\nimport type { AgentInfo } from \"../transport\";\nimport type {\n ConfigSchema,\n ValidatedConfig,\n DerivedState,\n DerivedActions,\n StateChanges,\n} from \"../store/types\";\nimport type {\n AgentOptions,\n AgentAPI,\n AgentConnectionInfo,\n StateSubscriber,\n} from \"./types\";\nimport { LifecycleError } from \"../errors\";\n\n// =============================================================================\n// Agent Class\n// =============================================================================\n\nexport class Agent<TConfig extends ConfigSchema> implements AgentAPI<TConfig> {\n private readonly config: ValidatedConfig<TConfig>;\n private readonly options: AgentOptions;\n private readonly porter: ReturnType<typeof connectPorter>;\n\n private _state: DerivedState<TConfig>;\n private _agentInfo: AgentInfo | null = null;\n private _isConnected = false;\n private _isDisconnected = false;\n\n private readonly subscribers: Set<StateSubscriber<TConfig>> = new Set();\n private readonly readyCallbacks: Set<(state: DerivedState<TConfig>) => void> = new Set();\n private readonly disconnectCallbacks: Set<() => void> = new Set();\n private readonly reconnectCallbacks: Set<(state: DerivedState<TConfig>) => void> = new Set();\n\n private readyPromise: Promise<DerivedState<TConfig>>;\n private readyResolve!: (state: DerivedState<TConfig>) => void;\n\n private readonly actionsProxy: DerivedActions<TConfig>;\n\n constructor(config: ValidatedConfig<TConfig>, options: AgentOptions = {}) {\n this.config = config;\n this.options = options;\n\n // Initialize state with defaults\n this._state = this.buildDefaultState();\n\n // Create ready promise\n this.readyPromise = new Promise((resolve) => {\n this.readyResolve = resolve;\n });\n\n // Connect to store via porter (using store name as namespace)\n this.porter = connectPorter({\n namespace: config.name,\n debug: options.debug ?? false,\n });\n\n // Set up message handlers\n this.setupMessageHandlers();\n\n // Create typed actions proxy\n this.actionsProxy = this.createActionsProxy();\n }\n\n // ===========================================================================\n // Public API - Connection\n // ===========================================================================\n\n ready(): Promise<DerivedState<TConfig>> {\n this.assertNotDisconnected();\n return this.readyPromise;\n }\n\n onReady(callback: (state: DerivedState<TConfig>) => void): () => void {\n this.assertNotDisconnected();\n \n // If already connected, call immediately\n if (this._isConnected) {\n setTimeout(() => callback(this._state), 0);\n }\n \n this.readyCallbacks.add(callback);\n return () => {\n this.readyCallbacks.delete(callback);\n };\n }\n\n // ===========================================================================\n // Public API - State\n // ===========================================================================\n\n getState(): DerivedState<TConfig> {\n this.assertNotDisconnected();\n return { ...this._state };\n }\n\n get state(): DerivedState<TConfig> {\n return this.getState();\n }\n\n async setState(state: Partial<DerivedState<TConfig>>): Promise<void> {\n this.assertNotDisconnected();\n \n // Optimistically update local state\n this._state = { ...this._state, ...state };\n \n // Send to store\n this.porter.post({\n action: \"setState\",\n payload: { state },\n });\n }\n\n // ===========================================================================\n // Public API - Subscriptions\n // ===========================================================================\n\n subscribe(\n callbackOrKeys: ((changes: StateChanges<TConfig>, state: DerivedState<TConfig>) => void) | Array<keyof DerivedState<TConfig>>,\n maybeCallback?: (changes: StateChanges<TConfig>, state: DerivedState<TConfig>) => void\n ): () => void {\n this.assertNotDisconnected();\n\n let keys: Array<keyof DerivedState<TConfig>> | undefined;\n let callback: (changes: StateChanges<TConfig>, state: DerivedState<TConfig>) => void;\n\n if (typeof callbackOrKeys === \"function\") {\n callback = callbackOrKeys;\n } else {\n keys = callbackOrKeys;\n callback = maybeCallback!;\n }\n\n const subscriber: StateSubscriber<TConfig> = { keys, callback };\n this.subscribers.add(subscriber);\n\n return () => {\n this.subscribers.delete(subscriber);\n };\n }\n\n // ===========================================================================\n // Public API - Actions\n // ===========================================================================\n\n get actions(): DerivedActions<TConfig> {\n this.assertNotDisconnected();\n return this.actionsProxy;\n }\n\n // ===========================================================================\n // Public API - Agent Info\n // ===========================================================================\n\n getInfo(): AgentConnectionInfo | null {\n if (!this._agentInfo) return null;\n \n return {\n id: this._agentInfo.id,\n tabId: this._agentInfo.location.tabId,\n frameId: this._agentInfo.location.frameId,\n context: this._agentInfo.location.context,\n };\n }\n\n // ===========================================================================\n // Public API - Lifecycle\n // ===========================================================================\n\n onDisconnect(callback: () => void): () => void {\n this.assertNotDisconnected();\n this.disconnectCallbacks.add(callback);\n return () => {\n this.disconnectCallbacks.delete(callback);\n };\n }\n\n onReconnect(callback: (state: DerivedState<TConfig>) => void): () => void {\n this.assertNotDisconnected();\n this.reconnectCallbacks.add(callback);\n return () => {\n this.reconnectCallbacks.delete(callback);\n };\n }\n\n disconnect(): void {\n if (this._isDisconnected) return;\n\n this._isDisconnected = true;\n this._isConnected = false;\n\n // Clear all callbacks\n this.subscribers.clear();\n this.readyCallbacks.clear();\n this.disconnectCallbacks.clear();\n this.reconnectCallbacks.clear();\n\n // Note: Porter doesn't have a disconnect method, but we've cleaned up our state\n }\n\n // ===========================================================================\n // Private - Message Handling\n // ===========================================================================\n\n private setupMessageHandlers(): void {\n // Handle initial state from store\n this.porter.on({\n initialState: (message) => {\n const { state, info } = message.payload;\n \n this._state = state;\n this._agentInfo = info;\n this._isConnected = true;\n\n // Resolve ready promise\n this.readyResolve(this._state);\n\n // Notify ready callbacks\n this.readyCallbacks.forEach((cb) => {\n try {\n cb(this._state);\n } catch (e) {\n console.error(\"[Crann Agent] Error in onReady callback:\", e);\n }\n });\n\n // Notify initial subscribers\n this.notifySubscribers(state);\n },\n\n stateUpdate: (message) => {\n const { state: changes } = message.payload;\n this._state = { ...this._state, ...changes };\n this.notifySubscribers(changes);\n },\n\n rpcResult: (message) => {\n // RPC results are handled by the action proxy's pending promises\n // This is handled in createActionsProxy\n },\n });\n\n // Handle disconnect/reconnect from porter\n this.porter.onDisconnect(() => {\n this._isConnected = false;\n this.disconnectCallbacks.forEach((cb) => {\n try {\n cb();\n } catch (e) {\n console.error(\"[Crann Agent] Error in onDisconnect callback:\", e);\n }\n });\n });\n\n this.porter.onReconnect((info: AgentInfo) => {\n this._agentInfo = info;\n this._isConnected = true;\n this.reconnectCallbacks.forEach((cb) => {\n try {\n cb(this._state);\n } catch (e) {\n console.error(\"[Crann Agent] Error in onReconnect callback:\", e);\n }\n });\n });\n }\n\n private notifySubscribers(changes: StateChanges<TConfig>): void {\n this.subscribers.forEach((subscriber) => {\n // If subscriber has specific keys, check if any changed\n if (subscriber.keys) {\n const hasRelevantChange = subscriber.keys.some(\n (key) => key in changes\n );\n if (!hasRelevantChange) return;\n }\n\n try {\n subscriber.callback(changes, this._state);\n } catch (e) {\n console.error(\"[Crann Agent] Error in subscriber callback:\", e);\n }\n });\n }\n\n // ===========================================================================\n // Private - Actions Proxy\n // ===========================================================================\n\n private createActionsProxy(): DerivedActions<TConfig> {\n const pendingCalls = new Map<string, { resolve: (v: any) => void; reject: (e: any) => void }>();\n let callId = 0;\n\n // Listen for RPC results\n this.porter.on({\n rpcResult: (message) => {\n const { callId: id, result, error, success } = message.payload;\n const pending = pendingCalls.get(id);\n if (pending) {\n pendingCalls.delete(id);\n if (success) {\n pending.resolve(result);\n } else {\n pending.reject(new Error(error));\n }\n }\n },\n });\n\n return new Proxy({} as DerivedActions<TConfig>, {\n get: (_target, actionName: string) => {\n return async (...args: unknown[]) => {\n const id = `${callId++}`;\n \n return new Promise((resolve, reject) => {\n pendingCalls.set(id, { resolve, reject });\n \n this.porter.post({\n action: \"rpc\",\n payload: {\n callId: id,\n actionName,\n args,\n },\n });\n });\n };\n },\n });\n }\n\n // ===========================================================================\n // Private - Utilities\n // ===========================================================================\n\n private buildDefaultState(): DerivedState<TConfig> {\n const state: Record<string, unknown> = {};\n\n for (const [key, item] of Object.entries(this.config)) {\n if (key === \"name\" || key === \"version\" || key === \"actions\") continue;\n if (typeof item === \"object\" && item !== null && \"default\" in item) {\n state[key] = item.default;\n }\n }\n\n return state as DerivedState<TConfig>;\n }\n\n private assertNotDisconnected(): void {\n if (this._isDisconnected) {\n throw new LifecycleError(this.config.name, \"agent\");\n }\n }\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\n/**\n * Connect to a Crann store from a client context.\n *\n * This should be called in content scripts, popup, sidepanel, or other\n * extension contexts that need to access the store. Uses the config's\n * `name` to find and connect to the matching store in the service worker.\n *\n * @param config - Validated config from createConfig() (must match store's config)\n * @param options - Optional connection options\n * @param options.debug - Enable debug logging\n * @returns An Agent instance for interacting with the store\n *\n * @example\n * // content.ts\n * import { createConfig, connectStore } from 'crann';\n *\n * const config = createConfig({\n * name: 'myFeature',\n * count: { default: 0, persist: 'local' },\n * });\n *\n * const agent = connectStore(config);\n *\n * // Wait for connection and initial state\n * const state = await agent.ready();\n * console.log('Initial count:', state.count);\n *\n * // Subscribe to changes\n * agent.subscribe((changes, state) => {\n * console.log('State changed:', changes);\n * });\n *\n * // Call actions\n * await agent.actions.increment();\n */\nexport function connectStore<TConfig extends ConfigSchema>(\n config: ValidatedConfig<TConfig>,\n options?: AgentOptions\n): AgentAPI<TConfig> {\n return new Agent(config, options);\n}\n\n"],
5
- "mappings": "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",
6
- "names": ["react_exports", "__export", "createCrannHooks", "__toCommonJS", "import_react", "import_webextension_polyfill", "PorterContext", "PorterError", "type", "message", "details", "isServiceWorker", "_Logger", "context", "_a", "_b", "_c", "options", "message", "args", "Logger", "import_webextension_polyfill", "import_uuid", "AgentManager", "logger", "port", "context", "_a", "_b", "connectionSource", "determinedContext", "tabId", "frameId", "tabAgentsInfo", "info", "agentId", "uuidv4", "agentInfo", "message", "agent", "location", "infoEntry", "key", "value", "hasContext", "hasTabId", "hasFrameId", "id", "p", "allAgents", "allAgentsInfo", "event", "handler", "handlers", "args", "_c", "_d", "_e", "_f", "_g", "sender", "manifest", "browser", "sidePanel", "optionsPage", "popupPage", "devtoolsPage", "newTabOverride", "bookmarksOverride", "historyOverride", "pageMatchers", "filename", "pageType", "pageMatcher", "ConnectionManager", "agentOperations", "namespace", "logger", "port", "PorterError", "e", "error", "message", "connectionId", "agentId", "agent", "porterError", "MessageHandler", "agentOperations", "logger", "message", "agent", "_a", "agentInfo", "target", "resolve", "reject", "timeoutId", "error", "isBrowserLocation", "isPorterContext", "errorMessage", "tabId", "agents", "PorterError", "location", "context", "port", "agentId", "config", "listener", "messageListener", "event", "handler", "info", "arg", "messageEvent", "handlerCount", "PorterContext", "_PorterSource", "namespace", "options", "Logger", "AgentManager", "MessageHandler", "ConnectionManager", "isServiceWorker", "PorterError", "message", "metadata", "agent", "browser", "target", "config", "listener", "key", "_a", "agentId", "location", "PorterSource", "import_webextension_polyfill", "MessageQueue", "logger", "message", "target", "oldCount", "messages", "now", "item", "AgentConnectionManager", "namespace", "logger", "MessageQueue", "callback", "error", "info", "_a", "portName", "browser", "handshakePromise", "resolve", "reject", "timeout", "PorterError", "onMessage", "message", "_b", "messages", "target", "messageToSend", "port", "AgentMessageHandler", "logger", "port", "message", "config", "action", "handler", "item", "actionHandlers", "target", "error", "PorterError", "_PorterAgent", "options", "namespace", "context", "Logger", "AgentConnectionManager", "AgentMessageHandler", "info", "port", "message", "p", "config", "target", "error", "callback", "PorterAgent", "connect", "porterInstance", "import_react", "CrannError", "message", "LifecycleError", "CrannError", "storeName", "entity", "Agent", "config", "options", "resolve", "connect", "callback", "state", "callbackOrKeys", "maybeCallback", "keys", "subscriber", "message", "info", "cb", "e", "changes", "key", "pendingCalls", "callId", "id", "result", "error", "success", "pending", "_target", "actionName", "args", "reject", "item", "LifecycleError", "connectStore", "import_jsx_runtime", "createCrannHooks", "config", "options", "moduleAgent", "AgentContext", "getAgent", "connectStore", "useAgent", "contextAgent", "agent", "setAgent", "useCrannReady", "isReady", "setIsReady", "unsubReady", "unsubDisconnect", "unsubReconnect", "useCrannState", "selectorOrKey", "isFunction", "selector", "state", "selectedValue", "setSelectedValue", "defaultState", "buildDefaultState", "prevSelectedRef", "currentState", "newSelected", "shallowEqual", "changes", "key", "setValue", "value", "useCrannActions", "agentRef", "actionsRef", "_target", "actionName", "args", "currentAgent", "children", "item", "a", "b", "keysA", "keysB"]
4
+ "sourcesContent": ["/**\n * Crann React Integration\n *\n * Provides React hooks for connecting to and using a Crann store.\n *\n * @example\n * import { createCrannHooks } from 'crann/react';\n * import { config } from './config';\n *\n * export const { useCrannState, useCrannActions, useCrannReady } = createCrannHooks(config);\n */\n\nexport { createCrannHooks } from \"./hooks\";\n\nexport type {\n CrannHooks,\n CreateCrannHooksOptions,\n UseCrannStateSelector,\n UseCrannStateTuple,\n} from \"./types\";\n\n", "/**\n * Crann React Hooks Implementation\n *\n * Provides React hooks for connecting to and using a Crann store.\n */\n\nimport {\n createContext,\n useContext,\n useState,\n useEffect,\n useRef,\n useCallback,\n useMemo,\n type ReactNode,\n type FC,\n} from \"react\";\nimport { connectStore } from \"../agent\";\nimport type { AgentAPI } from \"../agent/types\";\nimport type {\n ConfigSchema,\n ValidatedConfig,\n DerivedState,\n DerivedActions,\n} from \"../store/types\";\nimport type { CrannHooks, CreateCrannHooksOptions, UseCrannStateTuple } from \"./types\";\n\n/**\n * Creates a set of React hooks for a Crann store.\n *\n * This is the main entry point for React integration. Call once at module\n * level with your config, then use the returned hooks in your components.\n *\n * @param config - Validated config from createConfig()\n * @param options - Optional hook options\n * @returns Object containing all Crann React hooks\n *\n * @example\n * // hooks.ts\n * import { createConfig } from 'crann';\n * import { createCrannHooks } from 'crann/react';\n *\n * const config = createConfig({\n * name: 'myFeature',\n * count: { default: 0 },\n * actions: {\n * increment: { handler: async (ctx) => ctx.setState({ count: ctx.state.count + 1 }) },\n * },\n * });\n *\n * export const { useCrannState, useCrannActions, useCrannReady } = createCrannHooks(config);\n *\n * // MyComponent.tsx\n * function Counter() {\n * const count = useCrannState(s => s.count);\n * const { increment } = useCrannActions();\n * const isReady = useCrannReady();\n *\n * if (!isReady) return <div>Loading...</div>;\n *\n * return <button onClick={() => increment()}>Count: {count}</button>;\n * }\n */\nexport function createCrannHooks<TConfig extends ConfigSchema>(\n config: ValidatedConfig<TConfig>,\n options: CreateCrannHooksOptions = {}\n): CrannHooks<TConfig> {\n // Module-level agent instance (created lazily)\n let moduleAgent: AgentAPI<TConfig> | null = null;\n\n // Create React context for optional provider override\n const AgentContext = createContext<AgentAPI<TConfig> | null>(null);\n\n /**\n * Get or create the agent instance.\n */\n function getAgent(): AgentAPI<TConfig> {\n if (!moduleAgent) {\n moduleAgent = connectStore(config, { debug: options.debug });\n }\n return moduleAgent;\n }\n\n /**\n * Hook to get the current agent (from context or module-level).\n */\n function useAgent(): AgentAPI<TConfig> | null {\n const contextAgent = useContext(AgentContext);\n const [agent, setAgent] = useState<AgentAPI<TConfig> | null>(\n contextAgent ?? moduleAgent\n );\n\n useEffect(() => {\n if (!agent) {\n setAgent(getAgent());\n }\n }, [agent]);\n\n return contextAgent ?? agent;\n }\n\n /**\n * Hook to check if connection is ready.\n */\n function useCrannReady(): boolean {\n const agent = useAgent();\n const [isReady, setIsReady] = useState(false);\n\n useEffect(() => {\n if (!agent) return;\n\n // Check if already ready\n const info = agent.getInfo();\n if (info) {\n setIsReady(true);\n }\n\n // Subscribe to ready event\n const unsubReady = agent.onReady(() => {\n setIsReady(true);\n });\n\n // Subscribe to disconnect/reconnect\n const unsubDisconnect = agent.onDisconnect(() => {\n setIsReady(false);\n });\n\n const unsubReconnect = agent.onReconnect(() => {\n setIsReady(true);\n });\n\n return () => {\n unsubReady();\n unsubDisconnect();\n unsubReconnect();\n };\n }, [agent]);\n\n return isReady;\n }\n\n /**\n * Hook to read state with selector or key pattern.\n */\n function useCrannState<TSelected>(\n selector: (state: DerivedState<TConfig>) => TSelected\n ): TSelected;\n function useCrannState<K extends keyof DerivedState<TConfig>>(\n key: K\n ): UseCrannStateTuple<TConfig, K>;\n function useCrannState<TSelected, K extends keyof DerivedState<TConfig>>(\n selectorOrKey: ((state: DerivedState<TConfig>) => TSelected) | K\n ): TSelected | UseCrannStateTuple<TConfig, K> {\n const agent = useAgent();\n const isFunction = typeof selectorOrKey === \"function\";\n\n // For selector pattern\n const selector = isFunction\n ? (selectorOrKey as (state: DerivedState<TConfig>) => TSelected)\n : (state: DerivedState<TConfig>) => state[selectorOrKey as K] as unknown as TSelected;\n\n // Track selected value\n const [selectedValue, setSelectedValue] = useState<TSelected>(() => {\n if (!agent) {\n // Return default from config\n const defaultState = buildDefaultState(config);\n return selector(defaultState);\n }\n return selector(agent.getState());\n });\n\n // Store previous selected value for comparison\n const prevSelectedRef = useRef(selectedValue);\n\n useEffect(() => {\n if (!agent) return;\n\n // Update with current state\n const currentState = agent.getState();\n const newSelected = selector(currentState);\n if (!shallowEqual(newSelected, prevSelectedRef.current)) {\n prevSelectedRef.current = newSelected;\n setSelectedValue(newSelected);\n }\n\n // Subscribe to changes\n const unsubscribe = agent.subscribe((changes, state) => {\n const newSelected = selector(state);\n if (!shallowEqual(newSelected, prevSelectedRef.current)) {\n prevSelectedRef.current = newSelected;\n setSelectedValue(newSelected);\n }\n });\n\n return unsubscribe;\n }, [agent, selector]);\n\n // For key pattern, return tuple\n if (!isFunction) {\n const key = selectorOrKey as K;\n const setValue = useCallback(\n async (value: DerivedState<TConfig>[K]) => {\n if (agent) {\n await agent.setState({ [key]: value } as Partial<DerivedState<TConfig>>);\n }\n },\n [agent, key]\n );\n\n return [selectedValue as DerivedState<TConfig>[K], setValue] as UseCrannStateTuple<TConfig, K>;\n }\n\n return selectedValue;\n }\n\n /**\n * Hook to get typed actions with stable references.\n *\n * The returned object and all action methods are referentially stable,\n * so they can safely be used in dependency arrays without causing re-renders.\n */\n function useCrannActions(): DerivedActions<TConfig> {\n const agent = useAgent();\n\n // Store agent in ref so proxy can access current value\n const agentRef = useRef<AgentAPI<TConfig> | null>(null);\n agentRef.current = agent;\n\n // Cache for action wrappers to ensure stable references\n const actionsCacheRef = useRef<Record<string, (...args: unknown[]) => Promise<unknown>>>({});\n\n // Return stable proxy that delegates to agent.actions\n const actionsRef = useRef<DerivedActions<TConfig> | null>(null);\n\n if (!actionsRef.current) {\n actionsRef.current = new Proxy({} as DerivedActions<TConfig>, {\n get: (_target, actionName: string) => {\n // Return cached wrapper if it exists, otherwise create and cache it\n if (!actionsCacheRef.current[actionName]) {\n actionsCacheRef.current[actionName] = async (...args: unknown[]) => {\n const currentAgent = agentRef.current;\n if (!currentAgent) {\n throw new Error(\n `Cannot call action \"${actionName}\" before agent is connected`\n );\n }\n return (currentAgent.actions as any)[actionName](...args);\n };\n }\n return actionsCacheRef.current[actionName];\n },\n });\n }\n\n return actionsRef.current;\n }\n\n /**\n * Optional provider for dependency injection.\n */\n const CrannProvider: FC<{ agent?: AgentAPI<TConfig>; children: ReactNode }> = ({\n agent,\n children,\n }) => {\n const value = agent ?? getAgent();\n return <AgentContext.Provider value={value}>{children}</AgentContext.Provider>;\n };\n\n return {\n useCrannState,\n useCrannActions,\n useCrannReady,\n useAgent,\n CrannProvider,\n };\n}\n\n// =============================================================================\n// Utilities\n// =============================================================================\n\n/**\n * Build default state from config.\n */\nfunction buildDefaultState<TConfig extends ConfigSchema>(\n config: ValidatedConfig<TConfig>\n): DerivedState<TConfig> {\n const state: Record<string, unknown> = {};\n\n for (const [key, item] of Object.entries(config)) {\n if (key === \"name\" || key === \"version\" || key === \"actions\") continue;\n if (typeof item === \"object\" && item !== null && \"default\" in item) {\n state[key] = item.default;\n }\n }\n\n return state as DerivedState<TConfig>;\n}\n\n/**\n * Shallow equality check.\n */\nfunction shallowEqual(a: unknown, b: unknown): boolean {\n if (a === b) return true;\n if (typeof a !== \"object\" || typeof b !== \"object\") return false;\n if (a === null || b === null) return false;\n\n const keysA = Object.keys(a);\n const keysB = Object.keys(b);\n\n if (keysA.length !== keysB.length) return false;\n\n for (const key of keysA) {\n if ((a as any)[key] !== (b as any)[key]) return false;\n }\n\n return true;\n}\n\n", "import browser, { Runtime } from 'webextension-polyfill';\nimport {\n AgentInfo,\n Listener,\n Message,\n MessageConfig,\n PorterError,\n PorterErrorType,\n BrowserLocation,\n Unsubscribe,\n AgentId,\n MessageTarget,\n} from '../porter.model';\nimport { Agent } from '../porter.model';\nimport { isServiceWorker } from '../porter.utils';\nimport { Logger } from '../porter.utils';\nimport { AgentManager } from '../managers/AgentManager';\nimport { ConnectionManager } from '../managers/ConnectionManager';\nimport { MessageHandler } from '../managers/MessageHandler';\n\nexport interface PorterSourceOptions {\n namespace?: string;\n debug?: boolean;\n}\n\nexport class PorterSource {\n private static instances: Map<string, PorterSource> = new Map();\n private readonly agentManager: AgentManager;\n private readonly messageHandler: MessageHandler;\n private readonly connectionManager: ConnectionManager;\n private readonly logger: Logger;\n private static staticLogger = Logger.getLogger('SW');\n private namespace: string;\n\n private constructor(namespace?: string, options?: PorterSourceOptions) {\n // Configure logger if debug option is provided\n if (options?.debug !== undefined) {\n Logger.configure({ enabled: options.debug });\n }\n\n this.logger = Logger.getLogger(`SW`);\n this.namespace = namespace || 'porter';\n if (!namespace) {\n this.logger.error('No namespace provided, defaulting to \"porter\"');\n }\n\n this.agentManager = new AgentManager(this.logger);\n this.messageHandler = new MessageHandler(this.agentManager, this.logger);\n this.connectionManager = new ConnectionManager(\n this.agentManager,\n this.namespace,\n this.logger\n );\n this.logger.info(`Constructing Porter with namespace: ${this.namespace}`);\n\n if (!isServiceWorker()) {\n throw new PorterError(\n PorterErrorType.INVALID_CONTEXT,\n 'Can only create in a service worker'\n );\n }\n\n // Wire up event handlers\n this.agentManager.on(\n 'agentMessage',\n (message: any, metadata: AgentInfo) => {\n this.messageHandler.handleIncomingMessage(message, metadata);\n }\n );\n\n this.agentManager.on('agentDisconnect', (metadata: AgentInfo) => {\n this.messageHandler.handleDisconnect(metadata);\n });\n\n this.agentManager.on('agentSetup', (agent: Agent) => {\n this.logger.debug(`Handling agent setup`, { agent });\n this.messageHandler.handleConnect(agent.info);\n this.connectionManager.confirmConnection(agent);\n });\n\n browser.runtime.onConnect.addListener(\n this.connectionManager.handleConnection.bind(this.connectionManager)\n );\n }\n\n public static getInstance(\n namespace: string = 'porter',\n options?: PorterSourceOptions\n ): PorterSource {\n PorterSource.staticLogger.debug(\n `Getting instance for namespace: ${namespace}`\n );\n if (!PorterSource.instances.has(namespace)) {\n PorterSource.staticLogger.info(\n `Creating new instance for namespace: ${namespace}`\n );\n PorterSource.instances.set(\n namespace,\n new PorterSource(namespace, options)\n );\n } else if (options?.debug !== undefined) {\n // If instance exists but debug setting changed, configure logger\n Logger.configure({ enabled: options.debug });\n }\n return PorterSource.instances.get(namespace)!;\n }\n\n // Public API methods that will be exposed via the source function\n public post(message: Message<any>, target?: MessageTarget): Promise<void> {\n return this.messageHandler.post(message, target);\n }\n\n public onMessage(config: MessageConfig): Unsubscribe {\n return this.messageHandler.onMessage(config);\n }\n\n public on(config: MessageConfig): Unsubscribe {\n return this.messageHandler.on(config);\n }\n\n public onConnect(listener: Listener<'onConnect'>): Unsubscribe {\n return this.messageHandler.onConnect(listener);\n }\n\n public onDisconnect(listener: Listener<'onDisconnect'>): Unsubscribe {\n return this.messageHandler.onDisconnect(listener);\n }\n\n public onMessagesSet(listener: Listener<'onMessagesSet'>): Unsubscribe {\n return this.messageHandler.onMessagesSet(listener);\n }\n\n // Utility methods that might be needed externally\n public getInfo(key: string): AgentInfo | null {\n return this.agentManager.getAgentById(key)?.info || null;\n }\n\n // Utility methods that might be needed externally\n public getAgentById(agentId: AgentId): Agent | null {\n return this.agentManager.getAgentById(agentId);\n }\n\n public getAgentByLocation(location: BrowserLocation): Agent | null {\n return this.agentManager.getAgentByLocation(location);\n }\n\n public queryAgents(location: Partial<BrowserLocation>): Agent[] {\n return this.agentManager.queryAgents(location);\n }\n}\n\nexport interface PorterAPI {\n type: 'source';\n post: (message: Message<any>, target?: MessageTarget) => Promise<void>;\n onMessage: (config: MessageConfig) => Unsubscribe;\n on: (config: MessageConfig) => Unsubscribe;\n onConnect: (listener: Listener<'onConnect'>) => Unsubscribe;\n onDisconnect: (listener: Listener<'onDisconnect'>) => Unsubscribe;\n onMessagesSet: (listener: Listener<'onMessagesSet'>) => Unsubscribe;\n getAgentById: (id: AgentId) => Agent | null;\n getAgentByLocation: (location: BrowserLocation) => Agent | null;\n queryAgents: (location: Partial<BrowserLocation>) => Agent[];\n}\n\nexport function source(\n namespace: string = 'porter',\n options?: PorterSourceOptions\n): PorterAPI {\n const instance = PorterSource.getInstance(namespace, options);\n return {\n type: 'source',\n post: instance.post.bind(instance),\n onMessage: instance.onMessage.bind(instance),\n on: instance.on.bind(instance),\n onConnect: instance.onConnect.bind(instance),\n onDisconnect: instance.onDisconnect.bind(instance),\n onMessagesSet: instance.onMessagesSet.bind(instance),\n getAgentById: instance.getAgentById.bind(instance),\n getAgentByLocation: instance.getAgentByLocation.bind(instance),\n queryAgents: instance.queryAgents.bind(instance),\n };\n}\n", "import browser from 'webextension-polyfill';\n\nexport type AgentId = string;\n\nexport enum PorterContext {\n ContentScript = 'contentscript',\n Extension = 'extension',\n Popup = 'popup',\n Sidepanel = 'sidepanel',\n Devtools = 'devtools',\n Options = 'options',\n Unknown = 'unknown',\n}\n\n// Describes how the connection was established\nexport enum ConnectionType {\n NewTab = 'new-tab',\n NewFrame = 'new-frame',\n Refresh = 'refresh',\n NewExtensionContext = 'new-extension-context',\n}\n\n// export interface AgentLocation {\n// context: PorterContext;\n// tabId?: number;\n// frameId?: number;\n// customIdentifier?: string;\n// }\n\nexport interface AgentInfo {\n id: AgentId;\n location: BrowserLocation;\n createdAt: number;\n lastActiveAt: number;\n}\n\nexport type Agent = {\n port?: browser.Runtime.Port;\n info: AgentInfo;\n};\n\n// export type AgentTarget = {\n// id?: AgentId;\n// context?: PorterContext;\n// location?: AgentLocation;\n// };\n\nexport type BrowserLocation = {\n context: PorterContext;\n tabId: number;\n frameId: number;\n};\n\nexport type MessageTarget =\n | BrowserLocation // Target a specific location\n | PorterContext // Target all agents in a specific context\n | string // Target agent by unique id (advanced)\n | number; // Target a content script by tabId (all frames)\n\nexport type Unsubscribe = () => void;\n\nexport type Message<K extends keyof MessageAction> = {\n action: K;\n target?: MessageTarget;\n payload?: MessageAction[K];\n};\n\nexport type MessageAction = {\n [key: string]: any;\n};\n\nexport type Listener<T extends keyof PorterEvent> = (\n arg: PorterEvent[T]\n) => void;\n\nexport type MessageListener = {\n config: MessageConfig;\n listener: Listener<'onMessage'>;\n};\n\nexport type MessageConfig = {\n [K in keyof MessageAction]: (message: Message<K>, info?: AgentInfo) => void;\n};\n\n// export type GetAgentOptions = {\n// context?: PorterContext;\n// index?: number;\n// subIndex?: number;\n// };\n\nexport interface PorterEvent {\n onConnect: AgentInfo;\n onDisconnect: AgentInfo;\n onMessagesSet: AgentInfo;\n onMessage: AgentInfo & { message: Message<any> };\n}\n\nexport enum PorterErrorType {\n CONNECTION_FAILED = 'connection-failed',\n CONNECTION_TIMEOUT = 'connection-timeout',\n INVALID_TARGET = 'invalid-target',\n MESSAGE_FAILED = 'message-failed',\n INVALID_CONTEXT = 'invalid-context',\n INVALID_PORT = 'invalid-port',\n}\n\nexport class PorterError extends Error {\n constructor(\n public type: PorterErrorType,\n message: string,\n public details?: any\n ) {\n super(message);\n this.name = 'PorterError';\n }\n}\n", "import { Runtime } from 'webextension-polyfill';\n\nfunction isServiceWorker() {\n return (\n typeof ServiceWorkerGlobalScope !== 'undefined' &&\n self instanceof ServiceWorkerGlobalScope\n );\n}\n\nfunction isValidPort(port: Runtime.Port): port is Runtime.Port & {\n sender: Runtime.MessageSender & { tab: { id: number }; frameId: number };\n} {\n return !!port && !!port.sender && isValidSender(port.sender);\n}\n\nfunction isValidSender(\n sender: Runtime.MessageSender\n): sender is Runtime.MessageSender & { tab: { id: number }; frameId: number } {\n return !(\n !sender ||\n !sender.tab ||\n sender.frameId === undefined ||\n sender.tab.id === undefined\n );\n}\n\nexport enum LogLevel {\n ERROR = 0,\n WARN = 1,\n INFO = 2,\n DEBUG = 3,\n TRACE = 4,\n}\n\nexport interface LoggerOptions {\n level?: LogLevel;\n enabled?: boolean;\n}\n\nexport class Logger {\n private static level: LogLevel = Logger.getLevel();\n private static enabled: boolean = false;\n private static instances: Map<string, Logger> = new Map();\n private static globalOptions?: LoggerOptions;\n\n private static getLevel(): LogLevel {\n if (Logger.globalOptions?.level !== undefined) {\n return Logger.globalOptions.level;\n }\n const isProd =\n typeof process !== 'undefined' &&\n (process.env?.NODE_ENV === 'production' ||\n process.env?.PORTER_ENV === 'production');\n return isProd ? LogLevel.WARN : LogLevel.TRACE;\n }\n // Add a configure method to set global options\n static configure(options: LoggerOptions) {\n Logger.globalOptions = options;\n if (options.level !== undefined) {\n Logger.level = options.level;\n }\n if (options.enabled !== undefined) {\n Logger.enabled = options.enabled;\n }\n }\n\n // Factory method to get or create logger instance\n static getLogger(context: string): Logger {\n if (!this.instances.has(context)) {\n this.instances.set(context, new Logger(context));\n }\n return this.instances.get(context)!;\n }\n\n private constructor(private context: string) {}\n\n error(message: string, ...args: any[]) {\n if (!Logger.enabled) return;\n if (Logger.level >= LogLevel.ERROR) {\n console.error(`[Porter:${this.context}] ${message}`, ...args);\n }\n }\n\n warn(message: string, ...args: any[]) {\n if (!Logger.enabled) return;\n if (Logger.level >= LogLevel.WARN) {\n console.warn(`[Porter:${this.context}] ${message}`, ...args);\n }\n }\n\n info(message: string, ...args: any[]) {\n if (!Logger.enabled) return;\n if (Logger.level >= LogLevel.INFO) {\n console.info(`[Porter:${this.context}] ${message}`, ...args);\n }\n }\n\n debug(message: string, ...args: any[]) {\n if (!Logger.enabled) return;\n if (Logger.level >= LogLevel.DEBUG) {\n console.debug(`[Porter:${this.context}] ${message}`, ...args);\n }\n }\n\n trace(message: string, ...args: any[]) {\n if (!Logger.enabled) return;\n if (Logger.level >= LogLevel.TRACE) {\n console.trace(`[Porter:${this.context}] ${message}`, ...args);\n }\n }\n}\n\nexport { isValidPort, isValidSender, isServiceWorker };\n", "import browser, { Runtime } from 'webextension-polyfill';\nimport { v4 as uuidv4 } from 'uuid';\nimport {\n Agent,\n AgentInfo,\n ConnectionType,\n PorterContext,\n MessageTarget,\n AgentId,\n BrowserLocation,\n} from '../porter.model';\nimport { Logger } from '../porter.utils';\n\nexport interface AgentOperations {\n addAgent(port: Runtime.Port, context?: PorterContext): AgentId | undefined;\n queryAgents(location: Partial<BrowserLocation>): Agent[];\n getAgentById(id: AgentId): Agent | null;\n getAgentsByContext(context: PorterContext): Agent[];\n getAgentByLocation(location: BrowserLocation): Agent | null;\n getAllAgents(): Agent[];\n getAllAgentsInfo(): AgentInfo[];\n hasPort(port: Runtime.Port): boolean;\n removeAgent(agentId: AgentId): void;\n printAgents(): void;\n}\n\nexport interface AgentEventEmitter {\n on(event: 'agentSetup', handler: (agent: Agent) => void): void;\n on(\n event: 'agentMessage',\n handler: (message: any, info: AgentInfo) => void\n ): void;\n on(event: 'agentDisconnect', handler: (info: AgentInfo) => void): void;\n}\n\nexport class AgentManager implements AgentOperations, AgentEventEmitter {\n private agents: Map<AgentId, Runtime.Port> = new Map();\n private agentsInfo: Map<AgentId, AgentInfo> = new Map();\n private eventHandlers: Map<string, Set<Function>> = new Map();\n\n constructor(private logger: Logger) {\n this.eventHandlers.set('agentSetup', new Set());\n this.eventHandlers.set('agentMessage', new Set());\n this.eventHandlers.set('agentDisconnect', new Set());\n }\n\n public addAgent(\n port: Runtime.Port,\n context?: PorterContext\n ): AgentId | undefined {\n this.logger.debug(`Adding agent`, { context, port });\n const connectionSource = this.identifyConnectionSource(port);\n if (!connectionSource) {\n this.logger.error(`Cannot add agent that did not have a sender`);\n return;\n }\n\n const determinedContext = connectionSource.context;\n const tabId = connectionSource.tabId || -1;\n const frameId = connectionSource.frameId || 0;\n\n this.logger.debug(`Determined context for new agent`, {\n determinedContext,\n tabId,\n frameId,\n });\n // Find agents in the same tab or under the same extension context\n const tabAgentsInfo = Array.from(this.agentsInfo.values()).filter(\n (info) => {\n return (\n info.location.context === determinedContext &&\n info.location.tabId === tabId &&\n info.location.frameId === frameId\n );\n }\n );\n\n if (tabAgentsInfo.length > 0) {\n this.logger.debug('Adding agent: Found existing similar agent.', {\n tabAgentsInfo,\n });\n }\n\n const agentId =\n this.getAgentByLocation({ context: determinedContext, tabId, frameId })\n ?.info?.id || (uuidv4() as AgentId);\n\n this.logger.debug(`Adding agent with id: ${agentId}`);\n\n this.agents.set(agentId, port);\n\n const agentInfo: AgentInfo = {\n id: agentId,\n location: { context: determinedContext, tabId, frameId },\n createdAt: Date.now(),\n lastActiveAt: Date.now(),\n };\n this.agentsInfo.set(agentId, agentInfo);\n\n this.logger.debug(`Constructed agent info: ${JSON.stringify(agentInfo)}`);\n port.onMessage.addListener((message: any) =>\n this.emit('agentMessage', message, agentInfo)\n );\n\n const agent: Agent = { port, info: agentInfo };\n port.onDisconnect.addListener(() => {\n this.emit('agentDisconnect', agentInfo);\n this.logger.debug('Agent disconnected, removing from manager. ', {\n agentInfo,\n });\n this.removeAgent(agentId);\n });\n\n this.emit('agentSetup', agent);\n this.logger.debug('Setup complete for adding agent. ', {\n agentInfo,\n });\n return agentId;\n }\n\n public getAgentByLocation(location: BrowserLocation): Agent | null {\n const { context, tabId, frameId } = location;\n\n const infoEntry: [AgentId, AgentInfo] | undefined = Array.from(\n this.agentsInfo.entries()\n ).find(\n ([key, info]) =>\n info.location.context === context &&\n info.location.tabId === tabId &&\n info.location.frameId === frameId\n );\n if (infoEntry === undefined) {\n this.logger.error('No agent found for location. ', {\n location,\n });\n return null;\n }\n const agentId = infoEntry[0];\n let port = this.agents.get(agentId);\n let info = this.agentsInfo.get(agentId);\n if (!port || !info) {\n this.logger.error('No agent found for location. ', {\n location,\n });\n return null;\n }\n return { port, info };\n }\n\n public getAgentsByContext(context: PorterContext): Agent[] {\n let infoForAgents = Array.from(this.agentsInfo.entries()).filter(\n ([key, value]) => value.location.context === context\n );\n return infoForAgents.map(([key, value]) => ({\n port: this.agents.get(key),\n info: value,\n }));\n }\n\n public getAllAgents(): Agent[] {\n let allInfo = Array.from(this.agentsInfo.entries());\n return allInfo.map(([key, value]) => ({\n port: this.agents.get(key),\n info: value,\n }));\n }\n\n public queryAgents(location: Partial<BrowserLocation>): Agent[] {\n let infoForAgents = Array.from(this.agentsInfo.entries()).filter(\n ([key, value]) => {\n const hasContext = location.context\n ? value.location.context === location.context\n : true;\n const hasTabId = location.tabId\n ? value.location.tabId === location.tabId\n : true;\n const hasFrameId = location.frameId\n ? value.location.frameId === location.frameId\n : true;\n return hasContext && hasTabId && hasFrameId;\n }\n );\n return infoForAgents.map(([key, value]) => ({\n port: this.agents.get(key),\n info: value,\n }));\n }\n\n public getAgentById(id: AgentId): Agent | null {\n let port = this.agents.get(id);\n let info = this.agentsInfo.get(id);\n if (!port || !info) {\n this.logger.error('No agent found for agentId. ', {\n id,\n });\n return null;\n }\n return { port, info };\n }\n\n public getAllAgentsInfo(): AgentInfo[] {\n return Array.from(this.agentsInfo.values());\n }\n\n public hasPort(port: Runtime.Port): boolean {\n const matchingPort = Array.from(this.agents.values()).find(\n (p) => p.name === port.name\n );\n return !!matchingPort;\n }\n\n public removeAgent(agentId: AgentId) {\n if (this.agents.has(agentId) && this.agentsInfo.has(agentId)) {\n this.agents.delete(agentId);\n this.agentsInfo.delete(agentId);\n } else {\n this.logger.error('No agent found to remove. ', {\n agentId,\n });\n }\n }\n\n public printAgents() {\n const allAgents = Array.from(this.agents.entries());\n const allAgentsInfo = Array.from(this.agentsInfo.entries());\n this.logger.debug('Current agents:', {\n allAgents,\n allAgentsInfo,\n });\n }\n\n // private isContentScript(port: Runtime.Port) {\n // if (!port.sender) return false;\n // const hasFrame =\n // port.sender.tab &&\n // port.sender.tab.id !== undefined &&\n // port.sender.frameId !== undefined;\n // if (!hasFrame) return false;\n // if (!(port.sender as any).origin) return false;\n\n // const contentPage =\n // !(port.sender as any)!.origin.startsWith('chrome-extension://') &&\n // !(port.sender as any)!.tab!.url?.startsWith('moz-extension://');\n // return contentPage;\n // }\n\n public on(event: string, handler: Function) {\n const handlers = this.eventHandlers.get(event);\n if (handlers) {\n handlers.add(handler);\n }\n }\n\n private emit(event: string, ...args: any[]) {\n const handlers = this.eventHandlers.get(event);\n handlers?.forEach((handler) => handler(...args));\n }\n\n private identifyConnectionSource(port: Runtime.Port): {\n context: PorterContext;\n tabId?: number;\n frameId?: number;\n url?: string;\n portName?: string;\n } | null {\n const sender = port.sender;\n if (!sender) {\n this.logger.error(`Cannot add agent that did not have a sender`);\n return null;\n }\n // Cache the manifest data\n const manifest = browser.runtime.getManifest();\n\n // Extract page URLs from manifest\n const sidePanel = (manifest as any)?.side_panel?.default_path || '';\n const optionsPage = (manifest as any).options_page || '';\n const popupPage = (manifest as any).action?.default_popup || '';\n const devtoolsPage = (manifest as any).devtools_page || '';\n const newTabOverride = (manifest as any).chrome_url_overrides?.newtab || '';\n const bookmarksOverride =\n (manifest as any).chrome_url_overrides?.bookmarks || '';\n const historyOverride =\n (manifest as any).chrome_url_overrides?.history || '';\n\n // Create URL endings for matching\n // (handles both full paths and just filenames)\n const pageMatchers = {\n sidepanel: sidePanel ? sidePanel.split('/').pop() : 'sidepanel.html',\n options: optionsPage ? optionsPage.split('/').pop() : 'options.html',\n popup: popupPage ? popupPage.split('/').pop() : 'popup.html',\n devtools: devtoolsPage ? devtoolsPage.split('/').pop() : 'devtools.html',\n newtab: newTabOverride ? newTabOverride.split('/').pop() : 'newtab.html',\n bookmarks: bookmarksOverride\n ? bookmarksOverride.split('/').pop()\n : 'bookmarks.html',\n history: historyOverride\n ? historyOverride.split('/').pop()\n : 'history.html',\n };\n\n // Content scripts (web pages)\n if (sender.tab && sender.url && !sender.url.includes('extension://')) {\n return {\n context: PorterContext.ContentScript,\n tabId: sender.tab.id,\n frameId: sender.frameId || 0,\n url: sender.url,\n portName: port.name,\n };\n }\n\n // Extension pages\n if (sender.url && sender.url.includes('extension://')) {\n const urlPath = new URL(sender.url).pathname;\n const filename = urlPath.split('/').pop();\n\n // Check against our manifest-derived page matchers\n for (const [pageType, pageMatcher] of Object.entries(pageMatchers)) {\n if (filename === pageMatcher) {\n // It's a main extension page\n // Different handling based on presence of tab\n\n return {\n context: pageType as PorterContext,\n tabId: sender.tab?.id || 0,\n frameId: sender.frameId || 0,\n url: sender.url,\n portName: port.name,\n };\n }\n }\n\n // It's some other extension page not specifically listed in our matchers\n\n return {\n context: PorterContext.Unknown,\n tabId: sender.tab?.id || 0,\n frameId: sender.frameId || 0,\n url: sender.url,\n portName: port.name,\n };\n }\n\n // Fallback for unknown sources\n return {\n context: PorterContext.Unknown,\n tabId: 0,\n url: sender.url,\n portName: port.name,\n };\n }\n}\n", "import { Runtime } from 'webextension-polyfill';\nimport { AgentOperations } from './AgentManager';\nimport {\n Agent,\n AgentInfo,\n PorterError,\n PorterErrorType,\n} from '../porter.model';\nimport { Logger } from '../porter.utils';\n\nexport class ConnectionManager {\n constructor(\n private agentOperations: AgentOperations,\n private namespace: string,\n private logger: Logger\n ) {}\n\n public handleConnection(port: Runtime.Port) {\n try {\n this.logger.info('New connection request:', port.name);\n if (!port.name) {\n throw new PorterError(\n PorterErrorType.INVALID_CONTEXT,\n 'Port name not provided'\n );\n }\n\n if (!port.name || !port.name.startsWith(this.namespace + ':')) {\n throw new PorterError(\n PorterErrorType.INVALID_CONTEXT,\n `Invalid namespace or port name format. port name was ${port?.name || 'port undefined'} but namespace is ${this.namespace}`\n );\n }\n\n port.onMessage.addListener(this.handleInitMessage.bind(this, port));\n\n setTimeout(() => {\n if (!this.agentOperations.hasPort(port)) {\n try {\n port.disconnect();\n } catch (e) {\n this.logger.error('Failed to disconnect port:', e);\n }\n }\n }, 5000);\n } catch (error) {\n this.handleConnectionError(port, error as Error);\n }\n }\n\n private handleInitMessage(port: Runtime.Port, message: any): void {\n // Process only the init message\n if (message.action !== 'porter-init') {\n return;\n }\n\n try {\n // Remove this listener since we only need it once\n port.onMessage.removeListener(this.handleInitMessage.bind(this, port));\n\n const { connectionId } = message.payload;\n\n if (!connectionId) {\n throw new PorterError(\n PorterErrorType.INVALID_CONTEXT,\n 'Missing context or connection ID. Message was: ' +\n JSON.stringify(message)\n );\n }\n\n // Now add the agent with the provided context\n const agentId = this.agentOperations.addAgent(port);\n\n if (!agentId) {\n throw new PorterError(\n PorterErrorType.INVALID_CONTEXT,\n 'Failed to add agent'\n );\n }\n\n // Get the agent info to send back\n const agent = this.agentOperations.getAgentById(agentId);\n\n if (agent) {\n this.confirmConnection(agent);\n }\n\n this.agentOperations.printAgents();\n } catch (error) {\n this.handleConnectionError(port, error as Error);\n }\n }\n\n private handleConnectionError(port: Runtime.Port, error: Error): void {\n const porterError =\n error instanceof PorterError\n ? error\n : new PorterError(\n PorterErrorType.CONNECTION_FAILED,\n error instanceof Error ? error.message : 'Unknown connection error',\n { originalError: error }\n );\n this.logger.error('Connection handling failed: ', {\n porterError,\n });\n try {\n port.postMessage({\n action: 'porter-error',\n payload: { error: porterError },\n });\n } catch (e) {\n this.logger.error('Failed to send error message: ', {\n error: e,\n });\n }\n\n try {\n port.disconnect();\n } catch (e) {\n this.logger.error('Failed to disconnect port: ', {\n error: e,\n });\n }\n }\n\n public confirmConnection(agent: Agent) {\n this.logger.debug('Sending confirmation message back to initiator ', {\n agent,\n });\n if (!agent.port) {\n throw new PorterError(\n PorterErrorType.INVALID_PORT,\n 'Agent port is undefined when confirming connection'\n );\n }\n agent.port.postMessage({\n action: 'porter-handshake',\n payload: {\n info: agent.info,\n currentConnections: this.agentOperations.getAllAgentsInfo(),\n },\n });\n }\n}\n", "import { Runtime } from 'webextension-polyfill';\nimport { AgentOperations } from './AgentManager';\nimport {\n PorterEvent,\n Listener,\n MessageListener,\n Message,\n PorterErrorType,\n MessageConfig,\n PorterContext,\n PorterError,\n AgentInfo,\n MessageTarget,\n BrowserLocation,\n AgentId,\n} from '../porter.model';\nimport { Logger } from '../porter.utils';\n\nexport class MessageHandler {\n private eventListeners: Map<\n keyof PorterEvent,\n Set<Listener<keyof PorterEvent>>\n > = new Map();\n private messageListeners: Set<MessageListener> = new Set();\n private initializationHandler: MessageConfig;\n\n constructor(\n private agentOperations: AgentOperations,\n private logger: Logger\n ) {\n this.initializationHandler = {\n 'porter-messages-established': (\n message: Message<any>,\n agent?: AgentInfo\n ) => {\n if (!agent || !agent.id) return;\n const agentInfo = this.agentOperations.getAgentById(agent.id)?.info;\n if (!agentInfo) {\n this.logger.error('No agent info found for agent id: ', agent.id);\n return;\n }\n this.logger.debug(\n 'internalHandlers, established message received: ',\n agent.id,\n message\n );\n this.emitEvent('onMessagesSet', agentInfo);\n },\n };\n }\n\n public async post(\n message: Message<any>,\n target?: MessageTarget\n ): Promise<void> {\n return new Promise((resolve, reject) => {\n try {\n this.logger.debug('Post request received:', {\n action: message.action,\n target,\n });\n\n const timeoutId = setTimeout(() => {\n const error = new Error('Message posting timed out');\n this.logger.error('Post timeout:', error);\n reject(error);\n }, 5000);\n\n if (target === undefined) {\n this.broadcastMessage(message);\n // how to tell if target is BrowserLocation type?\n } else if (isBrowserLocation(target)) {\n this.postToLocation(message, target);\n } else if (isPorterContext(target)) {\n this.postToContext(message, target);\n } else if (typeof target === 'string') {\n this.postToId(message, target);\n } else {\n this.postToTab(message, target);\n }\n\n clearTimeout(timeoutId);\n resolve();\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : 'Unknown error';\n this.logger.error('Failed to post message:', errorMessage);\n reject(new Error(`Failed to post message: ${errorMessage}`));\n }\n });\n }\n\n private broadcastMessage(message: Message<any>): void {\n this.logger.info('Broadcasting message to all agents: ', message);\n this.agentOperations.getAllAgents().forEach((agent) => {\n if (agent.port) {\n agent.port.postMessage(message);\n }\n });\n }\n\n // Post to all frames in a tab\n private postToTab(message: Message<any>, tabId: number): void {\n // const key = `${PorterContext.ContentScript}:${tabId}:0`;\n const agents = this.agentOperations.queryAgents({\n context: PorterContext.ContentScript,\n tabId,\n });\n if (agents.length === 0) {\n this.logger.warn('post: No agents found for tab: ', tabId);\n throw new PorterError(\n PorterErrorType.MESSAGE_FAILED,\n `Failed to post message to tabId ${tabId}`,\n { originalError: message }\n );\n return;\n }\n agents.forEach((agent) => {\n if (agent.port) {\n this.postToPort(message, agent.port);\n }\n });\n }\n\n private postToLocation(\n message: Message<any>,\n location: BrowserLocation\n ): void {\n const agents = this.agentOperations.queryAgents(location);\n agents.forEach((agent) => {\n if (!agent.port) {\n throw new PorterError(\n PorterErrorType.INVALID_TARGET,\n `No port found for agent`,\n { agentInfo: agent.info }\n );\n return;\n }\n this.postToPort(message, agent.port);\n });\n }\n\n private postToContext(message: Message<any>, context: PorterContext): void {\n const agents = this.agentOperations.queryAgents({\n context,\n });\n agents.forEach((agent) => {\n if (!agent.port) {\n throw new PorterError(\n PorterErrorType.INVALID_TARGET,\n `No port found for agent`,\n { agentInfo: agent.info }\n );\n return;\n }\n this.postToPort(message, agent.port);\n });\n }\n\n private postToPort(message: Message<any>, port: Runtime.Port): void {\n try {\n port.postMessage(message);\n } catch (error) {\n throw new PorterError(\n PorterErrorType.MESSAGE_FAILED,\n `Failed to post message to port`,\n { originalError: error, message }\n );\n }\n }\n\n private postToId(message: Message<any>, agentId: AgentId): void {\n const agent = this.agentOperations.getAgentById(agentId);\n if (!agent?.port) {\n throw new PorterError(\n PorterErrorType.INVALID_TARGET,\n `No agent found for key: ${agentId}`\n );\n }\n this.postToPort(message, agent.port);\n }\n\n public onMessage(config: MessageConfig) {\n // Optionally: Check for existing listeners with same config\n const existingListener = Array.from(this.messageListeners).find(\n (listener) => JSON.stringify(listener.config) === JSON.stringify(config)\n );\n\n if (existingListener) {\n this.logger.warn(\n `Listener with same config already exists: ${JSON.stringify(config)}`\n );\n }\n\n const messageListener: MessageListener = {\n config,\n listener: (event: PorterEvent['onMessage']) => {\n const handler = config[event.message.action];\n if (handler) {\n this.logger.debug('onMessage, calling handler ', { event });\n const { message, ...info } = event;\n handler(message, info);\n } else {\n this.logger.debug('onMessage, no handler found ', { event });\n }\n },\n };\n this.messageListeners.add(messageListener);\n\n return () => {\n this.messageListeners.delete(messageListener);\n };\n }\n\n // Adding new 'on' method that works the same way as onMessage\n public on(config: MessageConfig) {\n return this.onMessage(config);\n }\n\n // Handles messages incomng from ports\n public handleIncomingMessage(message: any, info: AgentInfo) {\n this.logger.debug(`Received message`, {\n message,\n info,\n });\n\n this.emitMessage({ ...info, message });\n }\n\n private emitEvent<T extends keyof PorterEvent>(\n event: T,\n arg: PorterEvent[T]\n ) {\n this.logger.debug('emitting event: ', event, arg);\n this.eventListeners\n .get(event)\n ?.forEach((listener) => (listener as Listener<T>)(arg));\n }\n\n // Dispatches incoming messages, either to a registered listener on the source, or to a specific agent\n // if a target was specified (calling this a relay)\n private emitMessage(messageEvent: PorterEvent['onMessage']) {\n this.logger.debug('Dispatching incoming message to subscribers', {\n messageEvent,\n });\n\n if (messageEvent.message.action.startsWith('porter-')) {\n const handler = this.initializationHandler[messageEvent.message.action];\n if (handler) {\n this.logger.debug('Internal message being handled', {\n messageEvent,\n });\n const { message, ...info } = messageEvent;\n handler(message, info);\n return;\n }\n }\n\n // Handle relaying to a target\n if (!!messageEvent.message.target) {\n this.logger.debug(\n 'Relaying message to target:',\n messageEvent.message.target\n );\n this.post(messageEvent.message, messageEvent.message.target);\n }\n\n let handlerCount = 0;\n\n this.logger.trace('Processing message with registered handlers');\n for (const { listener, config } of this.messageListeners) {\n if (config[messageEvent.message.action]) {\n listener(messageEvent as PorterEvent['onMessage']);\n handlerCount++;\n this.logger.debug('Message handled by registered listener: ', {\n listener,\n config,\n });\n }\n }\n\n if (handlerCount === 0) {\n this.logger.warn(\n 'No handler found for message:',\n messageEvent.message.action\n );\n } else {\n this.logger.debug(\n `Message handled by ${handlerCount} registered listeners`\n );\n }\n }\n\n public addListener<T extends keyof PorterEvent>(\n event: T,\n listener: Listener<T>\n ) {\n if (!this.eventListeners.has(event)) {\n this.eventListeners.set(event, new Set());\n }\n this.eventListeners\n .get(event)!\n .add(listener as Listener<keyof PorterEvent>);\n\n return () => {\n this.eventListeners\n .get(event)\n ?.delete(listener as Listener<keyof PorterEvent>);\n };\n }\n\n public handleDisconnect(info: AgentInfo) {\n // Remove all message listeners for this agent\n this.messageListeners.forEach((messageListener) => {\n if (messageListener.config[info.id]) {\n this.messageListeners.delete(messageListener);\n }\n });\n this.logger.info('Agent disconnected:', { info });\n this.emitEvent('onDisconnect', info);\n }\n\n public handleConnect(info: AgentInfo) {\n this.logger.info('Agent connected:', { info });\n this.emitEvent('onConnect', info);\n }\n\n public onConnect(listener: Listener<'onConnect'>) {\n return this.addListener('onConnect', listener);\n }\n\n public onMessagesSet(listener: Listener<'onMessagesSet'>) {\n return this.addListener('onMessagesSet', listener);\n }\n\n public onDisconnect(listener: Listener<'onDisconnect'>) {\n return this.addListener('onDisconnect', listener);\n }\n}\n\n// Type guard for BrowserLocation\nfunction isBrowserLocation(target: MessageTarget): target is BrowserLocation {\n return (\n typeof target === 'object' &&\n target !== null &&\n 'context' in target &&\n 'tabId' in target &&\n 'frameId' in target\n );\n}\n\nfunction isPorterContext(target: MessageTarget): target is PorterContext {\n return (\n typeof target === 'string' &&\n Object.values(PorterContext).includes(target as PorterContext)\n );\n}\n", "import browser, { Runtime } from 'webextension-polyfill';\nimport { AgentInfo, PorterError, PorterErrorType } from '../porter.model';\nimport { Logger } from '../porter.utils';\nimport { MessageQueue } from './MessageQueue';\n\nexport type DisconnectCallback = () => void;\nexport type ReconnectCallback = (info: AgentInfo) => void;\n\nexport class AgentConnectionManager {\n private readonly CONNECTION_TIMEOUT = 5000;\n private readonly RECONNECT_INTERVAL = 1000; // 1 second\n private connectionTimer: NodeJS.Timeout | null = null;\n private reconnectTimer: NodeJS.Timeout | null = null;\n private agentInfo: AgentInfo | null = null;\n private port: Runtime.Port | null = null;\n private readonly logger: Logger;\n private readonly connectionId: string;\n private readonly messageQueue: MessageQueue;\n private isReconnecting: boolean = false;\n private reconnectAttemptCount: number = 0;\n\n // Event callbacks\n private disconnectCallbacks: Set<DisconnectCallback> = new Set();\n private reconnectCallbacks: Set<ReconnectCallback> = new Set();\n\n constructor(\n private readonly namespace: string,\n logger: Logger\n ) {\n this.connectionId = `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;\n this.logger = logger;\n this.messageQueue = new MessageQueue(logger);\n }\n\n /**\n * Register a callback to be called when the connection is lost\n * @returns Unsubscribe function\n */\n public onDisconnect(callback: DisconnectCallback): () => void {\n this.disconnectCallbacks.add(callback);\n return () => {\n this.disconnectCallbacks.delete(callback);\n };\n }\n\n /**\n * Register a callback to be called when reconnection succeeds\n * @returns Unsubscribe function\n */\n public onReconnect(callback: ReconnectCallback): () => void {\n this.reconnectCallbacks.add(callback);\n return () => {\n this.reconnectCallbacks.delete(callback);\n };\n }\n\n private emitDisconnect(): void {\n this.logger.debug('Emitting disconnect event', {\n callbackCount: this.disconnectCallbacks.size,\n });\n this.disconnectCallbacks.forEach((callback) => {\n try {\n callback();\n } catch (error) {\n this.logger.error('Error in disconnect callback:', error);\n }\n });\n }\n\n private emitReconnect(info: AgentInfo): void {\n this.logger.debug('Emitting reconnect event', {\n callbackCount: this.reconnectCallbacks.size,\n info,\n });\n this.reconnectCallbacks.forEach((callback) => {\n try {\n callback(info);\n } catch (error) {\n this.logger.error('Error in reconnect callback:', error);\n }\n });\n }\n\n public async initializeConnection(): Promise<void> {\n try {\n if (this.connectionTimer) {\n clearTimeout(this.connectionTimer);\n }\n\n const portName = `${this.namespace}:${this.connectionId}`;\n this.logger.debug('Connecting new port with name: ', { portName });\n this.port = browser.runtime.connect({ name: portName });\n\n const handshakePromise = new Promise<void>((resolve, reject) => {\n const timeout = setTimeout(\n () =>\n reject(\n new PorterError(\n PorterErrorType.CONNECTION_TIMEOUT,\n 'Connection timed out waiting for handshake'\n )\n ),\n this.CONNECTION_TIMEOUT\n );\n\n const onMessage = (message: any) => {\n if (message.action === 'porter-handshake') {\n this.logger.debug('Received handshake:', message);\n clearTimeout(timeout);\n this.agentInfo = message.payload.info;\n this.logger.debug('Handshake agent info:', {\n agentInfo: this.agentInfo,\n });\n this.port?.onMessage.removeListener(onMessage);\n resolve();\n } else if (message.action === 'porter-error') {\n clearTimeout(timeout);\n this.port?.onMessage.removeListener(onMessage);\n this.logger.error('Error:', message);\n reject(\n new PorterError(\n message.payload.type,\n message.payload.message,\n message.payload.details\n )\n );\n }\n };\n\n this.port?.onMessage.addListener(onMessage);\n });\n\n this.port?.postMessage({\n action: 'porter-init',\n payload: {\n info: this.agentInfo,\n connectionId: this.connectionId,\n },\n });\n\n await handshakePromise;\n\n // After successful connection, process any queued messages\n await this.processQueuedMessages();\n } catch (error) {\n this.logger.error('Connection initialization failed:', error);\n this.handleDisconnect(this.port!);\n throw error;\n }\n }\n\n private async processQueuedMessages(): Promise<void> {\n if (!this.port || this.messageQueue.isEmpty()) {\n return;\n }\n\n const messages = this.messageQueue.dequeue();\n this.logger.info(\n `Processing ${messages.length} queued messages after reconnection`\n );\n\n for (const { message, target } of messages) {\n try {\n // Send message in the same format as normal messages\n const messageToSend = { ...message };\n if (target) {\n messageToSend.target = target;\n }\n this.port.postMessage(messageToSend);\n this.logger.debug('Successfully resent queued message:', {\n message: messageToSend,\n });\n } catch (error) {\n this.logger.error('Failed to process queued message:', error);\n // Re-queue the message if it fails\n this.messageQueue.enqueue(message, target);\n this.logger.debug('Re-queued failed message for retry');\n }\n }\n }\n\n public getPort(): Runtime.Port | null {\n return this.port;\n }\n\n public getAgentInfo(): AgentInfo | null {\n return this.agentInfo;\n }\n\n public getNamespace(): string {\n return this.namespace;\n }\n\n public handleDisconnect(port: Runtime.Port) {\n this.logger.info('Port disconnected', {\n portName: port.name,\n connectionId: this.connectionId,\n queuedMessages: this.messageQueue.isEmpty() ? 0 : 'some',\n });\n this.port = null;\n this.agentInfo = null;\n\n // Notify listeners of disconnection\n this.emitDisconnect();\n\n // Start reconnection attempts if not already reconnecting\n if (!this.isReconnecting) {\n this.startReconnectionAttempts();\n }\n }\n\n private startReconnectionAttempts(): void {\n this.isReconnecting = true;\n this.reconnectAttemptCount = 0;\n\n if (this.reconnectTimer) {\n clearInterval(this.reconnectTimer);\n }\n\n this.logger.info('Starting reconnection attempts', {\n interval: this.RECONNECT_INTERVAL,\n queuedMessages: this.messageQueue.isEmpty() ? 0 : 'some',\n });\n\n this.reconnectTimer = setInterval(async () => {\n this.reconnectAttemptCount++;\n try {\n this.logger.debug(`Reconnection attempt ${this.reconnectAttemptCount}`);\n await this.initializeConnection();\n this.isReconnecting = false;\n if (this.reconnectTimer) {\n clearInterval(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n this.logger.info('Reconnection successful', {\n attempts: this.reconnectAttemptCount,\n queuedMessages: this.messageQueue.isEmpty() ? 0 : 'some',\n });\n\n // Notify listeners of successful reconnection\n if (this.agentInfo) {\n this.emitReconnect(this.agentInfo);\n }\n } catch (error) {\n this.logger.debug(\n `Reconnection attempt ${this.reconnectAttemptCount} failed:`,\n error\n );\n }\n }, this.RECONNECT_INTERVAL);\n }\n\n public queueMessage(message: any, target?: any): void {\n this.messageQueue.enqueue(message, target);\n this.logger.debug('Message queued for retry', {\n message,\n target,\n queueSize: this.messageQueue.isEmpty() ? 0 : 'some',\n });\n }\n}\n", "import { Message, BrowserLocation } from '../porter.model';\nimport { Logger } from '../porter.utils';\n\ninterface QueuedMessage {\n message: Message<any>;\n target?: BrowserLocation;\n timestamp: number;\n}\n\nexport class MessageQueue {\n private queue: QueuedMessage[] = [];\n private readonly logger: Logger;\n private readonly maxQueueSize: number = 1000; // Prevent memory issues\n private readonly maxMessageAge: number = 5 * 60 * 1000; // 5 minutes\n\n constructor(logger: Logger) {\n this.logger = logger;\n this.logger.debug('MessageQueue initialized', {\n maxQueueSize: this.maxQueueSize,\n maxMessageAge: `${this.maxMessageAge / 1000} seconds`,\n });\n }\n\n public enqueue(message: Message<any>, target?: BrowserLocation): void {\n // Remove old messages\n const oldCount = this.queue.length;\n this.cleanup();\n if (oldCount !== this.queue.length) {\n this.logger.debug(\n `Cleaned up ${oldCount - this.queue.length} old messages`\n );\n }\n\n // Check if queue is full\n if (this.queue.length >= this.maxQueueSize) {\n this.logger.warn('Message queue is full, dropping oldest message', {\n queueSize: this.queue.length,\n maxSize: this.maxQueueSize,\n });\n this.queue.shift();\n }\n\n this.queue.push({\n message,\n target,\n timestamp: Date.now(),\n });\n\n this.logger.debug('Message queued', {\n queueSize: this.queue.length,\n message,\n target,\n timestamp: new Date().toISOString(),\n });\n }\n\n public dequeue(): QueuedMessage[] {\n const messages = [...this.queue];\n this.queue = [];\n this.logger.info(`Dequeued ${messages.length} messages`, {\n oldestMessage: messages[0]\n ? new Date(messages[0].timestamp).toISOString()\n : null,\n newestMessage: messages[messages.length - 1]\n ? new Date(messages[messages.length - 1].timestamp).toISOString()\n : null,\n });\n return messages;\n }\n\n public isEmpty(): boolean {\n return this.queue.length === 0;\n }\n\n private cleanup(): void {\n const now = Date.now();\n const oldCount = this.queue.length;\n this.queue = this.queue.filter(\n (item) => now - item.timestamp < this.maxMessageAge\n );\n if (oldCount !== this.queue.length) {\n this.logger.debug(\n `Cleaned up ${oldCount - this.queue.length} expired messages`,\n {\n remaining: this.queue.length,\n maxAge: `${this.maxMessageAge / 1000} seconds`,\n }\n );\n }\n }\n}\n", "import { Runtime } from 'webextension-polyfill';\nimport {\n BrowserLocation,\n Message,\n MessageConfig,\n PorterError,\n PorterErrorType,\n} from '../porter.model';\nimport { Logger } from '../porter.utils';\n\nexport class AgentMessageHandler {\n private readonly MAX_QUEUE_SIZE = 1000;\n private readonly MESSAGE_TIMEOUT = 30000;\n private messageQueue: Array<{ message: Message<any>; timestamp: number }> =\n [];\n private handlers: Map<string, Array<Function>> = new Map();\n\n constructor(private readonly logger: Logger) {}\n\n public handleMessage(port: Runtime.Port, message: any) {\n this.logger.debug('handleMessage, message: ', message);\n if (this.handlers.size === 0) {\n if (this.messageQueue.length >= this.MAX_QUEUE_SIZE) {\n this.logger.warn('Message queue full, dropping message:', message);\n return;\n }\n this.logger.warn(\n 'No message handlers configured yet, queueing message: ',\n message\n );\n this.messageQueue.push({ message, timestamp: Date.now() });\n return;\n }\n this.processMessage(port, message);\n }\n\n // Legacy method - internally uses the new system\n public onMessage(config: MessageConfig) {\n this.logger.debug('Setting message handlers from config: ', config);\n // Clear previous handlers to maintain backward compatibility\n this.handlers.clear();\n this.on(config);\n\n this.processQueuedMessages();\n }\n\n public on(config: MessageConfig) {\n this.logger.debug('Adding message handlers from config: ', config);\n\n Object.entries(config).forEach(([action, handler]) => {\n if (!this.handlers.has(action)) {\n this.handlers.set(action, []);\n }\n this.handlers.get(action)!.push(handler);\n });\n\n this.processQueuedMessages();\n }\n\n private processQueuedMessages() {\n while (this.messageQueue.length > 0) {\n const item = this.messageQueue[0];\n if (Date.now() - item.timestamp > this.MESSAGE_TIMEOUT) {\n this.logger.warn(\n 'Message timeout, dropping message: ',\n this.messageQueue.shift()\n );\n continue;\n }\n this.processMessage(null!, item.message);\n this.messageQueue.shift();\n }\n }\n\n private processMessage(port: Runtime.Port, message: any) {\n const action = message.action;\n const actionHandlers = this.handlers.get(action) || [];\n\n if (actionHandlers.length > 0) {\n this.logger.debug(\n `Found ${actionHandlers.length} handlers for action: ${action}`\n );\n actionHandlers.forEach((handler) => handler(message));\n } else {\n this.logger.debug(`No handlers for message with action: ${action}`);\n }\n }\n\n public post(\n port: Runtime.Port,\n message: Message<any>,\n target?: BrowserLocation\n ) {\n this.logger.debug(`Sending message`, {\n action: message.action,\n target,\n hasPayload: !!message.payload,\n });\n\n try {\n if (target) {\n message.target = target;\n }\n port.postMessage(message);\n } catch (error) {\n throw new PorterError(\n PorterErrorType.MESSAGE_FAILED,\n 'Failed to post message',\n { originalError: error, message, target }\n );\n }\n }\n}\n", "import {\n AgentInfo,\n BrowserLocation,\n Message,\n MessageConfig,\n PorterContext,\n Unsubscribe,\n} from '../porter.model';\nimport {\n AgentConnectionManager,\n DisconnectCallback,\n ReconnectCallback,\n} from '../managers/AgentConnectionManager';\nimport { AgentMessageHandler } from '../managers/AgentMessageHandler';\nimport { Logger } from '../porter.utils';\n\nexport interface AgentAPI {\n type: 'agent';\n post: (message: Message<any>, target?: BrowserLocation) => void;\n onMessage: (config: MessageConfig) => void;\n on: (config: MessageConfig) => void;\n getAgentInfo: () => AgentInfo | null;\n onDisconnect: (callback: DisconnectCallback) => Unsubscribe;\n onReconnect: (callback: ReconnectCallback) => Unsubscribe;\n}\n\nexport interface PorterAgentOptions {\n agentContext?: PorterContext;\n namespace?: string;\n debug?: boolean;\n}\n\nexport class PorterAgent {\n private static instance: PorterAgent | null = null;\n private readonly connectionManager: AgentConnectionManager;\n private readonly messageHandler: AgentMessageHandler;\n private readonly logger: Logger;\n\n private constructor(options: PorterAgentOptions = {}) {\n const namespace = options.namespace ?? 'porter';\n const context = options.agentContext ?? this.determineContext();\n\n if (options.debug !== undefined) {\n Logger.configure({ enabled: options.debug });\n }\n\n this.logger = Logger.getLogger('Agent');\n\n this.connectionManager = new AgentConnectionManager(namespace, this.logger);\n this.messageHandler = new AgentMessageHandler(this.logger);\n\n // Listen for reconnection events to re-wire port listeners\n this.connectionManager.onReconnect((info) => {\n this.logger.info('Reconnected, re-wiring port listeners', { info });\n this.setupPortListeners();\n });\n\n this.logger.info('Initializing with options: ', { options, context });\n this.initializeConnection();\n }\n\n public static getInstance(options: PorterAgentOptions = {}): PorterAgent {\n if (\n !PorterAgent.instance ||\n PorterAgent.instance.connectionManager.getNamespace() !==\n options.namespace\n ) {\n PorterAgent.instance = new PorterAgent(options);\n } else if (options.debug !== undefined) {\n Logger.configure({ enabled: options.debug });\n }\n return PorterAgent.instance;\n }\n\n private async initializeConnection(): Promise<void> {\n await this.connectionManager.initializeConnection();\n this.setupPortListeners();\n }\n\n /**\n * Set up message and disconnect listeners on the current port.\n * Called after initial connection and after each reconnection.\n */\n private setupPortListeners(): void {\n const port = this.connectionManager.getPort();\n if (port) {\n this.logger.debug('Setting up port listeners');\n port.onMessage.addListener((message: any) =>\n this.messageHandler.handleMessage(port, message)\n );\n port.onDisconnect.addListener((p) =>\n this.connectionManager.handleDisconnect(p)\n );\n } else {\n this.logger.warn('Cannot setup port listeners: no port available');\n }\n }\n\n public onMessage(config: MessageConfig) {\n this.messageHandler.onMessage(config);\n const port = this.connectionManager.getPort();\n port?.postMessage({ action: 'porter-messages-established' });\n }\n\n public on(config: MessageConfig) {\n this.messageHandler.on(config);\n const port = this.connectionManager.getPort();\n port?.postMessage({ action: 'porter-messages-established' });\n }\n\n public post(message: Message<any>, target?: BrowserLocation) {\n const port = this.connectionManager.getPort();\n this.logger.debug('Posting message', { message, target, port });\n\n if (port) {\n try {\n this.messageHandler.post(port, message, target);\n } catch (error) {\n this.logger.error('Failed to post message, queueing for retry', {\n error,\n });\n this.connectionManager.queueMessage(message, target);\n }\n } else {\n this.logger.debug('No port found, queueing message', { message, target });\n this.connectionManager.queueMessage(message, target);\n }\n }\n\n private determineContext(): PorterContext {\n if (!window.location.protocol.includes('extension')) {\n return PorterContext.ContentScript;\n }\n return PorterContext.Extension;\n }\n\n public getAgentInfo(): AgentInfo | null {\n return this.connectionManager.getAgentInfo() || null;\n }\n\n /**\n * Register a callback to be called when the connection to the service worker is lost\n * @returns Unsubscribe function\n */\n public onDisconnect(callback: DisconnectCallback): Unsubscribe {\n return this.connectionManager.onDisconnect(callback);\n }\n\n /**\n * Register a callback to be called when reconnection to the service worker succeeds\n * @returns Unsubscribe function\n */\n public onReconnect(callback: ReconnectCallback): Unsubscribe {\n return this.connectionManager.onReconnect(callback);\n }\n}\n\nexport function connect(options?: PorterAgentOptions): AgentAPI {\n const porterInstance = PorterAgent.getInstance(options);\n return {\n type: 'agent',\n post: porterInstance.post.bind(porterInstance),\n onMessage: porterInstance.onMessage.bind(porterInstance),\n on: porterInstance.on.bind(porterInstance),\n getAgentInfo: porterInstance.getAgentInfo.bind(porterInstance),\n onDisconnect: porterInstance.onDisconnect.bind(porterInstance),\n onReconnect: porterInstance.onReconnect.bind(porterInstance),\n };\n}\n", "import { useState, useEffect, useCallback, useRef, useMemo } from 'react';\nimport { connect, AgentAPI } from '../core/PorterAgent';\nimport {\n AgentInfo,\n Message,\n MessageConfig,\n PorterContext,\n Unsubscribe,\n} from '../porter.model';\n\ninterface UsePorterResult {\n post: (message: Message<any>) => void;\n on: (handlers: MessageConfig) => void;\n isConnected: boolean;\n isReconnecting: boolean;\n error: Error | null;\n agentInfo: AgentInfo | null;\n}\n\nexport function usePorter(options?: {\n agentContext?: PorterContext;\n namespace?: string;\n debug?: boolean;\n onDisconnect?: () => void;\n onReconnect?: (info: AgentInfo) => void;\n}): UsePorterResult {\n const [isConnected, setIsConnected] = useState<boolean>(false);\n const [isReconnecting, setIsReconnecting] = useState<boolean>(false);\n const [error, setError] = useState<Error | null>(null);\n const [agentInfo, setAgentInfo] = useState<AgentInfo | null>(null);\n const postRef = useRef<((message: Message<any>) => void) | null>(null);\n const onRef = useRef<((handlers: MessageConfig) => void) | null>(null);\n const getAgentInfoRef = useRef<(() => AgentInfo | null) | null>(null);\n const unsubscribeRefs = useRef<Unsubscribe[]>([]);\n\n const memoizedOptions = useMemo(\n () => ({\n agentContext: options?.agentContext,\n namespace: options?.namespace,\n debug: options?.debug,\n }),\n [options?.agentContext, options?.namespace, options?.debug]\n );\n\n // Store callbacks in refs to avoid re-running effect when they change\n const onDisconnectRef = useRef(options?.onDisconnect);\n const onReconnectRef = useRef(options?.onReconnect);\n onDisconnectRef.current = options?.onDisconnect;\n onReconnectRef.current = options?.onReconnect;\n\n useEffect(() => {\n let isMounted = true;\n\n const initializePorter = async () => {\n try {\n const { post, on, getAgentInfo, onDisconnect, onReconnect } =\n connect(memoizedOptions);\n\n if (isMounted) {\n postRef.current = post;\n onRef.current = on;\n getAgentInfoRef.current = getAgentInfo;\n setIsConnected(true);\n setIsReconnecting(false);\n setError(null);\n\n // Set up disconnect handler\n const unsubDisconnect = onDisconnect(() => {\n if (isMounted) {\n setIsConnected(false);\n setIsReconnecting(true);\n setAgentInfo(null);\n onDisconnectRef.current?.();\n }\n });\n unsubscribeRefs.current.push(unsubDisconnect);\n\n // Set up reconnect handler\n const unsubReconnect = onReconnect((info: AgentInfo) => {\n if (isMounted) {\n setIsConnected(true);\n setIsReconnecting(false);\n setAgentInfo(info);\n onReconnectRef.current?.(info);\n }\n });\n unsubscribeRefs.current.push(unsubReconnect);\n\n // Set up internal porter-handshake handler\n on({\n 'porter-handshake': (message: Message<any>) => {\n if (isMounted) {\n setAgentInfo(message.payload.info);\n }\n },\n });\n }\n } catch (err) {\n if (isMounted) {\n console.error('[PORTER] initializePorter error ', err);\n setError(\n err instanceof Error\n ? err\n : new Error('Failed to connect to Porter')\n );\n setIsConnected(false);\n setIsReconnecting(false);\n }\n }\n };\n\n initializePorter();\n\n return () => {\n isMounted = false;\n // Clean up all subscriptions\n unsubscribeRefs.current.forEach((unsub) => unsub());\n unsubscribeRefs.current = [];\n };\n }, [memoizedOptions]);\n\n const post = useCallback((message: Message<any>) => {\n if (postRef.current) {\n try {\n postRef.current(message);\n } catch (err) {\n setError(\n err instanceof Error ? err : new Error('Failed to send message')\n );\n }\n } else {\n setError(new Error('Porter is not connected'));\n }\n }, []);\n\n const on = useCallback((handlers: MessageConfig) => {\n if (onRef.current) {\n try {\n onRef.current(handlers);\n } catch (err) {\n setError(\n err instanceof Error\n ? err\n : new Error('Failed to set message handlers')\n );\n }\n } else {\n setError(new Error('Porter is not connected'));\n }\n }, []);\n\n return { post, on, isConnected, isReconnecting, error, agentInfo };\n}\n", "/**\n * Crann Error Classes\n *\n * Custom error types for better debugging and error handling.\n */\n\n/**\n * Base error class for all Crann errors.\n */\nexport class CrannError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"CrannError\";\n // Maintains proper stack trace in V8 environments\n if (Error.captureStackTrace) {\n Error.captureStackTrace(this, this.constructor);\n }\n }\n}\n\n/**\n * Thrown when there's an issue with the config schema.\n */\nexport class ConfigError extends CrannError {\n constructor(message: string, public readonly field?: string) {\n super(field ? `Config error in '${field}': ${message}` : `Config error: ${message}`);\n this.name = \"ConfigError\";\n }\n}\n\n/**\n * Thrown when a store or agent has been destroyed/disconnected.\n */\nexport class LifecycleError extends CrannError {\n constructor(\n public readonly storeName: string,\n public readonly entity: \"store\" | \"agent\"\n ) {\n super(\n `${entity === \"store\" ? \"Store\" : \"Agent\"} \"${storeName}\" has been ${\n entity === \"store\" ? \"destroyed\" : \"disconnected\"\n } and cannot be used.`\n );\n this.name = \"LifecycleError\";\n }\n}\n\n/**\n * Thrown when there's a connection issue.\n */\nexport class ConnectionError extends CrannError {\n constructor(\n message: string,\n public readonly storeName: string,\n public readonly reason?: string\n ) {\n super(`Connection error for store \"${storeName}\": ${message}`);\n this.name = \"ConnectionError\";\n }\n}\n\n/**\n * Thrown when an action execution fails.\n */\nexport class ActionError extends CrannError {\n constructor(\n public readonly actionName: string,\n public readonly storeName: string,\n public readonly reason: string\n ) {\n super(`Action \"${actionName}\" failed in store \"${storeName}\": ${reason}`);\n this.name = \"ActionError\";\n }\n}\n\n/**\n * Thrown when action validation fails.\n */\nexport class ValidationError extends CrannError {\n constructor(\n public readonly actionName: string,\n public readonly storeName: string,\n public readonly reason: string\n ) {\n super(\n `Validation failed for action \"${actionName}\" in store \"${storeName}\": ${reason}`\n );\n this.name = \"ValidationError\";\n }\n}\n\n/**\n * Thrown when there's a storage key collision.\n */\nexport class StorageCollisionError extends CrannError {\n constructor(public readonly storeName: string) {\n super(\n `Store name \"${storeName}\" is already in use. Each store must have a unique name. ` +\n `If you're trying to connect to an existing store, use connectStore() instead.`\n );\n this.name = \"StorageCollisionError\";\n }\n}\n\n", "/**\n * Crann v2 Agent\n *\n * Client-side connection to a Crann store.\n * Runs in content scripts, popup, sidepanel, etc.\n */\n\nimport { connect as connectPorter } from \"../transport\";\nimport type { AgentInfo } from \"../transport\";\nimport type {\n ConfigSchema,\n ValidatedConfig,\n DerivedState,\n DerivedActions,\n StateChanges,\n} from \"../store/types\";\nimport type {\n AgentOptions,\n AgentAPI,\n AgentConnectionInfo,\n StateSubscriber,\n} from \"./types\";\nimport { LifecycleError } from \"../errors\";\n\n// =============================================================================\n// Agent Class\n// =============================================================================\n\nexport class Agent<TConfig extends ConfigSchema> implements AgentAPI<TConfig> {\n private readonly config: ValidatedConfig<TConfig>;\n private readonly options: AgentOptions;\n private readonly porter: ReturnType<typeof connectPorter>;\n\n private _state: DerivedState<TConfig>;\n private _agentInfo: AgentInfo | null = null;\n private _isConnected = false;\n private _isDisconnected = false;\n\n private readonly subscribers: Set<StateSubscriber<TConfig>> = new Set();\n private readonly readyCallbacks: Set<(state: DerivedState<TConfig>) => void> = new Set();\n private readonly disconnectCallbacks: Set<() => void> = new Set();\n private readonly reconnectCallbacks: Set<(state: DerivedState<TConfig>) => void> = new Set();\n\n private readyPromise: Promise<DerivedState<TConfig>>;\n private readyResolve!: (state: DerivedState<TConfig>) => void;\n\n private readonly actionsProxy: DerivedActions<TConfig>;\n\n constructor(config: ValidatedConfig<TConfig>, options: AgentOptions = {}) {\n this.config = config;\n this.options = options;\n\n // Initialize state with defaults\n this._state = this.buildDefaultState();\n\n // Create ready promise\n this.readyPromise = new Promise((resolve) => {\n this.readyResolve = resolve;\n });\n\n // Connect to store via porter (using store name as namespace)\n this.porter = connectPorter({\n namespace: config.name,\n debug: options.debug ?? false,\n });\n\n // Set up message handlers\n this.setupMessageHandlers();\n\n // Create typed actions proxy\n this.actionsProxy = this.createActionsProxy();\n }\n\n // ===========================================================================\n // Public API - Connection\n // ===========================================================================\n\n ready(): Promise<DerivedState<TConfig>> {\n this.assertNotDisconnected();\n return this.readyPromise;\n }\n\n onReady(callback: (state: DerivedState<TConfig>) => void): () => void {\n this.assertNotDisconnected();\n \n // If already connected, call immediately\n if (this._isConnected) {\n setTimeout(() => callback(this._state), 0);\n }\n \n this.readyCallbacks.add(callback);\n return () => {\n this.readyCallbacks.delete(callback);\n };\n }\n\n // ===========================================================================\n // Public API - State\n // ===========================================================================\n\n getState(): DerivedState<TConfig> {\n this.assertNotDisconnected();\n return { ...this._state };\n }\n\n get state(): DerivedState<TConfig> {\n return this.getState();\n }\n\n async setState(state: Partial<DerivedState<TConfig>>): Promise<void> {\n this.assertNotDisconnected();\n \n // Optimistically update local state\n this._state = { ...this._state, ...state };\n \n // Send to store\n this.porter.post({\n action: \"setState\",\n payload: { state },\n });\n }\n\n // ===========================================================================\n // Public API - Subscriptions\n // ===========================================================================\n\n subscribe(\n callbackOrKeys: ((changes: StateChanges<TConfig>, state: DerivedState<TConfig>) => void) | Array<keyof DerivedState<TConfig>>,\n maybeCallback?: (changes: StateChanges<TConfig>, state: DerivedState<TConfig>) => void\n ): () => void {\n this.assertNotDisconnected();\n\n let keys: Array<keyof DerivedState<TConfig>> | undefined;\n let callback: (changes: StateChanges<TConfig>, state: DerivedState<TConfig>) => void;\n\n if (typeof callbackOrKeys === \"function\") {\n callback = callbackOrKeys;\n } else {\n keys = callbackOrKeys;\n callback = maybeCallback!;\n }\n\n const subscriber: StateSubscriber<TConfig> = { keys, callback };\n this.subscribers.add(subscriber);\n\n return () => {\n this.subscribers.delete(subscriber);\n };\n }\n\n // ===========================================================================\n // Public API - Actions\n // ===========================================================================\n\n get actions(): DerivedActions<TConfig> {\n this.assertNotDisconnected();\n return this.actionsProxy;\n }\n\n // ===========================================================================\n // Public API - Agent Info\n // ===========================================================================\n\n getInfo(): AgentConnectionInfo | null {\n if (!this._agentInfo) return null;\n \n return {\n id: this._agentInfo.id,\n tabId: this._agentInfo.location.tabId,\n frameId: this._agentInfo.location.frameId,\n context: this._agentInfo.location.context,\n };\n }\n\n // ===========================================================================\n // Public API - Lifecycle\n // ===========================================================================\n\n onDisconnect(callback: () => void): () => void {\n this.assertNotDisconnected();\n this.disconnectCallbacks.add(callback);\n return () => {\n this.disconnectCallbacks.delete(callback);\n };\n }\n\n onReconnect(callback: (state: DerivedState<TConfig>) => void): () => void {\n this.assertNotDisconnected();\n this.reconnectCallbacks.add(callback);\n return () => {\n this.reconnectCallbacks.delete(callback);\n };\n }\n\n disconnect(): void {\n if (this._isDisconnected) return;\n\n this._isDisconnected = true;\n this._isConnected = false;\n\n // Clear all callbacks\n this.subscribers.clear();\n this.readyCallbacks.clear();\n this.disconnectCallbacks.clear();\n this.reconnectCallbacks.clear();\n\n // Note: Porter doesn't have a disconnect method, but we've cleaned up our state\n }\n\n // ===========================================================================\n // Private - Message Handling\n // ===========================================================================\n\n private setupMessageHandlers(): void {\n // Handle initial state from store\n this.porter.on({\n initialState: (message) => {\n const { state, info } = message.payload;\n \n this._state = state;\n this._agentInfo = info;\n this._isConnected = true;\n\n // Resolve ready promise\n this.readyResolve(this._state);\n\n // Notify ready callbacks\n this.readyCallbacks.forEach((cb) => {\n try {\n cb(this._state);\n } catch (e) {\n console.error(\"[Crann Agent] Error in onReady callback:\", e);\n }\n });\n\n // Notify initial subscribers\n this.notifySubscribers(state);\n },\n\n stateUpdate: (message) => {\n const { state: changes } = message.payload;\n this._state = { ...this._state, ...changes };\n this.notifySubscribers(changes);\n },\n\n rpcResult: (message) => {\n // RPC results are handled by the action proxy's pending promises\n // This is handled in createActionsProxy\n },\n });\n\n // Handle disconnect/reconnect from porter\n this.porter.onDisconnect(() => {\n this._isConnected = false;\n this.disconnectCallbacks.forEach((cb) => {\n try {\n cb();\n } catch (e) {\n console.error(\"[Crann Agent] Error in onDisconnect callback:\", e);\n }\n });\n });\n\n this.porter.onReconnect((info: AgentInfo) => {\n this._agentInfo = info;\n this._isConnected = true;\n this.reconnectCallbacks.forEach((cb) => {\n try {\n cb(this._state);\n } catch (e) {\n console.error(\"[Crann Agent] Error in onReconnect callback:\", e);\n }\n });\n });\n }\n\n private notifySubscribers(changes: StateChanges<TConfig>): void {\n this.subscribers.forEach((subscriber) => {\n // If subscriber has specific keys, check if any changed\n if (subscriber.keys) {\n const hasRelevantChange = subscriber.keys.some(\n (key) => key in changes\n );\n if (!hasRelevantChange) return;\n }\n\n try {\n subscriber.callback(changes, this._state);\n } catch (e) {\n console.error(\"[Crann Agent] Error in subscriber callback:\", e);\n }\n });\n }\n\n // ===========================================================================\n // Private - Actions Proxy\n // ===========================================================================\n\n private createActionsProxy(): DerivedActions<TConfig> {\n const pendingCalls = new Map<string, { resolve: (v: any) => void; reject: (e: any) => void }>();\n let callId = 0;\n\n // Listen for RPC results\n this.porter.on({\n rpcResult: (message) => {\n const { callId: id, result, error, success } = message.payload;\n const pending = pendingCalls.get(id);\n if (pending) {\n pendingCalls.delete(id);\n if (success) {\n pending.resolve(result);\n } else {\n pending.reject(new Error(error));\n }\n }\n },\n });\n\n return new Proxy({} as DerivedActions<TConfig>, {\n get: (_target, actionName: string) => {\n return async (...args: unknown[]) => {\n const id = `${callId++}`;\n \n return new Promise((resolve, reject) => {\n pendingCalls.set(id, { resolve, reject });\n \n this.porter.post({\n action: \"rpc\",\n payload: {\n callId: id,\n actionName,\n args,\n },\n });\n });\n };\n },\n });\n }\n\n // ===========================================================================\n // Private - Utilities\n // ===========================================================================\n\n private buildDefaultState(): DerivedState<TConfig> {\n const state: Record<string, unknown> = {};\n\n for (const [key, item] of Object.entries(this.config)) {\n if (key === \"name\" || key === \"version\" || key === \"actions\") continue;\n if (typeof item === \"object\" && item !== null && \"default\" in item) {\n state[key] = item.default;\n }\n }\n\n return state as DerivedState<TConfig>;\n }\n\n private assertNotDisconnected(): void {\n if (this._isDisconnected) {\n throw new LifecycleError(this.config.name, \"agent\");\n }\n }\n}\n\n// =============================================================================\n// Factory Function\n// =============================================================================\n\n/**\n * Connect to a Crann store from a client context.\n *\n * This should be called in content scripts, popup, sidepanel, or other\n * extension contexts that need to access the store. Uses the config's\n * `name` to find and connect to the matching store in the service worker.\n *\n * @param config - Validated config from createConfig() (must match store's config)\n * @param options - Optional connection options\n * @param options.debug - Enable debug logging\n * @returns An Agent instance for interacting with the store\n *\n * @example\n * // content.ts\n * import { createConfig, connectStore } from 'crann';\n *\n * const config = createConfig({\n * name: 'myFeature',\n * count: { default: 0, persist: 'local' },\n * });\n *\n * const agent = connectStore(config);\n *\n * // Wait for connection and initial state\n * const state = await agent.ready();\n * console.log('Initial count:', state.count);\n *\n * // Subscribe to changes\n * agent.subscribe((changes, state) => {\n * console.log('State changed:', changes);\n * });\n *\n * // Call actions\n * await agent.actions.increment();\n */\nexport function connectStore<TConfig extends ConfigSchema>(\n config: ValidatedConfig<TConfig>,\n options?: AgentOptions\n): AgentAPI<TConfig> {\n return new Agent(config, options);\n}\n\n"],
5
+ "mappings": "skBAAA,IAAAA,GAAA,GAAAC,GAAAD,GAAA,sBAAAE,KAAA,eAAAC,GAAAH,ICMA,IAAAI,EAUO,iBChBP,IAAAC,EAAiC,oCCI1B,IAAKC,OACVA,EAAA,cAAgB,gBAChBA,EAAA,UAAY,YACZA,EAAA,MAAQ,QACRA,EAAA,UAAY,YACZA,EAAA,SAAW,WACXA,EAAA,QAAU,UACVA,EAAA,QAAU,UAPAA,OAAA,IAsGL,IAAMC,EAAN,cAA0B,KAAM,CACrC,YACSC,EACPC,EACOC,EACP,CACA,MAAMD,CAAO,EAJN,UAAAD,EAEA,aAAAE,EAGP,KAAK,KAAO,aACd,CACF,ECjHA,SAASC,GAAkB,CACzB,OACE,OAAO,yBAA6B,KACpC,gBAAgB,wBAEpB,CAgCO,IAAMC,EAAN,MAAMA,CAAO,CAmCV,YAAoBC,EAAiB,CAAjB,aAAAA,CAAkB,CA7B9C,OAAe,UAAqB,CA7CtC,IAAAC,EAAAC,EAAAC,EA8CI,QAAIF,EAAAF,EAAO,gBAAP,YAAAE,EAAsB,SAAU,OAC3BF,EAAO,cAAc,MAG5B,OAAO,QAAY,QAClBG,EAAA,QAAQ,MAAR,YAAAA,EAAa,YAAa,gBACzBC,EAAA,QAAQ,MAAR,YAAAA,EAAa,cAAe,cAChB,EAAgB,CAClC,CAEA,OAAO,UAAUC,EAAwB,CACvCL,EAAO,cAAgBK,EACnBA,EAAQ,QAAU,SACpBL,EAAO,MAAQK,EAAQ,OAErBA,EAAQ,UAAY,SACtBL,EAAO,QAAUK,EAAQ,QAE7B,CAGA,OAAO,UAAUJ,EAAyB,CACxC,OAAK,KAAK,UAAU,IAAIA,CAAO,GAC7B,KAAK,UAAU,IAAIA,EAAS,IAAID,EAAOC,CAAO,CAAC,EAE1C,KAAK,UAAU,IAAIA,CAAO,CACnC,CAIA,MAAMK,KAAoBC,EAAa,CAChCP,EAAO,SACRA,EAAO,OAAS,GAClB,QAAQ,MAAM,WAAW,KAAK,OAAO,KAAKM,CAAO,GAAI,GAAGC,CAAI,CAEhE,CAEA,KAAKD,KAAoBC,EAAa,CAC/BP,EAAO,SACRA,EAAO,OAAS,GAClB,QAAQ,KAAK,WAAW,KAAK,OAAO,KAAKM,CAAO,GAAI,GAAGC,CAAI,CAE/D,CAEA,KAAKD,KAAoBC,EAAa,CAC/BP,EAAO,SACRA,EAAO,OAAS,GAClB,QAAQ,KAAK,WAAW,KAAK,OAAO,KAAKM,CAAO,GAAI,GAAGC,CAAI,CAE/D,CAEA,MAAMD,KAAoBC,EAAa,CAChCP,EAAO,SACRA,EAAO,OAAS,GAClB,QAAQ,MAAM,WAAW,KAAK,OAAO,KAAKM,CAAO,GAAI,GAAGC,CAAI,CAEhE,CAEA,MAAMD,KAAoBC,EAAa,CAChCP,EAAO,SACRA,EAAO,OAAS,GAClB,QAAQ,MAAM,WAAW,KAAK,OAAO,KAAKM,CAAO,GAAI,GAAGC,CAAI,CAEhE,CACF,EAvEaP,EACI,MAAkBA,EAAO,SAAS,EADtCA,EAEI,QAAmB,GAFvBA,EAGI,UAAiC,IAAI,IAH/C,IAAMQ,EAANR,ECvCP,IAAAS,EAAiC,oCACjCC,EAA6B,gBAkCtB,IAAMC,EAAN,KAAiE,CAKtE,YAAoBC,EAAgB,CAAhB,YAAAA,EAJpB,KAAQ,OAAqC,IAAI,IACjD,KAAQ,WAAsC,IAAI,IAClD,KAAQ,cAA4C,IAAI,IAGtD,KAAK,cAAc,IAAI,aAAc,IAAI,GAAK,EAC9C,KAAK,cAAc,IAAI,eAAgB,IAAI,GAAK,EAChD,KAAK,cAAc,IAAI,kBAAmB,IAAI,GAAK,CACrD,CAEO,SACLC,EACAC,EACqB,CAjDzB,IAAAC,EAAAC,EAkDI,KAAK,OAAO,MAAM,eAAgB,CAAE,QAAAF,EAAS,KAAAD,CAAK,CAAC,EACnD,IAAMI,EAAmB,KAAK,yBAAyBJ,CAAI,EAC3D,GAAI,CAACI,EAAkB,CACrB,KAAK,OAAO,MAAM,6CAA6C,EAC/D,MACF,CAEA,IAAMC,EAAoBD,EAAiB,QACrCE,EAAQF,EAAiB,OAAS,GAClCG,EAAUH,EAAiB,SAAW,EAE5C,KAAK,OAAO,MAAM,mCAAoC,CACpD,kBAAAC,EACA,MAAAC,EACA,QAAAC,CACF,CAAC,EAED,IAAMC,EAAgB,MAAM,KAAK,KAAK,WAAW,OAAO,CAAC,EAAE,OACxDC,GAEGA,EAAK,SAAS,UAAYJ,GAC1BI,EAAK,SAAS,QAAUH,GACxBG,EAAK,SAAS,UAAYF,CAGhC,EAEIC,EAAc,OAAS,GACzB,KAAK,OAAO,MAAM,8CAA+C,CAC/D,cAAAA,CACF,CAAC,EAGH,IAAME,IACJP,GAAAD,EAAA,KAAK,mBAAmB,CAAE,QAASG,EAAmB,MAAAC,EAAO,QAAAC,CAAQ,CAAC,IAAtE,YAAAL,EACI,OADJ,YAAAC,EACU,QAAO,EAAAQ,IAAO,EAE1B,KAAK,OAAO,MAAM,yBAAyBD,CAAO,EAAE,EAEpD,KAAK,OAAO,IAAIA,EAASV,CAAI,EAE7B,IAAMY,EAAuB,CAC3B,GAAIF,EACJ,SAAU,CAAE,QAASL,EAAmB,MAAAC,EAAO,QAAAC,CAAQ,EACvD,UAAW,KAAK,IAAI,EACpB,aAAc,KAAK,IAAI,CACzB,EACA,KAAK,WAAW,IAAIG,EAASE,CAAS,EAEtC,KAAK,OAAO,MAAM,2BAA2B,KAAK,UAAUA,CAAS,CAAC,EAAE,EACxEZ,EAAK,UAAU,YAAaa,GAC1B,KAAK,KAAK,eAAgBA,EAASD,CAAS,CAC9C,EAEA,IAAME,EAAe,CAAE,KAAAd,EAAM,KAAMY,CAAU,EAC7C,OAAAZ,EAAK,aAAa,YAAY,IAAM,CAClC,KAAK,KAAK,kBAAmBY,CAAS,EACtC,KAAK,OAAO,MAAM,8CAA+C,CAC/D,UAAAA,CACF,CAAC,EACD,KAAK,YAAYF,CAAO,CAC1B,CAAC,EAED,KAAK,KAAK,aAAcI,CAAK,EAC7B,KAAK,OAAO,MAAM,oCAAqC,CACrD,UAAAF,CACF,CAAC,EACMF,CACT,CAEO,mBAAmBK,EAAyC,CACjE,GAAM,CAAE,QAAAd,EAAS,MAAAK,EAAO,QAAAC,CAAQ,EAAIQ,EAE9BC,EAA8C,MAAM,KACxD,KAAK,WAAW,QAAQ,CAC1B,EAAE,KACA,CAAC,CAACC,EAAKR,CAAI,IACTA,EAAK,SAAS,UAAYR,GAC1BQ,EAAK,SAAS,QAAUH,GACxBG,EAAK,SAAS,UAAYF,CAC9B,EACA,GAAIS,IAAc,OAChB,YAAK,OAAO,MAAM,gCAAiC,CACjD,SAAAD,CACF,CAAC,EACM,KAET,IAAML,EAAUM,EAAU,CAAC,EACvBhB,EAAO,KAAK,OAAO,IAAIU,CAAO,EAC9BD,EAAO,KAAK,WAAW,IAAIC,CAAO,EACtC,MAAI,CAACV,GAAQ,CAACS,GACZ,KAAK,OAAO,MAAM,gCAAiC,CACjD,SAAAM,CACF,CAAC,EACM,MAEF,CAAE,KAAAf,EAAM,KAAAS,CAAK,CACtB,CAEO,mBAAmBR,EAAiC,CAIzD,OAHoB,MAAM,KAAK,KAAK,WAAW,QAAQ,CAAC,EAAE,OACxD,CAAC,CAACgB,EAAKC,CAAK,IAAMA,EAAM,SAAS,UAAYjB,CAC/C,EACqB,IAAI,CAAC,CAACgB,EAAKC,CAAK,KAAO,CAC1C,KAAM,KAAK,OAAO,IAAID,CAAG,EACzB,KAAMC,CACR,EAAE,CACJ,CAEO,cAAwB,CAE7B,OADc,MAAM,KAAK,KAAK,WAAW,QAAQ,CAAC,EACnC,IAAI,CAAC,CAACD,EAAKC,CAAK,KAAO,CACpC,KAAM,KAAK,OAAO,IAAID,CAAG,EACzB,KAAMC,CACR,EAAE,CACJ,CAEO,YAAYH,EAA6C,CAe9D,OAdoB,MAAM,KAAK,KAAK,WAAW,QAAQ,CAAC,EAAE,OACxD,CAAC,CAACE,EAAKC,CAAK,IAAM,CAChB,IAAMC,EAAaJ,EAAS,QACxBG,EAAM,SAAS,UAAYH,EAAS,QACpC,GACEK,EAAWL,EAAS,MACtBG,EAAM,SAAS,QAAUH,EAAS,MAClC,GACEM,EAAaN,EAAS,QACxBG,EAAM,SAAS,UAAYH,EAAS,QACpC,GACJ,OAAOI,GAAcC,GAAYC,CACnC,CACF,EACqB,IAAI,CAAC,CAACJ,EAAKC,CAAK,KAAO,CAC1C,KAAM,KAAK,OAAO,IAAID,CAAG,EACzB,KAAMC,CACR,EAAE,CACJ,CAEO,aAAaI,EAA2B,CAC7C,IAAItB,EAAO,KAAK,OAAO,IAAIsB,CAAE,EACzBb,EAAO,KAAK,WAAW,IAAIa,CAAE,EACjC,MAAI,CAACtB,GAAQ,CAACS,GACZ,KAAK,OAAO,MAAM,+BAAgC,CAChD,GAAAa,CACF,CAAC,EACM,MAEF,CAAE,KAAAtB,EAAM,KAAAS,CAAK,CACtB,CAEO,kBAAgC,CACrC,OAAO,MAAM,KAAK,KAAK,WAAW,OAAO,CAAC,CAC5C,CAEO,QAAQT,EAA6B,CAI1C,MAAO,CAAC,CAHa,MAAM,KAAK,KAAK,OAAO,OAAO,CAAC,EAAE,KACnDuB,GAAMA,EAAE,OAASvB,EAAK,IACzB,CAEF,CAEO,YAAYU,EAAkB,CAC/B,KAAK,OAAO,IAAIA,CAAO,GAAK,KAAK,WAAW,IAAIA,CAAO,GACzD,KAAK,OAAO,OAAOA,CAAO,EAC1B,KAAK,WAAW,OAAOA,CAAO,GAE9B,KAAK,OAAO,MAAM,6BAA8B,CAC9C,QAAAA,CACF,CAAC,CAEL,CAEO,aAAc,CACnB,IAAMc,EAAY,MAAM,KAAK,KAAK,OAAO,QAAQ,CAAC,EAC5CC,EAAgB,MAAM,KAAK,KAAK,WAAW,QAAQ,CAAC,EAC1D,KAAK,OAAO,MAAM,kBAAmB,CACnC,UAAAD,EACA,cAAAC,CACF,CAAC,CACH,CAiBO,GAAGC,EAAeC,EAAmB,CAC1C,IAAMC,EAAW,KAAK,cAAc,IAAIF,CAAK,EACzCE,GACFA,EAAS,IAAID,CAAO,CAExB,CAEQ,KAAKD,KAAkBG,EAAa,CAC1C,IAAMD,EAAW,KAAK,cAAc,IAAIF,CAAK,EAC7CE,GAAA,MAAAA,EAAU,QAASD,GAAYA,EAAQ,GAAGE,CAAI,EAChD,CAEQ,yBAAyB7B,EAMxB,CAxQX,IAAAE,EAAAC,EAAA2B,EAAAC,EAAAC,EAAAC,EAAAC,EAyQI,IAAMC,EAASnC,EAAK,OACpB,GAAI,CAACmC,EACH,YAAK,OAAO,MAAM,6CAA6C,EACxD,KAGT,IAAMC,EAAW,EAAAC,QAAQ,QAAQ,YAAY,EAGvCC,IAAapC,EAAAkC,GAAA,YAAAA,EAAkB,aAAlB,YAAAlC,EAA8B,eAAgB,GAC3DqC,EAAeH,EAAiB,cAAgB,GAChDI,IAAarC,EAAAiC,EAAiB,SAAjB,YAAAjC,EAAyB,gBAAiB,GACvDsC,EAAgBL,EAAiB,eAAiB,GAClDM,IAAkBZ,EAAAM,EAAiB,uBAAjB,YAAAN,EAAuC,SAAU,GACnEa,IACHZ,EAAAK,EAAiB,uBAAjB,YAAAL,EAAuC,YAAa,GACjDa,IACHZ,EAAAI,EAAiB,uBAAjB,YAAAJ,EAAuC,UAAW,GAI/Ca,EAAe,CACnB,UAAWP,EAAYA,EAAU,MAAM,GAAG,EAAE,IAAI,EAAI,iBACpD,QAASC,EAAcA,EAAY,MAAM,GAAG,EAAE,IAAI,EAAI,eACtD,MAAOC,EAAYA,EAAU,MAAM,GAAG,EAAE,IAAI,EAAI,aAChD,SAAUC,EAAeA,EAAa,MAAM,GAAG,EAAE,IAAI,EAAI,gBACzD,OAAQC,EAAiBA,EAAe,MAAM,GAAG,EAAE,IAAI,EAAI,cAC3D,UAAWC,EACPA,EAAkB,MAAM,GAAG,EAAE,IAAI,EACjC,iBACJ,QAASC,EACLA,EAAgB,MAAM,GAAG,EAAE,IAAI,EAC/B,cACN,EAGA,GAAIT,EAAO,KAAOA,EAAO,KAAO,CAACA,EAAO,IAAI,SAAS,cAAc,EACjE,MAAO,CACL,wBACA,MAAOA,EAAO,IAAI,GAClB,QAASA,EAAO,SAAW,EAC3B,IAAKA,EAAO,IACZ,SAAUnC,EAAK,IACjB,EAIF,GAAImC,EAAO,KAAOA,EAAO,IAAI,SAAS,cAAc,EAAG,CAErD,IAAMW,EADU,IAAI,IAAIX,EAAO,GAAG,EAAE,SACX,MAAM,GAAG,EAAE,IAAI,EAGxC,OAAW,CAACY,EAAUC,CAAW,IAAK,OAAO,QAAQH,CAAY,EAC/D,GAAIC,IAAaE,EAIf,MAAO,CACL,QAASD,EACT,QAAOd,EAAAE,EAAO,MAAP,YAAAF,EAAY,KAAM,EACzB,QAASE,EAAO,SAAW,EAC3B,IAAKA,EAAO,IACZ,SAAUnC,EAAK,IACjB,EAMJ,MAAO,CACL,kBACA,QAAOkC,EAAAC,EAAO,MAAP,YAAAD,EAAY,KAAM,EACzB,QAASC,EAAO,SAAW,EAC3B,IAAKA,EAAO,IACZ,SAAUnC,EAAK,IACjB,CACF,CAGA,MAAO,CACL,kBACA,MAAO,EACP,IAAKmC,EAAO,IACZ,SAAUnC,EAAK,IACjB,CACF,CACF,ECrVO,IAAMiD,EAAN,KAAwB,CAC7B,YACUC,EACAC,EACAC,EACR,CAHQ,qBAAAF,EACA,eAAAC,EACA,YAAAC,CACP,CAEI,iBAAiBC,EAAoB,CAC1C,GAAI,CAEF,GADA,KAAK,OAAO,KAAK,0BAA2BA,EAAK,IAAI,EACjD,CAACA,EAAK,KACR,MAAM,IAAIC,oBAER,wBACF,EAGF,GAAI,CAACD,EAAK,MAAQ,CAACA,EAAK,KAAK,WAAW,KAAK,UAAY,GAAG,EAC1D,MAAM,IAAIC,oBAER,yDAAwDD,GAAA,YAAAA,EAAM,OAAQ,gBAAgB,qBAAqB,KAAK,SAAS,EAC3H,EAGFA,EAAK,UAAU,YAAY,KAAK,kBAAkB,KAAK,KAAMA,CAAI,CAAC,EAElE,WAAW,IAAM,CACf,GAAI,CAAC,KAAK,gBAAgB,QAAQA,CAAI,EACpC,GAAI,CACFA,EAAK,WAAW,CAClB,OAASE,EAAG,CACV,KAAK,OAAO,MAAM,6BAA8BA,CAAC,CACnD,CAEJ,EAAG,GAAI,CACT,OAASC,EAAO,CACd,KAAK,sBAAsBH,EAAMG,CAAc,CACjD,CACF,CAEQ,kBAAkBH,EAAoBI,EAAoB,CAEhE,GAAIA,EAAQ,SAAW,cAIvB,GAAI,CAEFJ,EAAK,UAAU,eAAe,KAAK,kBAAkB,KAAK,KAAMA,CAAI,CAAC,EAErE,GAAM,CAAE,aAAAK,CAAa,EAAID,EAAQ,QAEjC,GAAI,CAACC,EACH,MAAM,IAAIJ,oBAER,kDACE,KAAK,UAAUG,CAAO,CAC1B,EAIF,IAAME,EAAU,KAAK,gBAAgB,SAASN,CAAI,EAElD,GAAI,CAACM,EACH,MAAM,IAAIL,oBAER,qBACF,EAIF,IAAMM,EAAQ,KAAK,gBAAgB,aAAaD,CAAO,EAEnDC,GACF,KAAK,kBAAkBA,CAAK,EAG9B,KAAK,gBAAgB,YAAY,CACnC,OAASJ,EAAO,CACd,KAAK,sBAAsBH,EAAMG,CAAc,CACjD,CACF,CAEQ,sBAAsBH,EAAoBG,EAAoB,CACpE,IAAMK,EACJL,aAAiBF,EACbE,EACA,IAAIF,sBAEFE,aAAiB,MAAQA,EAAM,QAAU,2BACzC,CAAE,cAAeA,CAAM,CACzB,EACN,KAAK,OAAO,MAAM,+BAAgC,CAChD,YAAAK,CACF,CAAC,EACD,GAAI,CACFR,EAAK,YAAY,CACf,OAAQ,eACR,QAAS,CAAE,MAAOQ,CAAY,CAChC,CAAC,CACH,OAASN,EAAG,CACV,KAAK,OAAO,MAAM,iCAAkC,CAClD,MAAOA,CACT,CAAC,CACH,CAEA,GAAI,CACFF,EAAK,WAAW,CAClB,OAASE,EAAG,CACV,KAAK,OAAO,MAAM,8BAA+B,CAC/C,MAAOA,CACT,CAAC,CACH,CACF,CAEO,kBAAkBK,EAAc,CAIrC,GAHA,KAAK,OAAO,MAAM,kDAAmD,CACnE,MAAAA,CACF,CAAC,EACG,CAACA,EAAM,KACT,MAAM,IAAIN,iBAER,oDACF,EAEFM,EAAM,KAAK,YAAY,CACrB,OAAQ,mBACR,QAAS,CACP,KAAMA,EAAM,KACZ,mBAAoB,KAAK,gBAAgB,iBAAiB,CAC5D,CACF,CAAC,CACH,CACF,EC7HO,IAAME,EAAN,KAAqB,CAQ1B,YACUC,EACAC,EACR,CAFQ,qBAAAD,EACA,YAAAC,EATV,KAAQ,eAGJ,IAAI,IACR,KAAQ,iBAAyC,IAAI,IAOnD,KAAK,sBAAwB,CAC3B,8BAA+B,CAC7BC,EACAC,IACG,CAlCX,IAAAC,EAmCQ,GAAI,CAACD,GAAS,CAACA,EAAM,GAAI,OACzB,IAAME,GAAYD,EAAA,KAAK,gBAAgB,aAAaD,EAAM,EAAE,IAA1C,YAAAC,EAA6C,KAC/D,GAAI,CAACC,EAAW,CACd,KAAK,OAAO,MAAM,qCAAsCF,EAAM,EAAE,EAChE,MACF,CACA,KAAK,OAAO,MACV,mDACAA,EAAM,GACND,CACF,EACA,KAAK,UAAU,gBAAiBG,CAAS,CAC3C,CACF,CACF,CAEA,MAAa,KACXH,EACAI,EACe,CACf,OAAO,IAAI,QAAQ,CAACC,EAASC,IAAW,CACtC,GAAI,CACF,KAAK,OAAO,MAAM,yBAA0B,CAC1C,OAAQN,EAAQ,OAChB,OAAAI,CACF,CAAC,EAED,IAAMG,EAAY,WAAW,IAAM,CACjC,IAAMC,EAAQ,IAAI,MAAM,2BAA2B,EACnD,KAAK,OAAO,MAAM,gBAAiBA,CAAK,EACxCF,EAAOE,CAAK,CACd,EAAG,GAAI,EAEHJ,IAAW,OACb,KAAK,iBAAiBJ,CAAO,EAEpBS,GAAkBL,CAAM,EACjC,KAAK,eAAeJ,EAASI,CAAM,EAC1BM,GAAgBN,CAAM,EAC/B,KAAK,cAAcJ,EAASI,CAAM,EACzB,OAAOA,GAAW,SAC3B,KAAK,SAASJ,EAASI,CAAM,EAE7B,KAAK,UAAUJ,EAASI,CAAM,EAGhC,aAAaG,CAAS,EACtBF,EAAQ,CACV,OAASG,EAAO,CACd,IAAMG,EACJH,aAAiB,MAAQA,EAAM,QAAU,gBAC3C,KAAK,OAAO,MAAM,0BAA2BG,CAAY,EACzDL,EAAO,IAAI,MAAM,2BAA2BK,CAAY,EAAE,CAAC,CAC7D,CACF,CAAC,CACH,CAEQ,iBAAiBX,EAA6B,CACpD,KAAK,OAAO,KAAK,uCAAwCA,CAAO,EAChE,KAAK,gBAAgB,aAAa,EAAE,QAASC,GAAU,CACjDA,EAAM,MACRA,EAAM,KAAK,YAAYD,CAAO,CAElC,CAAC,CACH,CAGQ,UAAUA,EAAuBY,EAAqB,CAE5D,IAAMC,EAAS,KAAK,gBAAgB,YAAY,CAC9C,wBACA,MAAAD,CACF,CAAC,EACD,GAAIC,EAAO,SAAW,EAAG,CACvB,WAAK,OAAO,KAAK,kCAAmCD,CAAK,EACnD,IAAIE,mBAER,mCAAmCF,CAAK,GACxC,CAAE,cAAeZ,CAAQ,CAC3B,EACA,MACF,CACAa,EAAO,QAASZ,GAAU,CACpBA,EAAM,MACR,KAAK,WAAWD,EAASC,EAAM,IAAI,CAEvC,CAAC,CACH,CAEQ,eACND,EACAe,EACM,CACS,KAAK,gBAAgB,YAAYA,CAAQ,EACjD,QAASd,GAAU,CACxB,GAAI,CAACA,EAAM,KACT,MAAM,IAAIa,mBAER,0BACA,CAAE,UAAWb,EAAM,IAAK,CAC1B,EAGF,KAAK,WAAWD,EAASC,EAAM,IAAI,CACrC,CAAC,CACH,CAEQ,cAAcD,EAAuBgB,EAA8B,CAC1D,KAAK,gBAAgB,YAAY,CAC9C,QAAAA,CACF,CAAC,EACM,QAASf,GAAU,CACxB,GAAI,CAACA,EAAM,KACT,MAAM,IAAIa,mBAER,0BACA,CAAE,UAAWb,EAAM,IAAK,CAC1B,EAGF,KAAK,WAAWD,EAASC,EAAM,IAAI,CACrC,CAAC,CACH,CAEQ,WAAWD,EAAuBiB,EAA0B,CAClE,GAAI,CACFA,EAAK,YAAYjB,CAAO,CAC1B,OAASQ,EAAO,CACd,MAAM,IAAIM,mBAER,iCACA,CAAE,cAAeN,EAAO,QAAAR,CAAQ,CAClC,CACF,CACF,CAEQ,SAASA,EAAuBkB,EAAwB,CAC9D,IAAMjB,EAAQ,KAAK,gBAAgB,aAAaiB,CAAO,EACvD,GAAI,EAACjB,GAAA,MAAAA,EAAO,MACV,MAAM,IAAIa,mBAER,2BAA2BI,CAAO,EACpC,EAEF,KAAK,WAAWlB,EAASC,EAAM,IAAI,CACrC,CAEO,UAAUkB,EAAuB,CAEb,MAAM,KAAK,KAAK,gBAAgB,EAAE,KACxDC,GAAa,KAAK,UAAUA,EAAS,MAAM,IAAM,KAAK,UAAUD,CAAM,CACzE,GAGE,KAAK,OAAO,KACV,6CAA6C,KAAK,UAAUA,CAAM,CAAC,EACrE,EAGF,IAAME,EAAmC,CACvC,OAAAF,EACA,SAAWG,GAAoC,CAC7C,IAAMC,EAAUJ,EAAOG,EAAM,QAAQ,MAAM,EAC3C,GAAIC,EAAS,CACX,KAAK,OAAO,MAAM,8BAA+B,CAAE,MAAAD,CAAM,CAAC,EAC1D,GAAM,CAAE,QAAAtB,EAAS,GAAGwB,CAAK,EAAIF,EAC7BC,EAAQvB,EAASwB,CAAI,CACvB,MACE,KAAK,OAAO,MAAM,+BAAgC,CAAE,MAAAF,CAAM,CAAC,CAE/D,CACF,EACA,YAAK,iBAAiB,IAAID,CAAe,EAElC,IAAM,CACX,KAAK,iBAAiB,OAAOA,CAAe,CAC9C,CACF,CAGO,GAAGF,EAAuB,CAC/B,OAAO,KAAK,UAAUA,CAAM,CAC9B,CAGO,sBAAsBnB,EAAcwB,EAAiB,CAC1D,KAAK,OAAO,MAAM,mBAAoB,CACpC,QAAAxB,EACA,KAAAwB,CACF,CAAC,EAED,KAAK,YAAY,CAAE,GAAGA,EAAM,QAAAxB,CAAQ,CAAC,CACvC,CAEQ,UACNsB,EACAG,EACA,CAxOJ,IAAAvB,EAyOI,KAAK,OAAO,MAAM,mBAAoBoB,EAAOG,CAAG,GAChDvB,EAAA,KAAK,eACF,IAAIoB,CAAK,IADZ,MAAApB,EAEI,QAASkB,GAAcA,EAAyBK,CAAG,EACzD,CAIQ,YAAYC,EAAwC,CAK1D,GAJA,KAAK,OAAO,MAAM,8CAA+C,CAC/D,aAAAA,CACF,CAAC,EAEGA,EAAa,QAAQ,OAAO,WAAW,SAAS,EAAG,CACrD,IAAMH,EAAU,KAAK,sBAAsBG,EAAa,QAAQ,MAAM,EACtE,GAAIH,EAAS,CACX,KAAK,OAAO,MAAM,iCAAkC,CAClD,aAAAG,CACF,CAAC,EACD,GAAM,CAAE,QAAA1B,EAAS,GAAGwB,CAAK,EAAIE,EAC7BH,EAAQvB,EAASwB,CAAI,EACrB,MACF,CACF,CAGME,EAAa,QAAQ,SACzB,KAAK,OAAO,MACV,8BACAA,EAAa,QAAQ,MACvB,EACA,KAAK,KAAKA,EAAa,QAASA,EAAa,QAAQ,MAAM,GAG7D,IAAIC,EAAe,EAEnB,KAAK,OAAO,MAAM,6CAA6C,EAC/D,OAAW,CAAE,SAAAP,EAAU,OAAAD,CAAO,IAAK,KAAK,iBAClCA,EAAOO,EAAa,QAAQ,MAAM,IACpCN,EAASM,CAAwC,EACjDC,IACA,KAAK,OAAO,MAAM,2CAA4C,CAC5D,SAAAP,EACA,OAAAD,CACF,CAAC,GAIDQ,IAAiB,EACnB,KAAK,OAAO,KACV,gCACAD,EAAa,QAAQ,MACvB,EAEA,KAAK,OAAO,MACV,sBAAsBC,CAAY,uBACpC,CAEJ,CAEO,YACLL,EACAF,EACA,CACA,OAAK,KAAK,eAAe,IAAIE,CAAK,GAChC,KAAK,eAAe,IAAIA,EAAO,IAAI,GAAK,EAE1C,KAAK,eACF,IAAIA,CAAK,EACT,IAAIF,CAAuC,EAEvC,IAAM,CAhTjB,IAAAlB,GAiTMA,EAAA,KAAK,eACF,IAAIoB,CAAK,IADZ,MAAApB,EAEI,OAAOkB,EACb,CACF,CAEO,iBAAiBI,EAAiB,CAEvC,KAAK,iBAAiB,QAASH,GAAoB,CAC7CA,EAAgB,OAAOG,EAAK,EAAE,GAChC,KAAK,iBAAiB,OAAOH,CAAe,CAEhD,CAAC,EACD,KAAK,OAAO,KAAK,sBAAuB,CAAE,KAAAG,CAAK,CAAC,EAChD,KAAK,UAAU,eAAgBA,CAAI,CACrC,CAEO,cAAcA,EAAiB,CACpC,KAAK,OAAO,KAAK,mBAAoB,CAAE,KAAAA,CAAK,CAAC,EAC7C,KAAK,UAAU,YAAaA,CAAI,CAClC,CAEO,UAAUJ,EAAiC,CAChD,OAAO,KAAK,YAAY,YAAaA,CAAQ,CAC/C,CAEO,cAAcA,EAAqC,CACxD,OAAO,KAAK,YAAY,gBAAiBA,CAAQ,CACnD,CAEO,aAAaA,EAAoC,CACtD,OAAO,KAAK,YAAY,eAAgBA,CAAQ,CAClD,CACF,EAGA,SAASX,GAAkBL,EAAkD,CAC3E,OACE,OAAOA,GAAW,UAClBA,IAAW,MACX,YAAaA,GACb,UAAWA,GACX,YAAaA,CAEjB,CAEA,SAASM,GAAgBN,EAAgD,CACvE,OACE,OAAOA,GAAW,UAClB,OAAO,OAAOwB,CAAa,EAAE,SAASxB,CAAuB,CAEjE,CL3UO,IAAMyB,EAAN,MAAMA,CAAa,CAShB,YAAYC,EAAoBC,EAA+B,CAqBrE,IAnBIA,GAAA,YAAAA,EAAS,SAAU,QACrBC,EAAO,UAAU,CAAE,QAASD,EAAQ,KAAM,CAAC,EAG7C,KAAK,OAASC,EAAO,UAAU,IAAI,EACnC,KAAK,UAAYF,GAAa,SACzBA,GACH,KAAK,OAAO,MAAM,+CAA+C,EAGnE,KAAK,aAAe,IAAIG,EAAa,KAAK,MAAM,EAChD,KAAK,eAAiB,IAAIC,EAAe,KAAK,aAAc,KAAK,MAAM,EACvE,KAAK,kBAAoB,IAAIC,EAC3B,KAAK,aACL,KAAK,UACL,KAAK,MACP,EACA,KAAK,OAAO,KAAK,uCAAuC,KAAK,SAAS,EAAE,EAEpE,CAACC,EAAgB,EACnB,MAAM,IAAIC,oBAER,qCACF,EAIF,KAAK,aAAa,GAChB,eACA,CAACC,EAAcC,IAAwB,CACrC,KAAK,eAAe,sBAAsBD,EAASC,CAAQ,CAC7D,CACF,EAEA,KAAK,aAAa,GAAG,kBAAoBA,GAAwB,CAC/D,KAAK,eAAe,iBAAiBA,CAAQ,CAC/C,CAAC,EAED,KAAK,aAAa,GAAG,aAAeC,GAAiB,CACnD,KAAK,OAAO,MAAM,uBAAwB,CAAE,MAAAA,CAAM,CAAC,EACnD,KAAK,eAAe,cAAcA,EAAM,IAAI,EAC5C,KAAK,kBAAkB,kBAAkBA,CAAK,CAChD,CAAC,EAED,EAAAC,QAAQ,QAAQ,UAAU,YACxB,KAAK,kBAAkB,iBAAiB,KAAK,KAAK,iBAAiB,CACrE,CACF,CAEA,OAAc,YACZX,EAAoB,SACpBC,EACc,CACd,OAAAF,EAAa,aAAa,MACxB,mCAAmCC,CAAS,EAC9C,EACKD,EAAa,UAAU,IAAIC,CAAS,GAQ9BC,GAAA,YAAAA,EAAS,SAAU,QAE5BC,EAAO,UAAU,CAAE,QAASD,EAAQ,KAAM,CAAC,GAT3CF,EAAa,aAAa,KACxB,wCAAwCC,CAAS,EACnD,EACAD,EAAa,UAAU,IACrBC,EACA,IAAID,EAAaC,EAAWC,CAAO,CACrC,GAKKF,EAAa,UAAU,IAAIC,CAAS,CAC7C,CAGO,KAAKQ,EAAuBI,EAAuC,CACxE,OAAO,KAAK,eAAe,KAAKJ,EAASI,CAAM,CACjD,CAEO,UAAUC,EAAoC,CACnD,OAAO,KAAK,eAAe,UAAUA,CAAM,CAC7C,CAEO,GAAGA,EAAoC,CAC5C,OAAO,KAAK,eAAe,GAAGA,CAAM,CACtC,CAEO,UAAUC,EAA8C,CAC7D,OAAO,KAAK,eAAe,UAAUA,CAAQ,CAC/C,CAEO,aAAaA,EAAiD,CACnE,OAAO,KAAK,eAAe,aAAaA,CAAQ,CAClD,CAEO,cAAcA,EAAkD,CACrE,OAAO,KAAK,eAAe,cAAcA,CAAQ,CACnD,CAGO,QAAQC,EAA+B,CArIhD,IAAAC,EAsII,QAAOA,EAAA,KAAK,aAAa,aAAaD,CAAG,IAAlC,YAAAC,EAAqC,OAAQ,IACtD,CAGO,aAAaC,EAAgC,CAClD,OAAO,KAAK,aAAa,aAAaA,CAAO,CAC/C,CAEO,mBAAmBC,EAAyC,CACjE,OAAO,KAAK,aAAa,mBAAmBA,CAAQ,CACtD,CAEO,YAAYA,EAA6C,CAC9D,OAAO,KAAK,aAAa,YAAYA,CAAQ,CAC/C,CACF,EA5HanB,EACI,UAAuC,IAAI,IAD/CA,EAMI,aAAeG,EAAO,UAAU,IAAI,EAN9C,IAAMiB,EAANpB,EMzBP,IAAAqB,EAAiC,oCCS1B,IAAMC,EAAN,KAAmB,CAMxB,YAAYC,EAAgB,CAL5B,KAAQ,MAAyB,CAAC,EAElC,KAAiB,aAAuB,IACxC,KAAiB,cAAwB,EAAI,GAAK,IAGhD,KAAK,OAASA,EACd,KAAK,OAAO,MAAM,2BAA4B,CAC5C,aAAc,KAAK,aACnB,cAAe,GAAG,KAAK,cAAgB,GAAI,UAC7C,CAAC,CACH,CAEO,QAAQC,EAAuBC,EAAgC,CAEpE,IAAMC,EAAW,KAAK,MAAM,OAC5B,KAAK,QAAQ,EACTA,IAAa,KAAK,MAAM,QAC1B,KAAK,OAAO,MACV,cAAcA,EAAW,KAAK,MAAM,MAAM,eAC5C,EAIE,KAAK,MAAM,QAAU,KAAK,eAC5B,KAAK,OAAO,KAAK,iDAAkD,CACjE,UAAW,KAAK,MAAM,OACtB,QAAS,KAAK,YAChB,CAAC,EACD,KAAK,MAAM,MAAM,GAGnB,KAAK,MAAM,KAAK,CACd,QAAAF,EACA,OAAAC,EACA,UAAW,KAAK,IAAI,CACtB,CAAC,EAED,KAAK,OAAO,MAAM,iBAAkB,CAClC,UAAW,KAAK,MAAM,OACtB,QAAAD,EACA,OAAAC,EACA,UAAW,IAAI,KAAK,EAAE,YAAY,CACpC,CAAC,CACH,CAEO,SAA2B,CAChC,IAAME,EAAW,CAAC,GAAG,KAAK,KAAK,EAC/B,YAAK,MAAQ,CAAC,EACd,KAAK,OAAO,KAAK,YAAYA,EAAS,MAAM,YAAa,CACvD,cAAeA,EAAS,CAAC,EACrB,IAAI,KAAKA,EAAS,CAAC,EAAE,SAAS,EAAE,YAAY,EAC5C,KACJ,cAAeA,EAASA,EAAS,OAAS,CAAC,EACvC,IAAI,KAAKA,EAASA,EAAS,OAAS,CAAC,EAAE,SAAS,EAAE,YAAY,EAC9D,IACN,CAAC,EACMA,CACT,CAEO,SAAmB,CACxB,OAAO,KAAK,MAAM,SAAW,CAC/B,CAEQ,SAAgB,CACtB,IAAMC,EAAM,KAAK,IAAI,EACfF,EAAW,KAAK,MAAM,OAC5B,KAAK,MAAQ,KAAK,MAAM,OACrBG,GAASD,EAAMC,EAAK,UAAY,KAAK,aACxC,EACIH,IAAa,KAAK,MAAM,QAC1B,KAAK,OAAO,MACV,cAAcA,EAAW,KAAK,MAAM,MAAM,oBAC1C,CACE,UAAW,KAAK,MAAM,OACtB,OAAQ,GAAG,KAAK,cAAgB,GAAI,UACtC,CACF,CAEJ,CACF,EDlFO,IAAMI,EAAN,KAA6B,CAiBlC,YACmBC,EACjBC,EACA,CAFiB,eAAAD,EAjBnB,KAAiB,mBAAqB,IACtC,KAAiB,mBAAqB,IACtC,KAAQ,gBAAyC,KACjD,KAAQ,eAAwC,KAChD,KAAQ,UAA8B,KACtC,KAAQ,KAA4B,KAIpC,KAAQ,eAA0B,GAClC,KAAQ,sBAAgC,EAGxC,KAAQ,oBAA+C,IAAI,IAC3D,KAAQ,mBAA6C,IAAI,IAMvD,KAAK,aAAe,GAAG,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,UAAU,EAAG,CAAC,CAAC,GAC/E,KAAK,OAASC,EACd,KAAK,aAAe,IAAIC,EAAaD,CAAM,CAC7C,CAMO,aAAaE,EAA0C,CAC5D,YAAK,oBAAoB,IAAIA,CAAQ,EAC9B,IAAM,CACX,KAAK,oBAAoB,OAAOA,CAAQ,CAC1C,CACF,CAMO,YAAYA,EAAyC,CAC1D,YAAK,mBAAmB,IAAIA,CAAQ,EAC7B,IAAM,CACX,KAAK,mBAAmB,OAAOA,CAAQ,CACzC,CACF,CAEQ,gBAAuB,CAC7B,KAAK,OAAO,MAAM,4BAA6B,CAC7C,cAAe,KAAK,oBAAoB,IAC1C,CAAC,EACD,KAAK,oBAAoB,QAASA,GAAa,CAC7C,GAAI,CACFA,EAAS,CACX,OAASC,EAAO,CACd,KAAK,OAAO,MAAM,gCAAiCA,CAAK,CAC1D,CACF,CAAC,CACH,CAEQ,cAAcC,EAAuB,CAC3C,KAAK,OAAO,MAAM,2BAA4B,CAC5C,cAAe,KAAK,mBAAmB,KACvC,KAAAA,CACF,CAAC,EACD,KAAK,mBAAmB,QAASF,GAAa,CAC5C,GAAI,CACFA,EAASE,CAAI,CACf,OAASD,EAAO,CACd,KAAK,OAAO,MAAM,+BAAgCA,CAAK,CACzD,CACF,CAAC,CACH,CAEA,MAAa,sBAAsC,CAnFrD,IAAAE,EAoFI,GAAI,CACE,KAAK,iBACP,aAAa,KAAK,eAAe,EAGnC,IAAMC,EAAW,GAAG,KAAK,SAAS,IAAI,KAAK,YAAY,GACvD,KAAK,OAAO,MAAM,kCAAmC,CAAE,SAAAA,CAAS,CAAC,EACjE,KAAK,KAAO,EAAAC,QAAQ,QAAQ,QAAQ,CAAE,KAAMD,CAAS,CAAC,EAEtD,IAAME,EAAmB,IAAI,QAAc,CAACC,EAASC,IAAW,CA7FtE,IAAAL,EA8FQ,IAAMM,EAAU,WACd,IACED,EACE,IAAIE,uBAEF,4CACF,CACF,EACF,KAAK,kBACP,EAEMC,EAAaC,GAAiB,CAzG5C,IAAAT,EAAAU,EA0GcD,EAAQ,SAAW,oBACrB,KAAK,OAAO,MAAM,sBAAuBA,CAAO,EAChD,aAAaH,CAAO,EACpB,KAAK,UAAYG,EAAQ,QAAQ,KACjC,KAAK,OAAO,MAAM,wBAAyB,CACzC,UAAW,KAAK,SAClB,CAAC,GACDT,EAAA,KAAK,OAAL,MAAAA,EAAW,UAAU,eAAeQ,GACpCJ,EAAQ,GACCK,EAAQ,SAAW,iBAC5B,aAAaH,CAAO,GACpBI,EAAA,KAAK,OAAL,MAAAA,EAAW,UAAU,eAAeF,GACpC,KAAK,OAAO,MAAM,SAAUC,CAAO,EACnCJ,EACE,IAAIE,EACFE,EAAQ,QAAQ,KAChBA,EAAQ,QAAQ,QAChBA,EAAQ,QAAQ,OAClB,CACF,EAEJ,GAEAT,EAAA,KAAK,OAAL,MAAAA,EAAW,UAAU,YAAYQ,EACnC,CAAC,GAEDR,EAAA,KAAK,OAAL,MAAAA,EAAW,YAAY,CACrB,OAAQ,cACR,QAAS,CACP,KAAM,KAAK,UACX,aAAc,KAAK,YACrB,CACF,GAEA,MAAMG,EAGN,MAAM,KAAK,sBAAsB,CACnC,OAASL,EAAO,CACd,WAAK,OAAO,MAAM,oCAAqCA,CAAK,EAC5D,KAAK,iBAAiB,KAAK,IAAK,EAC1BA,CACR,CACF,CAEA,MAAc,uBAAuC,CACnD,GAAI,CAAC,KAAK,MAAQ,KAAK,aAAa,QAAQ,EAC1C,OAGF,IAAMa,EAAW,KAAK,aAAa,QAAQ,EAC3C,KAAK,OAAO,KACV,cAAcA,EAAS,MAAM,qCAC/B,EAEA,OAAW,CAAE,QAAAF,EAAS,OAAAG,CAAO,IAAKD,EAChC,GAAI,CAEF,IAAME,EAAgB,CAAE,GAAGJ,CAAQ,EAC/BG,IACFC,EAAc,OAASD,GAEzB,KAAK,KAAK,YAAYC,CAAa,EACnC,KAAK,OAAO,MAAM,sCAAuC,CACvD,QAASA,CACX,CAAC,CACH,OAASf,EAAO,CACd,KAAK,OAAO,MAAM,oCAAqCA,CAAK,EAE5D,KAAK,aAAa,QAAQW,EAASG,CAAM,EACzC,KAAK,OAAO,MAAM,oCAAoC,CACxD,CAEJ,CAEO,SAA+B,CACpC,OAAO,KAAK,IACd,CAEO,cAAiC,CACtC,OAAO,KAAK,SACd,CAEO,cAAuB,CAC5B,OAAO,KAAK,SACd,CAEO,iBAAiBE,EAAoB,CAC1C,KAAK,OAAO,KAAK,oBAAqB,CACpC,SAAUA,EAAK,KACf,aAAc,KAAK,aACnB,eAAgB,KAAK,aAAa,QAAQ,EAAI,EAAI,MACpD,CAAC,EACD,KAAK,KAAO,KACZ,KAAK,UAAY,KAGjB,KAAK,eAAe,EAGf,KAAK,gBACR,KAAK,0BAA0B,CAEnC,CAEQ,2BAAkC,CACxC,KAAK,eAAiB,GACtB,KAAK,sBAAwB,EAEzB,KAAK,gBACP,cAAc,KAAK,cAAc,EAGnC,KAAK,OAAO,KAAK,iCAAkC,CACjD,SAAU,KAAK,mBACf,eAAgB,KAAK,aAAa,QAAQ,EAAI,EAAI,MACpD,CAAC,EAED,KAAK,eAAiB,YAAY,SAAY,CAC5C,KAAK,wBACL,GAAI,CACF,KAAK,OAAO,MAAM,wBAAwB,KAAK,qBAAqB,EAAE,EACtE,MAAM,KAAK,qBAAqB,EAChC,KAAK,eAAiB,GAClB,KAAK,iBACP,cAAc,KAAK,cAAc,EACjC,KAAK,eAAiB,MAExB,KAAK,OAAO,KAAK,0BAA2B,CAC1C,SAAU,KAAK,sBACf,eAAgB,KAAK,aAAa,QAAQ,EAAI,EAAI,MACpD,CAAC,EAGG,KAAK,WACP,KAAK,cAAc,KAAK,SAAS,CAErC,OAAShB,EAAO,CACd,KAAK,OAAO,MACV,wBAAwB,KAAK,qBAAqB,WAClDA,CACF,CACF,CACF,EAAG,KAAK,kBAAkB,CAC5B,CAEO,aAAaW,EAAcG,EAAoB,CACpD,KAAK,aAAa,QAAQH,EAASG,CAAM,EACzC,KAAK,OAAO,MAAM,2BAA4B,CAC5C,QAAAH,EACA,OAAAG,EACA,UAAW,KAAK,aAAa,QAAQ,EAAI,EAAI,MAC/C,CAAC,CACH,CACF,EE1PO,IAAMG,EAAN,KAA0B,CAO/B,YAA6BC,EAAgB,CAAhB,YAAAA,EAN7B,KAAiB,eAAiB,IAClC,KAAiB,gBAAkB,IACnC,KAAQ,aACN,CAAC,EACH,KAAQ,SAAyC,IAAI,GAEP,CAEvC,cAAcC,EAAoBC,EAAc,CAErD,GADA,KAAK,OAAO,MAAM,2BAA4BA,CAAO,EACjD,KAAK,SAAS,OAAS,EAAG,CAC5B,GAAI,KAAK,aAAa,QAAU,KAAK,eAAgB,CACnD,KAAK,OAAO,KAAK,wCAAyCA,CAAO,EACjE,MACF,CACA,KAAK,OAAO,KACV,yDACAA,CACF,EACA,KAAK,aAAa,KAAK,CAAE,QAAAA,EAAS,UAAW,KAAK,IAAI,CAAE,CAAC,EACzD,MACF,CACA,KAAK,eAAeD,EAAMC,CAAO,CACnC,CAGO,UAAUC,EAAuB,CACtC,KAAK,OAAO,MAAM,yCAA0CA,CAAM,EAElE,KAAK,SAAS,MAAM,EACpB,KAAK,GAAGA,CAAM,EAEd,KAAK,sBAAsB,CAC7B,CAEO,GAAGA,EAAuB,CAC/B,KAAK,OAAO,MAAM,wCAAyCA,CAAM,EAEjE,OAAO,QAAQA,CAAM,EAAE,QAAQ,CAAC,CAACC,EAAQC,CAAO,IAAM,CAC/C,KAAK,SAAS,IAAID,CAAM,GAC3B,KAAK,SAAS,IAAIA,EAAQ,CAAC,CAAC,EAE9B,KAAK,SAAS,IAAIA,CAAM,EAAG,KAAKC,CAAO,CACzC,CAAC,EAED,KAAK,sBAAsB,CAC7B,CAEQ,uBAAwB,CAC9B,KAAO,KAAK,aAAa,OAAS,GAAG,CACnC,IAAMC,EAAO,KAAK,aAAa,CAAC,EAChC,GAAI,KAAK,IAAI,EAAIA,EAAK,UAAY,KAAK,gBAAiB,CACtD,KAAK,OAAO,KACV,sCACA,KAAK,aAAa,MAAM,CAC1B,EACA,QACF,CACA,KAAK,eAAe,KAAOA,EAAK,OAAO,EACvC,KAAK,aAAa,MAAM,CAC1B,CACF,CAEQ,eAAeL,EAAoBC,EAAc,CACvD,IAAME,EAASF,EAAQ,OACjBK,EAAiB,KAAK,SAAS,IAAIH,CAAM,GAAK,CAAC,EAEjDG,EAAe,OAAS,GAC1B,KAAK,OAAO,MACV,SAASA,EAAe,MAAM,yBAAyBH,CAAM,EAC/D,EACAG,EAAe,QAASF,GAAYA,EAAQH,CAAO,CAAC,GAEpD,KAAK,OAAO,MAAM,wCAAwCE,CAAM,EAAE,CAEtE,CAEO,KACLH,EACAC,EACAM,EACA,CACA,KAAK,OAAO,MAAM,kBAAmB,CACnC,OAAQN,EAAQ,OAChB,OAAAM,EACA,WAAY,CAAC,CAACN,EAAQ,OACxB,CAAC,EAED,GAAI,CACEM,IACFN,EAAQ,OAASM,GAEnBP,EAAK,YAAYC,CAAO,CAC1B,OAASO,EAAO,CACd,MAAM,IAAIC,mBAER,yBACA,CAAE,cAAeD,EAAO,QAAAP,EAAS,OAAAM,CAAO,CAC1C,CACF,CACF,CACF,EChFO,IAAMG,EAAN,MAAMA,CAAY,CAMf,YAAYC,EAA8B,CAAC,EAAG,CACpD,IAAMC,EAAYD,EAAQ,WAAa,SACjCE,EAAUF,EAAQ,cAAgB,KAAK,iBAAiB,EAE1DA,EAAQ,QAAU,QACpBG,EAAO,UAAU,CAAE,QAASH,EAAQ,KAAM,CAAC,EAG7C,KAAK,OAASG,EAAO,UAAU,OAAO,EAEtC,KAAK,kBAAoB,IAAIC,EAAuBH,EAAW,KAAK,MAAM,EAC1E,KAAK,eAAiB,IAAII,EAAoB,KAAK,MAAM,EAGzD,KAAK,kBAAkB,YAAaC,GAAS,CAC3C,KAAK,OAAO,KAAK,wCAAyC,CAAE,KAAAA,CAAK,CAAC,EAClE,KAAK,mBAAmB,CAC1B,CAAC,EAED,KAAK,OAAO,KAAK,8BAA+B,CAAE,QAAAN,EAAS,QAAAE,CAAQ,CAAC,EACpE,KAAK,qBAAqB,CAC5B,CAEA,OAAc,YAAYF,EAA8B,CAAC,EAAgB,CACvE,MACE,CAACD,EAAY,UACbA,EAAY,SAAS,kBAAkB,aAAa,IAClDC,EAAQ,UAEVD,EAAY,SAAW,IAAIA,EAAYC,CAAO,EACrCA,EAAQ,QAAU,QAC3BG,EAAO,UAAU,CAAE,QAASH,EAAQ,KAAM,CAAC,EAEtCD,EAAY,QACrB,CAEA,MAAc,sBAAsC,CAClD,MAAM,KAAK,kBAAkB,qBAAqB,EAClD,KAAK,mBAAmB,CAC1B,CAMQ,oBAA2B,CACjC,IAAMQ,EAAO,KAAK,kBAAkB,QAAQ,EACxCA,GACF,KAAK,OAAO,MAAM,2BAA2B,EAC7CA,EAAK,UAAU,YAAaC,GAC1B,KAAK,eAAe,cAAcD,EAAMC,CAAO,CACjD,EACAD,EAAK,aAAa,YAAaE,GAC7B,KAAK,kBAAkB,iBAAiBA,CAAC,CAC3C,GAEA,KAAK,OAAO,KAAK,gDAAgD,CAErE,CAEO,UAAUC,EAAuB,CACtC,KAAK,eAAe,UAAUA,CAAM,EACpC,IAAMH,EAAO,KAAK,kBAAkB,QAAQ,EAC5CA,GAAA,MAAAA,EAAM,YAAY,CAAE,OAAQ,6BAA8B,EAC5D,CAEO,GAAGG,EAAuB,CAC/B,KAAK,eAAe,GAAGA,CAAM,EAC7B,IAAMH,EAAO,KAAK,kBAAkB,QAAQ,EAC5CA,GAAA,MAAAA,EAAM,YAAY,CAAE,OAAQ,6BAA8B,EAC5D,CAEO,KAAKC,EAAuBG,EAA0B,CAC3D,IAAMJ,EAAO,KAAK,kBAAkB,QAAQ,EAG5C,GAFA,KAAK,OAAO,MAAM,kBAAmB,CAAE,QAAAC,EAAS,OAAAG,EAAQ,KAAAJ,CAAK,CAAC,EAE1DA,EACF,GAAI,CACF,KAAK,eAAe,KAAKA,EAAMC,EAASG,CAAM,CAChD,OAASC,EAAO,CACd,KAAK,OAAO,MAAM,6CAA8C,CAC9D,MAAAA,CACF,CAAC,EACD,KAAK,kBAAkB,aAAaJ,EAASG,CAAM,CACrD,MAEA,KAAK,OAAO,MAAM,kCAAmC,CAAE,QAAAH,EAAS,OAAAG,CAAO,CAAC,EACxE,KAAK,kBAAkB,aAAaH,EAASG,CAAM,CAEvD,CAEQ,kBAAkC,CACxC,OAAK,OAAO,SAAS,SAAS,SAAS,WAAW,6BAIpD,CAEO,cAAiC,CACtC,OAAO,KAAK,kBAAkB,aAAa,GAAK,IAClD,CAMO,aAAaE,EAA2C,CAC7D,OAAO,KAAK,kBAAkB,aAAaA,CAAQ,CACrD,CAMO,YAAYA,EAA0C,CAC3D,OAAO,KAAK,kBAAkB,YAAYA,CAAQ,CACpD,CACF,EA3Had,EACI,SAA+B,KADzC,IAAMe,EAANf,EA6HA,SAASgB,EAAQf,EAAwC,CAC9D,IAAMgB,EAAiBF,EAAY,YAAYd,CAAO,EACtD,MAAO,CACL,KAAM,QACN,KAAMgB,EAAe,KAAK,KAAKA,CAAc,EAC7C,UAAWA,EAAe,UAAU,KAAKA,CAAc,EACvD,GAAIA,EAAe,GAAG,KAAKA,CAAc,EACzC,aAAcA,EAAe,aAAa,KAAKA,CAAc,EAC7D,aAAcA,EAAe,aAAa,KAAKA,CAAc,EAC7D,YAAaA,EAAe,YAAY,KAAKA,CAAc,CAC7D,CACF,CCxKA,IAAAC,EAAkE,iBCS3D,IAAMC,EAAN,cAAyB,KAAM,CACpC,YAAYC,EAAiB,CAC3B,MAAMA,CAAO,EACb,KAAK,KAAO,aAER,MAAM,mBACR,MAAM,kBAAkB,KAAM,KAAK,WAAW,CAElD,CACF,EAeO,IAAMC,EAAN,cAA6BC,CAAW,CAC7C,YACkBC,EACAC,EAChB,CACA,MACE,GAAGA,IAAW,QAAU,QAAU,OAAO,KAAKD,CAAS,cACrDC,IAAW,QAAU,YAAc,cACrC,sBACF,EAPgB,eAAAD,EACA,YAAAC,EAOhB,KAAK,KAAO,gBACd,CACF,ECjBO,IAAMC,EAAN,KAAuE,CAoB5E,YAAYC,EAAkCC,EAAwB,CAAC,EAAG,CAd1E,KAAQ,WAA+B,KACvC,KAAQ,aAAe,GACvB,KAAQ,gBAAkB,GAE1B,KAAiB,YAA6C,IAAI,IAClE,KAAiB,eAA8D,IAAI,IACnF,KAAiB,oBAAuC,IAAI,IAC5D,KAAiB,mBAAkE,IAAI,IAQrF,KAAK,OAASD,EACd,KAAK,QAAUC,EAGf,KAAK,OAAS,KAAK,kBAAkB,EAGrC,KAAK,aAAe,IAAI,QAASC,GAAY,CAC3C,KAAK,aAAeA,CACtB,CAAC,EAGD,KAAK,OAASC,EAAc,CAC1B,UAAWH,EAAO,KAClB,MAAOC,EAAQ,OAAS,EAC1B,CAAC,EAGD,KAAK,qBAAqB,EAG1B,KAAK,aAAe,KAAK,mBAAmB,CAC9C,CAMA,OAAwC,CACtC,YAAK,sBAAsB,EACpB,KAAK,YACd,CAEA,QAAQG,EAA8D,CACpE,YAAK,sBAAsB,EAGvB,KAAK,cACP,WAAW,IAAMA,EAAS,KAAK,MAAM,EAAG,CAAC,EAG3C,KAAK,eAAe,IAAIA,CAAQ,EACzB,IAAM,CACX,KAAK,eAAe,OAAOA,CAAQ,CACrC,CACF,CAMA,UAAkC,CAChC,YAAK,sBAAsB,EACpB,CAAE,GAAG,KAAK,MAAO,CAC1B,CAEA,IAAI,OAA+B,CACjC,OAAO,KAAK,SAAS,CACvB,CAEA,MAAM,SAASC,EAAsD,CACnE,KAAK,sBAAsB,EAG3B,KAAK,OAAS,CAAE,GAAG,KAAK,OAAQ,GAAGA,CAAM,EAGzC,KAAK,OAAO,KAAK,CACf,OAAQ,WACR,QAAS,CAAE,MAAAA,CAAM,CACnB,CAAC,CACH,CAMA,UACEC,EACAC,EACY,CACZ,KAAK,sBAAsB,EAE3B,IAAIC,EACAJ,EAEA,OAAOE,GAAmB,WAC5BF,EAAWE,GAEXE,EAAOF,EACPF,EAAWG,GAGb,IAAME,EAAuC,CAAE,KAAAD,EAAM,SAAAJ,CAAS,EAC9D,YAAK,YAAY,IAAIK,CAAU,EAExB,IAAM,CACX,KAAK,YAAY,OAAOA,CAAU,CACpC,CACF,CAMA,IAAI,SAAmC,CACrC,YAAK,sBAAsB,EACpB,KAAK,YACd,CAMA,SAAsC,CACpC,OAAK,KAAK,WAEH,CACL,GAAI,KAAK,WAAW,GACpB,MAAO,KAAK,WAAW,SAAS,MAChC,QAAS,KAAK,WAAW,SAAS,QAClC,QAAS,KAAK,WAAW,SAAS,OACpC,EAP6B,IAQ/B,CAMA,aAAaL,EAAkC,CAC7C,YAAK,sBAAsB,EAC3B,KAAK,oBAAoB,IAAIA,CAAQ,EAC9B,IAAM,CACX,KAAK,oBAAoB,OAAOA,CAAQ,CAC1C,CACF,CAEA,YAAYA,EAA8D,CACxE,YAAK,sBAAsB,EAC3B,KAAK,mBAAmB,IAAIA,CAAQ,EAC7B,IAAM,CACX,KAAK,mBAAmB,OAAOA,CAAQ,CACzC,CACF,CAEA,YAAmB,CACb,KAAK,kBAET,KAAK,gBAAkB,GACvB,KAAK,aAAe,GAGpB,KAAK,YAAY,MAAM,EACvB,KAAK,eAAe,MAAM,EAC1B,KAAK,oBAAoB,MAAM,EAC/B,KAAK,mBAAmB,MAAM,EAGhC,CAMQ,sBAA6B,CAEnC,KAAK,OAAO,GAAG,CACb,aAAeM,GAAY,CACzB,GAAM,CAAE,MAAAL,EAAO,KAAAM,CAAK,EAAID,EAAQ,QAEhC,KAAK,OAASL,EACd,KAAK,WAAaM,EAClB,KAAK,aAAe,GAGpB,KAAK,aAAa,KAAK,MAAM,EAG7B,KAAK,eAAe,QAASC,GAAO,CAClC,GAAI,CACFA,EAAG,KAAK,MAAM,CAChB,OAASC,EAAG,CACV,QAAQ,MAAM,2CAA4CA,CAAC,CAC7D,CACF,CAAC,EAGD,KAAK,kBAAkBR,CAAK,CAC9B,EAEA,YAAcK,GAAY,CACxB,GAAM,CAAE,MAAOI,CAAQ,EAAIJ,EAAQ,QACnC,KAAK,OAAS,CAAE,GAAG,KAAK,OAAQ,GAAGI,CAAQ,EAC3C,KAAK,kBAAkBA,CAAO,CAChC,EAEA,UAAYJ,GAAY,CAGxB,CACF,CAAC,EAGD,KAAK,OAAO,aAAa,IAAM,CAC7B,KAAK,aAAe,GACpB,KAAK,oBAAoB,QAASE,GAAO,CACvC,GAAI,CACFA,EAAG,CACL,OAASC,EAAG,CACV,QAAQ,MAAM,gDAAiDA,CAAC,CAClE,CACF,CAAC,CACH,CAAC,EAED,KAAK,OAAO,YAAaF,GAAoB,CAC3C,KAAK,WAAaA,EAClB,KAAK,aAAe,GACpB,KAAK,mBAAmB,QAASC,GAAO,CACtC,GAAI,CACFA,EAAG,KAAK,MAAM,CAChB,OAASC,EAAG,CACV,QAAQ,MAAM,+CAAgDA,CAAC,CACjE,CACF,CAAC,CACH,CAAC,CACH,CAEQ,kBAAkBC,EAAsC,CAC9D,KAAK,YAAY,QAASL,GAAe,CAEvC,GAAI,EAAAA,EAAW,MAIT,CAHsBA,EAAW,KAAK,KACvCM,GAAQA,KAAOD,CAClB,GAIF,GAAI,CACFL,EAAW,SAASK,EAAS,KAAK,MAAM,CAC1C,OAASD,EAAG,CACV,QAAQ,MAAM,8CAA+CA,CAAC,CAChE,CACF,CAAC,CACH,CAMQ,oBAA8C,CACpD,IAAMG,EAAe,IAAI,IACrBC,EAAS,EAGb,YAAK,OAAO,GAAG,CACb,UAAYP,GAAY,CACtB,GAAM,CAAE,OAAQQ,EAAI,OAAAC,EAAQ,MAAAC,EAAO,QAAAC,CAAQ,EAAIX,EAAQ,QACjDY,EAAUN,EAAa,IAAIE,CAAE,EAC/BI,IACFN,EAAa,OAAOE,CAAE,EAClBG,EACFC,EAAQ,QAAQH,CAAM,EAEtBG,EAAQ,OAAO,IAAI,MAAMF,CAAK,CAAC,EAGrC,CACF,CAAC,EAEM,IAAI,MAAM,CAAC,EAA8B,CAC9C,IAAK,CAACG,EAASC,IACN,SAAUC,IAAoB,CACnC,IAAMP,EAAK,GAAGD,GAAQ,GAEtB,OAAO,IAAI,QAAQ,CAACf,EAASwB,IAAW,CACtCV,EAAa,IAAIE,EAAI,CAAE,QAAAhB,EAAS,OAAAwB,CAAO,CAAC,EAExC,KAAK,OAAO,KAAK,CACf,OAAQ,MACR,QAAS,CACP,OAAQR,EACR,WAAAM,EACA,KAAAC,CACF,CACF,CAAC,CACH,CAAC,CACH,CAEJ,CAAC,CACH,CAMQ,mBAA2C,CACjD,IAAMpB,EAAiC,CAAC,EAExC,OAAW,CAACU,EAAKY,CAAI,IAAK,OAAO,QAAQ,KAAK,MAAM,EAC9CZ,IAAQ,QAAUA,IAAQ,WAAaA,IAAQ,WAC/C,OAAOY,GAAS,UAAYA,IAAS,MAAQ,YAAaA,IAC5DtB,EAAMU,CAAG,EAAIY,EAAK,SAItB,OAAOtB,CACT,CAEQ,uBAA8B,CACpC,GAAI,KAAK,gBACP,MAAM,IAAIuB,EAAe,KAAK,OAAO,KAAM,OAAO,CAEtD,CACF,EAyCO,SAASC,EACd7B,EACAC,EACmB,CACnB,OAAO,IAAIF,EAAMC,EAAQC,CAAO,CAClC,Cb/IW,IAAA6B,GAAA,6BA1MJ,SAASC,GACdC,EACAC,EAAmC,CAAC,EACf,CAErB,IAAIC,EAAwC,KAGtCC,KAAe,iBAAwC,IAAI,EAKjE,SAASC,GAA8B,CACrC,OAAKF,IACHA,EAAcG,EAAaL,EAAQ,CAAE,MAAOC,EAAQ,KAAM,CAAC,GAEtDC,CACT,CAKA,SAASI,GAAqC,CAC5C,IAAMC,KAAe,cAAWJ,CAAY,EACtC,CAACK,EAAOC,CAAQ,KAAI,YACxBF,GAAgBL,CAClB,EAEA,sBAAU,IAAM,CACTM,GACHC,EAASL,EAAS,CAAC,CAEvB,EAAG,CAACI,CAAK,CAAC,EAEHD,GAAgBC,CACzB,CAKA,SAASE,GAAyB,CAChC,IAAMF,EAAQF,EAAS,EACjB,CAACK,EAASC,CAAU,KAAI,YAAS,EAAK,EAE5C,sBAAU,IAAM,CACd,GAAI,CAACJ,EAAO,OAGCA,EAAM,QAAQ,GAEzBI,EAAW,EAAI,EAIjB,IAAMC,EAAaL,EAAM,QAAQ,IAAM,CACrCI,EAAW,EAAI,CACjB,CAAC,EAGKE,EAAkBN,EAAM,aAAa,IAAM,CAC/CI,EAAW,EAAK,CAClB,CAAC,EAEKG,EAAiBP,EAAM,YAAY,IAAM,CAC7CI,EAAW,EAAI,CACjB,CAAC,EAED,MAAO,IAAM,CACXC,EAAW,EACXC,EAAgB,EAChBC,EAAe,CACjB,CACF,EAAG,CAACP,CAAK,CAAC,EAEHG,CACT,CAWA,SAASK,EACPC,EAC4C,CAC5C,IAAMT,EAAQF,EAAS,EACjBY,EAAa,OAAOD,GAAkB,WAGtCE,EAAWD,EACZD,EACAG,GAAiCA,EAAMH,CAAkB,EAGxD,CAACI,EAAeC,CAAgB,KAAI,YAAoB,IAAM,CAClE,GAAI,CAACd,EAAO,CAEV,IAAMe,EAAeC,GAAkBxB,CAAM,EAC7C,OAAOmB,EAASI,CAAY,CAC9B,CACA,OAAOJ,EAASX,EAAM,SAAS,CAAC,CAClC,CAAC,EAGKiB,KAAkB,UAAOJ,CAAa,EA0B5C,MAxBA,aAAU,IAAM,CACd,GAAI,CAACb,EAAO,OAGZ,IAAMkB,EAAelB,EAAM,SAAS,EAC9BmB,EAAcR,EAASO,CAAY,EACzC,OAAKE,EAAaD,EAAaF,EAAgB,OAAO,IACpDA,EAAgB,QAAUE,EAC1BL,EAAiBK,CAAW,GAIVnB,EAAM,UAAU,CAACqB,EAAST,IAAU,CACtD,IAAMO,EAAcR,EAASC,CAAK,EAC7BQ,EAAaD,EAAaF,EAAgB,OAAO,IACpDA,EAAgB,QAAUE,EAC1BL,EAAiBK,CAAW,EAEhC,CAAC,CAGH,EAAG,CAACnB,EAAOW,CAAQ,CAAC,EAGhB,CAACD,EAAY,CACf,IAAMY,EAAMb,EACNc,KAAW,eACf,MAAOC,GAAoC,CACrCxB,GACF,MAAMA,EAAM,SAAS,CAAE,CAACsB,CAAG,EAAGE,CAAM,CAAmC,CAE3E,EACA,CAACxB,EAAOsB,CAAG,CACb,EAEA,MAAO,CAACT,EAA2CU,CAAQ,CAC7D,CAEA,OAAOV,CACT,CAQA,SAASY,GAA2C,CAClD,IAAMzB,EAAQF,EAAS,EAGjB4B,KAAW,UAAiC,IAAI,EACtDA,EAAS,QAAU1B,EAGnB,IAAM2B,KAAkB,UAAiE,CAAC,CAAC,EAGrFC,KAAa,UAAuC,IAAI,EAE9D,OAAKA,EAAW,UACdA,EAAW,QAAU,IAAI,MAAM,CAAC,EAA8B,CAC5D,IAAK,CAACC,EAASC,KAERH,EAAgB,QAAQG,CAAU,IACrCH,EAAgB,QAAQG,CAAU,EAAI,SAAUC,IAAoB,CAClE,IAAMC,EAAeN,EAAS,QAC9B,GAAI,CAACM,EACH,MAAM,IAAI,MACR,uBAAuBF,CAAU,6BACnC,EAEF,OAAQE,EAAa,QAAgBF,CAAU,EAAE,GAAGC,CAAI,CAC1D,GAEKJ,EAAgB,QAAQG,CAAU,EAE7C,CAAC,GAGIF,EAAW,OACpB,CAaA,MAAO,CACL,cAAApB,EACA,gBAAAiB,EACA,cAAAvB,EACA,SAAAJ,EACA,cAb4E,CAAC,CAC7E,MAAAE,EACA,SAAAiC,CACF,IAAM,CACJ,IAAMT,EAAQxB,GAASJ,EAAS,EAChC,SAAO,QAACD,EAAa,SAAb,CAAsB,MAAO6B,EAAQ,SAAAS,EAAS,CACxD,CAQA,CACF,CASA,SAASjB,GACPxB,EACuB,CACvB,IAAMoB,EAAiC,CAAC,EAExC,OAAW,CAACU,EAAKY,CAAI,IAAK,OAAO,QAAQ1C,CAAM,EACzC8B,IAAQ,QAAUA,IAAQ,WAAaA,IAAQ,WAC/C,OAAOY,GAAS,UAAYA,IAAS,MAAQ,YAAaA,IAC5DtB,EAAMU,CAAG,EAAIY,EAAK,SAItB,OAAOtB,CACT,CAKA,SAASQ,EAAae,EAAYC,EAAqB,CACrD,GAAID,IAAMC,EAAG,MAAO,GAEpB,GADI,OAAOD,GAAM,UAAY,OAAOC,GAAM,UACtCD,IAAM,MAAQC,IAAM,KAAM,MAAO,GAErC,IAAMC,EAAQ,OAAO,KAAKF,CAAC,EACrBG,EAAQ,OAAO,KAAKF,CAAC,EAE3B,GAAIC,EAAM,SAAWC,EAAM,OAAQ,MAAO,GAE1C,QAAWhB,KAAOe,EAChB,GAAKF,EAAUb,CAAG,IAAOc,EAAUd,CAAG,EAAG,MAAO,GAGlD,MAAO,EACT",
6
+ "names": ["react_exports", "__export", "createCrannHooks", "__toCommonJS", "import_react", "import_webextension_polyfill", "PorterContext", "PorterError", "type", "message", "details", "isServiceWorker", "_Logger", "context", "_a", "_b", "_c", "options", "message", "args", "Logger", "import_webextension_polyfill", "import_uuid", "AgentManager", "logger", "port", "context", "_a", "_b", "connectionSource", "determinedContext", "tabId", "frameId", "tabAgentsInfo", "info", "agentId", "uuidv4", "agentInfo", "message", "agent", "location", "infoEntry", "key", "value", "hasContext", "hasTabId", "hasFrameId", "id", "p", "allAgents", "allAgentsInfo", "event", "handler", "handlers", "args", "_c", "_d", "_e", "_f", "_g", "sender", "manifest", "browser", "sidePanel", "optionsPage", "popupPage", "devtoolsPage", "newTabOverride", "bookmarksOverride", "historyOverride", "pageMatchers", "filename", "pageType", "pageMatcher", "ConnectionManager", "agentOperations", "namespace", "logger", "port", "PorterError", "e", "error", "message", "connectionId", "agentId", "agent", "porterError", "MessageHandler", "agentOperations", "logger", "message", "agent", "_a", "agentInfo", "target", "resolve", "reject", "timeoutId", "error", "isBrowserLocation", "isPorterContext", "errorMessage", "tabId", "agents", "PorterError", "location", "context", "port", "agentId", "config", "listener", "messageListener", "event", "handler", "info", "arg", "messageEvent", "handlerCount", "PorterContext", "_PorterSource", "namespace", "options", "Logger", "AgentManager", "MessageHandler", "ConnectionManager", "isServiceWorker", "PorterError", "message", "metadata", "agent", "browser", "target", "config", "listener", "key", "_a", "agentId", "location", "PorterSource", "import_webextension_polyfill", "MessageQueue", "logger", "message", "target", "oldCount", "messages", "now", "item", "AgentConnectionManager", "namespace", "logger", "MessageQueue", "callback", "error", "info", "_a", "portName", "browser", "handshakePromise", "resolve", "reject", "timeout", "PorterError", "onMessage", "message", "_b", "messages", "target", "messageToSend", "port", "AgentMessageHandler", "logger", "port", "message", "config", "action", "handler", "item", "actionHandlers", "target", "error", "PorterError", "_PorterAgent", "options", "namespace", "context", "Logger", "AgentConnectionManager", "AgentMessageHandler", "info", "port", "message", "p", "config", "target", "error", "callback", "PorterAgent", "connect", "porterInstance", "import_react", "CrannError", "message", "LifecycleError", "CrannError", "storeName", "entity", "Agent", "config", "options", "resolve", "connect", "callback", "state", "callbackOrKeys", "maybeCallback", "keys", "subscriber", "message", "info", "cb", "e", "changes", "key", "pendingCalls", "callId", "id", "result", "error", "success", "pending", "_target", "actionName", "args", "reject", "item", "LifecycleError", "connectStore", "import_jsx_runtime", "createCrannHooks", "config", "options", "moduleAgent", "AgentContext", "getAgent", "connectStore", "useAgent", "contextAgent", "agent", "setAgent", "useCrannReady", "isReady", "setIsReady", "unsubReady", "unsubDisconnect", "unsubReconnect", "useCrannState", "selectorOrKey", "isFunction", "selector", "state", "selectedValue", "setSelectedValue", "defaultState", "buildDefaultState", "prevSelectedRef", "currentState", "newSelected", "shallowEqual", "changes", "key", "setValue", "value", "useCrannActions", "agentRef", "actionsCacheRef", "actionsRef", "_target", "actionName", "args", "currentAgent", "children", "item", "a", "b", "keysA", "keysB"]
7
7
  }