@gigabuddy/chat-sdk 0.1.33 → 0.1.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../libs/chat-sdk/src/lib/Conversation.ts", "../../../libs/chat-sdk/src/lib/SSEClient.ts", "../../../libs/chat-sdk/src/lib/GigabuddyChat.ts", "../../../libs/chat-sdk/src/lib/types.ts"],
4
- "sourcesContent": ["/**\n * Conversation\n *\n * Represents an active conversation. Events are received via the\n * parent GigabuddyChat's user stream \u2014 filtered by conversationId.\n * connect()/disconnect() are no-ops (kept for backward compat).\n */\n\nimport type {\n ChatEventBus,\n ChannelEvent,\n ConversationEventMap,\n GigabuddyChatConfig,\n Message,\n MessageContent,\n ThreadSummary,\n} from './types.js';\n\nexport class Conversation {\n readonly conversationId: string;\n private config: GigabuddyChatConfig;\n private eventBus: ChatEventBus | null;\n private unsubscribes: (() => void)[] = [];\n\n constructor(conversationId: string, config: GigabuddyChatConfig, eventBus?: ChatEventBus) {\n this.conversationId = conversationId;\n this.config = config;\n this.eventBus = eventBus ?? null;\n }\n\n private async buildHeaders(): Promise<Record<string, string>> {\n const token = await this.config.getToken();\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n };\n if (this.config.userId) {\n headers['X-Chat-User-Id'] = this.config.userId;\n }\n return headers;\n }\n\n // ===========================================================================\n // Messages\n // ===========================================================================\n\n async sendMessage(content: string | MessageContent): Promise<Message> {\n const headers = await this.buildHeaders();\n\n const body: Record<string, unknown> = {\n conversationId: this.conversationId,\n };\n\n if (typeof content === 'string') {\n body['text'] = content;\n } else {\n body['content'] = content;\n }\n\n const response = await fetch(`${this.config.apiUrl}/chat/send-message`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const responseBody = await response.text();\n throw new Error(`Failed to send message: ${response.status} ${responseBody}`);\n }\n\n const data = (await response.json()) as { message?: Message };\n return data.message!;\n }\n\n async replyTo(messageId: string, content: string | MessageContent): Promise<void> {\n const headers = await this.buildHeaders();\n\n const body: Record<string, unknown> = {\n conversationId: this.conversationId,\n parentMessageId: messageId,\n };\n\n if (typeof content === 'string') {\n body['text'] = content;\n } else {\n body['content'] = content;\n }\n\n const response = await fetch(`${this.config.apiUrl}/chat/reply-to-message`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const responseBody = await response.text();\n throw new Error(`Failed to reply to message: ${response.status} ${responseBody}`);\n }\n }\n\n async editMessage(messageId: string, content: string | MessageContent): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/edit-message`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n messageId,\n content,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to edit message: ${response.status} ${body}`);\n }\n }\n\n async deleteMessage(messageId: string): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/delete-message`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n messageId,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to delete message: ${response.status} ${body}`);\n }\n }\n\n async sendEphemeralMessage(targetUserId: string, content: string | MessageContent): Promise<void> {\n const headers = await this.buildHeaders();\n\n const body: Record<string, unknown> = {\n conversationId: this.conversationId,\n targetUserId,\n };\n\n if (typeof content === 'string') {\n body['text'] = content;\n } else {\n body['content'] = content;\n }\n\n const response = await fetch(`${this.config.apiUrl}/chat/send-ephemeral-message`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const responseBody = await response.text();\n throw new Error(`Failed to send ephemeral message: ${response.status} ${responseBody}`);\n }\n }\n\n // ===========================================================================\n // Reactions\n // ===========================================================================\n\n async addReaction(messageId: string, emoji: string): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/add-reaction`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n messageId,\n emoji,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to add reaction: ${response.status} ${body}`);\n }\n }\n\n async removeReaction(messageId: string, emoji: string): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/remove-reaction`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n messageId,\n emoji,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to remove reaction: ${response.status} ${body}`);\n }\n }\n\n // ===========================================================================\n // Read state & metadata\n // ===========================================================================\n\n async markRead(messageId?: string): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/mark-read`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n ...(messageId ? { messageId } : {}),\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to mark read: ${response.status} ${body}`);\n }\n }\n\n async markThreadRead(parentMessageId: string, messageId?: string): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/mark-thread-read`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n parentMessageId,\n ...(messageId ? { messageId } : {}),\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to mark thread read: ${response.status} ${body}`);\n }\n }\n\n async getThreadSummaries(opts?: { limit?: number; before?: string }): Promise<ThreadSummary[]> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/get-thread-summaries`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n ...(opts?.limit ? { limit: opts.limit } : {}),\n ...(opts?.before ? { before: opts.before } : {}),\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get thread summaries: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { threads?: ThreadSummary[] };\n return data.threads ?? [];\n }\n\n async sendTyping(opts?: { parentMessageId?: string }): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/send-typing`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n ...(opts?.parentMessageId ? { parentMessageId: opts.parentMessageId } : {}),\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to send typing: ${response.status} ${body}`);\n }\n }\n\n async updateTopic(topic: string): Promise<void> {\n return this.updateChannel({ topic });\n }\n\n async updateChannel(fields: {\n name?: string;\n description?: string;\n topic?: string;\n pictureUrl?: string;\n }): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/update-channel`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n ...fields,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to update channel: ${response.status} ${body}`);\n }\n }\n\n // ===========================================================================\n // Metadata\n // ===========================================================================\n\n /**\n * Set app-specific metadata on this conversation.\n * Useful for providing buddy context, game state, or any application data.\n *\n * @param metadata - Key-value pairs to set\n * @param opts.merge - If true (default), shallow-merges into existing metadata. If false, replaces entirely.\n */\n async setMetadata(metadata: Record<string, unknown>, opts?: { merge?: boolean }): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/set-metadata`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n metadata,\n merge: opts?.merge ?? true,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to set metadata: ${response.status} ${body}`);\n }\n }\n\n // ===========================================================================\n // Participants\n // ===========================================================================\n\n async addParticipant(userId: string): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/invite-to-channel`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n userId,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to add participant: ${response.status} ${body}`);\n }\n }\n\n async removeParticipant(userId: string): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/remove-participant`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n userId,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to remove participant: ${response.status} ${body}`);\n }\n }\n\n async inviteBuddy(buddyId: string, opts?: { buddyProjectId?: string }): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/invite-buddy`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n buddyId,\n ...(opts?.buddyProjectId ? { buddyProjectId: opts.buddyProjectId } : {}),\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to invite buddy: ${response.status} ${body}`);\n }\n }\n\n async removeBuddy(): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/remove-buddy`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to remove buddy: ${response.status} ${body}`);\n }\n }\n\n async leave(): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/leave-conversation`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to leave conversation: ${response.status} ${body}`);\n }\n }\n\n // ===========================================================================\n // Message history\n // ===========================================================================\n\n async getMessages(opts?: { limit?: number; before?: string; after?: string }): Promise<Message[]> {\n const headers = await this.buildHeaders();\n const response = await fetch(`${this.config.apiUrl}/chat/get-messages`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n ...(opts?.limit ? { limit: opts.limit } : {}),\n ...(opts?.before ? { before: opts.before } : {}),\n ...(opts?.after ? { after: opts.after } : {}),\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get messages: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { messages?: Message[] };\n return data.messages ?? [];\n }\n\n async getThreadReplies(parentMessageId: string, opts?: { limit?: number; before?: string }): Promise<Message[]> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/get-thread-replies`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n parentMessageId,\n ...(opts?.limit ? { limit: opts.limit } : {}),\n ...(opts?.before ? { before: opts.before } : {}),\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get thread replies: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { messages?: Message[] };\n return data.messages ?? [];\n }\n\n // ===========================================================================\n // Event listeners \u2014 route through parent user stream\n // ===========================================================================\n\n /**\n * Register an event handler filtered to this conversation.\n * Events are received from the parent GigabuddyChat's user stream.\n * Returns an unsubscribe function.\n */\n on<T extends keyof ConversationEventMap>(event: T, handler: ConversationEventMap[T]): () => void {\n if (this.eventBus) {\n const unsub = this.eventBus.addConversationListener(\n this.conversationId,\n event,\n handler as (...args: unknown[]) => void,\n );\n this.unsubscribes.push(unsub);\n return unsub;\n }\n // No event bus \u2014 listener is a no-op\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n return () => {};\n }\n\n // ===========================================================================\n // SSE lifecycle \u2014 no-ops (stream managed by GigabuddyChat)\n // ===========================================================================\n\n /** @deprecated No-op \u2014 SSE is managed by GigabuddyChat.connect() */\n async connect(): Promise<void> {\n // No-op: user stream is managed at the GigabuddyChat level\n }\n\n /** @deprecated No-op \u2014 SSE is managed by GigabuddyChat.disconnect() */\n disconnect(): void {\n // Clean up any registered listeners\n for (const unsub of this.unsubscribes) {\n unsub();\n }\n this.unsubscribes = [];\n }\n}\n", "/**\n * SSE Client\n *\n * Uses fetch() + ReadableStream instead of native EventSource\n * to support Authorization headers. Handles reconnection with\n * exponential backoff.\n */\n\nimport type { ChannelEvent, ChannelEventType } from './types.js';\n\nexport type SSEEventHandler = (event: ChannelEvent) => void;\nexport type SSEConnectionHandler = () => void;\n\ninterface SSEClientConfig {\n url: string;\n getToken: () => Promise<string> | string;\n userId?: string;\n onEvent: SSEEventHandler;\n onConnected: SSEConnectionHandler;\n onDisconnected: SSEConnectionHandler;\n}\n\nconst MAX_RECONNECT_ATTEMPTS = 10;\nconst INITIAL_BACKOFF_MS = 1_000;\nconst MAX_BACKOFF_MS = 30_000;\n\nexport class SSEClient {\n private config: SSEClientConfig;\n private abortController: AbortController | null = null;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private reconnectAttempt = 0;\n private stopped = false;\n\n constructor(config: SSEClientConfig) {\n this.config = config;\n }\n\n async connect(): Promise<void> {\n this.stopped = false;\n this.reconnectAttempt = 0;\n await this.startStream();\n }\n\n disconnect(): void {\n this.stopped = true;\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n if (this.abortController) {\n this.abortController.abort();\n this.abortController = null;\n }\n }\n\n async updateToken(newToken: string): Promise<void> {\n // Store the new token getter for reconnections\n const originalGetToken = this.config.getToken;\n this.config.getToken = () => newToken;\n\n // Reconnect with new token\n if (this.abortController) {\n this.abortController.abort();\n this.abortController = null;\n this.reconnectAttempt = 0;\n await this.startStream();\n }\n\n // Restore the dynamic getter for future reconnections\n this.config.getToken = originalGetToken;\n }\n\n private async startStream(): Promise<void> {\n if (this.stopped) return;\n\n try {\n const token = await this.config.getToken();\n this.abortController = new AbortController();\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${token}`,\n Accept: 'text/event-stream',\n };\n if (this.config.userId) {\n headers['X-Chat-User-Id'] = this.config.userId;\n }\n\n const response = await fetch(this.config.url, {\n method: 'GET',\n headers,\n signal: this.abortController.signal,\n });\n\n if (!response.ok) {\n throw new Error(`SSE connection failed: ${response.status} ${response.statusText}`);\n }\n\n if (!response.body) {\n throw new Error('SSE response has no body');\n }\n\n this.reconnectAttempt = 0;\n // Read stream in background \u2014 don't await (readStream blocks until stream ends)\n this.readStream(response.body);\n } catch (error) {\n if (this.stopped) return;\n if (error instanceof DOMException && error.name === 'AbortError') return;\n\n this.config.onDisconnected();\n this.scheduleReconnect();\n }\n }\n\n private async readStream(body: ReadableStream<Uint8Array>): Promise<void> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // Process complete SSE messages (terminated by \\n\\n)\n const messages = buffer.split('\\n\\n');\n buffer = messages.pop() ?? '';\n\n for (const message of messages) {\n this.parseSSEMessage(message);\n }\n }\n } catch (error) {\n if (this.stopped) return;\n if (error instanceof DOMException && error.name === 'AbortError') return;\n } finally {\n reader.releaseLock();\n }\n\n // Stream ended \u2014 reconnect if not explicitly stopped\n if (!this.stopped) {\n this.config.onDisconnected();\n this.scheduleReconnect();\n }\n }\n\n private parseSSEMessage(message: string): void {\n // Skip heartbeat comments\n if (message.trim().startsWith(':')) return;\n\n let eventType = 'message';\n let data = '';\n\n for (const line of message.split('\\n')) {\n if (line.startsWith('event: ')) {\n eventType = line.slice(7).trim();\n } else if (line.startsWith('data: ')) {\n data = line.slice(6);\n } else if (line.startsWith('data:')) {\n data = line.slice(5);\n }\n }\n\n if (eventType === 'connected') {\n this.config.onConnected();\n return;\n }\n\n if (!data) return;\n\n try {\n const parsed = JSON.parse(data) as ChannelEvent;\n // Ensure type field matches event type\n if (!parsed.type) {\n parsed.type = eventType as ChannelEventType;\n }\n this.config.onEvent(parsed);\n } catch {\n // Ignore malformed events\n }\n }\n\n private scheduleReconnect(): void {\n if (this.stopped) return;\n if (this.reconnectAttempt >= MAX_RECONNECT_ATTEMPTS) return;\n\n const backoff = Math.min(INITIAL_BACKOFF_MS * Math.pow(2, this.reconnectAttempt), MAX_BACKOFF_MS);\n this.reconnectAttempt++;\n\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n this.startStream();\n }, backoff);\n }\n}\n", "/**\n * GigabuddyChat\n *\n * Main entry point for the Chat SDK. Manages a single user-level SSE\n * connection and routes events to conversation-level listeners.\n */\n\nimport { Conversation } from './Conversation.js';\nimport { SSEClient } from './SSEClient.js';\nimport type {\n BuddyInfo,\n ChatEventBus,\n ChatUser,\n ChannelEvent,\n ConversationInfo,\n ConversationType,\n ConversationVisibility,\n CreateGadgetInput,\n CreateStatefulGadgetInput,\n FileUploadResult,\n GadgetDetail,\n GadgetInfo,\n GadgetVersionDetail,\n GadgetVersionInfo,\n GigabuddyChatConfig,\n GlobalEventMap,\n StatefulGadgetResult,\n StatefulGadgetSpec,\n UpdateGadgetInput,\n UserProfile,\n UserStat,\n} from './types.js';\n\nexport class GigabuddyChat implements ChatEventBus {\n private config: GigabuddyChatConfig;\n private conversations = new Map<string, Conversation>();\n\n // User stream SSE\n private sseClient: SSEClient | null = null;\n private tokenRefreshTimer: ReturnType<typeof setInterval> | null = null;\n private _connected = false;\n\n // Global event listeners (across all conversations)\n private globalListeners = new Map<string, Set<(...args: unknown[]) => void>>();\n\n // Per-conversation event listeners\n private conversationListeners = new Map<string, Map<string, Set<(...args: unknown[]) => void>>>();\n\n // SSE event dedup \u2014 rolling window of recent eventIds to prevent duplicates\n private recentEventIds = new Set<string>();\n private eventIdQueue: string[] = [];\n private static readonly MAX_DEDUP_WINDOW = 200;\n\n // Per-conversation last seen message ID \u2014 used for catch-up on reconnect\n private lastSeenMessageId = new Map<string, string>();\n private hasConnectedBefore = false;\n\n // Derived URL parts\n private baseUrl: string;\n private streamBaseUrl: string;\n private projectId: string;\n\n constructor(config: GigabuddyChatConfig) {\n this.config = config;\n const url = new URL(config.apiUrl);\n this.baseUrl = url.origin;\n this.streamBaseUrl =\n config.streamUrl ??\n (url.hostname.startsWith('api.') ? `${url.protocol}//${url.hostname.replace(/^api\\./, 'stream.')}` : url.origin);\n const pathParts = url.pathname.split('/').filter(Boolean);\n this.projectId = pathParts[pathParts.length - 1];\n }\n\n // ===========================================================================\n // User stream connection\n // ===========================================================================\n\n /**\n * Connect the user stream SSE. Call once after auth.\n */\n connect(): void {\n if (this.sseClient) return;\n\n this.sseClient = new SSEClient({\n url: `${this.streamBaseUrl}/ext/stream/user?projectId=${encodeURIComponent(this.projectId)}`,\n getToken: this.config.getToken,\n userId: this.config.userId,\n onEvent: (event: ChannelEvent) => this.handleStreamEvent(event),\n onConnected: () => {\n this._connected = true;\n if (this.hasConnectedBefore) {\n void this.catchUpMissedMessages();\n }\n this.hasConnectedBefore = true;\n this.emitGlobal('connected');\n },\n onDisconnected: () => {\n this._connected = false;\n this.emitGlobal('disconnected');\n },\n });\n\n void this.sseClient.connect();\n\n // Refresh token every 50 minutes\n this.tokenRefreshTimer = setInterval(\n async () => {\n if (this.sseClient) {\n const newToken = await this.config.getToken();\n await this.sseClient.updateToken(newToken);\n }\n },\n 50 * 60 * 1_000,\n );\n }\n\n /**\n * Returns true if the user stream is connected.\n */\n isConnected(): boolean {\n return this._connected;\n }\n\n /**\n * Disconnect the user stream and clean up all listeners.\n */\n disconnect(): void {\n if (this.tokenRefreshTimer) {\n clearInterval(this.tokenRefreshTimer);\n this.tokenRefreshTimer = null;\n }\n if (this.sseClient) {\n this.sseClient.disconnect();\n this.sseClient = null;\n }\n this._connected = false;\n }\n\n // ===========================================================================\n // Global event listeners\n // ===========================================================================\n\n /**\n * Listen for events across all conversations.\n * Returns an unsubscribe function.\n */\n on<T extends keyof GlobalEventMap>(event: T, handler: GlobalEventMap[T]): () => void {\n if (!this.globalListeners.has(event)) {\n this.globalListeners.set(event, new Set());\n }\n this.globalListeners.get(event)!.add(handler as (...args: unknown[]) => void);\n return () => {\n this.globalListeners.get(event)?.delete(handler as (...args: unknown[]) => void);\n };\n }\n\n /**\n * Remove a global event listener.\n */\n off<T extends keyof GlobalEventMap>(event: T, handler: GlobalEventMap[T]): void {\n this.globalListeners.get(event)?.delete(handler as (...args: unknown[]) => void);\n }\n\n // ===========================================================================\n // ChatEventBus: per-conversation listeners (used by Conversation)\n // ===========================================================================\n\n addConversationListener(conversationId: string, event: string, handler: (...args: unknown[]) => void): () => void {\n if (!this.conversationListeners.has(conversationId)) {\n this.conversationListeners.set(conversationId, new Map());\n }\n const convMap = this.conversationListeners.get(conversationId)!;\n if (!convMap.has(event)) {\n convMap.set(event, new Set());\n }\n convMap.get(event)!.add(handler);\n\n return () => {\n convMap.get(event)?.delete(handler);\n };\n }\n\n // ===========================================================================\n // Internal event routing\n // ===========================================================================\n\n private handleStreamEvent(event: ChannelEvent): void {\n // Dedup: skip events we've already seen\n const dedupKey = event.eventId ?? event.messageId;\n if (dedupKey) {\n if (this.recentEventIds.has(dedupKey)) return;\n this.recentEventIds.add(dedupKey);\n this.eventIdQueue.push(dedupKey);\n if (this.eventIdQueue.length > GigabuddyChat.MAX_DEDUP_WINDOW) {\n const oldest = this.eventIdQueue.shift()!;\n this.recentEventIds.delete(oldest);\n }\n }\n\n const eventType = event.type;\n const conversationId = event.conversationId;\n\n // Track last seen message ID per conversation for reconnect catch-up\n if (conversationId && event.messageId && (eventType === 'message' || eventType === 'thread_reply')) {\n this.lastSeenMessageId.set(conversationId, event.messageId);\n }\n\n // Dispatch to global listeners\n this.emitGlobal(eventType, event);\n\n // Dispatch to conversation-specific listeners\n if (conversationId) {\n const convMap = this.conversationListeners.get(conversationId);\n if (convMap) {\n const handlers = convMap.get(eventType);\n if (handlers) {\n for (const handler of handlers) {\n try {\n handler(event);\n } catch {\n // Don't let listener errors break the stream\n }\n }\n }\n }\n }\n }\n\n private emitGlobal(event: string, ...args: unknown[]): void {\n const handlers = this.globalListeners.get(event);\n if (handlers) {\n for (const handler of handlers) {\n try {\n handler(...args);\n } catch {\n // Don't let listener errors break the stream\n }\n }\n }\n }\n\n // ===========================================================================\n // Reconnect catch-up\n // ===========================================================================\n\n /**\n * After an SSE reconnect, fetch any messages we missed per conversation\n * and replay them through the normal event pipeline.\n */\n private async catchUpMissedMessages(): Promise<void> {\n const entries = [...this.lastSeenMessageId.entries()];\n if (entries.length === 0) return;\n\n // Catch up each conversation in parallel\n await Promise.allSettled(\n entries.map(async ([conversationId, afterMessageId]) => {\n try {\n const conversation = this.conversations.get(conversationId);\n if (!conversation) return;\n\n const missed = await conversation.getMessages({ after: afterMessageId, limit: 100 });\n for (const msg of missed) {\n // Synthesize a ChannelEvent and route through normal pipeline (dedup will prevent doubles)\n this.handleStreamEvent({\n type: msg.parentMessageId ? 'thread_reply' : 'message',\n conversationId,\n eventId: msg.id,\n timestamp: msg.createdAt,\n data: msg as unknown as Record<string, unknown>,\n messageId: msg.id,\n senderName: msg.senderName,\n });\n }\n } catch {\n // Silently fail \u2014 better to miss catch-up than crash\n }\n }),\n );\n }\n\n // ===========================================================================\n // API helpers\n // ===========================================================================\n\n private async buildHeaders(): Promise<Record<string, string>> {\n const token = await this.config.getToken();\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n };\n if (this.config.userId) {\n headers['X-Chat-User-Id'] = this.config.userId;\n }\n return headers;\n }\n\n private extractConversationId(data: Record<string, unknown>): string {\n let conversationId = data['conversationId'] as string | undefined;\n if (!conversationId && data['conversation']) {\n let conv = data['conversation'];\n if (typeof conv === 'string') {\n try {\n conv = JSON.parse(conv);\n } catch {\n // not JSON\n }\n }\n if (conv && typeof conv === 'object' && 'id' in conv) {\n conversationId = (conv as { id: string }).id;\n }\n }\n if (!conversationId) {\n throw new Error('No conversationId returned from API');\n }\n return conversationId;\n }\n\n private trackConversation(conversationId: string): Conversation {\n const existing = this.conversations.get(conversationId);\n if (existing) return existing;\n const conversation = new Conversation(conversationId, this.config, this);\n this.conversations.set(conversationId, conversation);\n return conversation;\n }\n\n /**\n * Get or create a Conversation handle for a known conversationId.\n * Does not make any API calls \u2014 just returns a local wrapper.\n */\n getConversation(conversationId: string): Conversation {\n return this.trackConversation(conversationId);\n }\n\n // ===========================================================================\n // Conversation creation (v1 compat)\n // ===========================================================================\n\n async openConversation(\n options: {\n topic?: string;\n buddyId?: string;\n buddyProjectId?: string;\n userName?: string;\n } = {},\n ): Promise<Conversation> {\n const headers = await this.buildHeaders();\n\n const createResponse = await fetch(`${this.config.apiUrl}/chat/create-conversation`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n ...(options.topic ? { topic: options.topic } : {}),\n ...(options.userName ? { userName: options.userName } : {}),\n }),\n });\n\n if (!createResponse.ok) {\n const body = await createResponse.text();\n throw new Error(`Failed to create conversation: ${createResponse.status} ${body}`);\n }\n\n const createData = (await createResponse.json()) as Record<string, unknown>;\n const conversationId = this.extractConversationId(createData);\n\n if (options.buddyId) {\n const attachResponse = await fetch(`${this.config.apiUrl}/chat/attach-buddy`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId,\n buddyId: options.buddyId,\n ...(options.buddyProjectId ? { buddyProjectId: options.buddyProjectId } : {}),\n }),\n });\n\n if (!attachResponse.ok) {\n const body = await attachResponse.text();\n throw new Error(`Failed to attach buddy: ${attachResponse.status} ${body}`);\n }\n }\n\n return this.trackConversation(conversationId);\n }\n\n // ===========================================================================\n // v2: Channel management\n // ===========================================================================\n\n async createChannel(opts: {\n name: string;\n visibility?: ConversationVisibility;\n description?: string;\n topic?: string;\n }): Promise<Conversation> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/create-channel`, {\n method: 'POST',\n headers,\n body: JSON.stringify(opts),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to create channel: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as Record<string, unknown>;\n const conversationId = this.extractConversationId(data);\n return this.trackConversation(conversationId);\n }\n\n async joinChannel(conversationId: string): Promise<Conversation> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/join-channel`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ conversationId }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to join channel: ${response.status} ${body}`);\n }\n\n return this.trackConversation(conversationId);\n }\n\n async listChannels(opts?: { visibility?: ConversationVisibility; search?: string }): Promise<ConversationInfo[]> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/list-channels`, {\n method: 'POST',\n headers,\n body: JSON.stringify(opts ?? {}),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to list channels: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { conversations?: ConversationInfo[] };\n return data.conversations ?? [];\n }\n\n // ===========================================================================\n // v2: DMs\n // ===========================================================================\n\n async openDM(userId: string): Promise<Conversation> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/create-dm`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ participantId: userId }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to open DM: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as Record<string, unknown>;\n const conversationId = this.extractConversationId(data);\n return this.trackConversation(conversationId);\n }\n\n async openGroupDM(userIds: string[]): Promise<Conversation> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/create-group-dm`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ participantIds: userIds }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to open group DM: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as Record<string, unknown>;\n const conversationId = this.extractConversationId(data);\n return this.trackConversation(conversationId);\n }\n\n // ===========================================================================\n // v2: List conversations (all types)\n // ===========================================================================\n\n async listConversations(opts?: {\n type?: ConversationType;\n status?: string;\n visibility?: ConversationVisibility;\n limit?: number;\n offset?: number;\n }): Promise<{ conversations: ConversationInfo[]; myContextInstanceId?: string }> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/list-conversations`, {\n method: 'POST',\n headers,\n body: JSON.stringify(opts ?? {}),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to list conversations: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { conversations?: ConversationInfo[]; myContextInstanceId?: string };\n return { conversations: data.conversations ?? [], myContextInstanceId: data.myContextInstanceId };\n }\n\n // ===========================================================================\n // User search\n // ===========================================================================\n\n async searchUsers(opts?: { search?: string; limit?: number }): Promise<ChatUser[]> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/search-users`, {\n method: 'POST',\n headers,\n body: JSON.stringify(opts ?? {}),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to search users: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { users?: ChatUser[] };\n return data.users ?? [];\n }\n\n async listBuddies(opts?: { search?: string }): Promise<BuddyInfo[]> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/list-buddies`, {\n method: 'POST',\n headers,\n body: JSON.stringify(opts ?? {}),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to list buddies: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { buddies?: BuddyInfo[] };\n return data.buddies ?? [];\n }\n\n // ===========================================================================\n // User profile\n // ===========================================================================\n\n async getProfile(): Promise<UserProfile & { contextInstanceId?: string }> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/get-profile`, {\n method: 'POST',\n headers,\n body: JSON.stringify({}),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get profile: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { profile?: UserProfile; contextInstanceId?: string };\n return { ...(data.profile ?? {}), contextInstanceId: data.contextInstanceId };\n }\n\n async getProfiles(contextInstanceIds: string[]): Promise<Record<string, UserProfile>> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/get-profiles`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ contextInstanceIds }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get profiles: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { profiles?: Record<string, UserProfile> };\n return data.profiles ?? {};\n }\n\n async updateProfile(profile: {\n name?: string;\n avatarUrl?: string;\n handle?: string;\n notificationPreference?: 'all' | 'mentions' | 'nothing';\n }): Promise<UserProfile> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/update-profile`, {\n method: 'POST',\n headers,\n body: JSON.stringify(profile),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to update profile: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { profile?: UserProfile };\n return data.profile ?? {};\n }\n\n // ===========================================================================\n // Gadget management\n // ===========================================================================\n\n async listGadgets(): Promise<GadgetInfo[]> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/list-gadgets`, {\n method: 'POST',\n headers,\n body: JSON.stringify({}),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to list gadgets: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { entities?: GadgetInfo[] };\n return data.entities ?? [];\n }\n\n async getGadget(gadgetId: string): Promise<GadgetDetail> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/get-gadget`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ gadgetId }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get gadget: ${response.status} ${body}`);\n }\n\n return (await response.json()) as GadgetDetail;\n }\n\n async getGadgetByShare(shareId: string): Promise<GadgetDetail> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/get-gadget-by-share`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ shareId }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get gadget by share: ${response.status} ${body}`);\n }\n\n return (await response.json()) as GadgetDetail;\n }\n\n async createGadget(input: CreateGadgetInput): Promise<GadgetDetail> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/create-gadget`, {\n method: 'POST',\n headers,\n body: JSON.stringify(input),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to create gadget: ${response.status} ${body}`);\n }\n\n return (await response.json()) as GadgetDetail;\n }\n\n async updateGadget(input: UpdateGadgetInput): Promise<GadgetDetail> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/update-gadget`, {\n method: 'POST',\n headers,\n body: JSON.stringify(input),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to update gadget: ${response.status} ${body}`);\n }\n\n return (await response.json()) as GadgetDetail;\n }\n\n async deleteGadget(gadgetId: string): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/delete-gadget`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ gadgetId }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to delete gadget: ${response.status} ${body}`);\n }\n }\n\n async duplicateGadget(gadgetId: string, newName?: string): Promise<GadgetDetail> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/duplicate-gadget`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ gadgetId, newName }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to duplicate gadget: ${response.status} ${body}`);\n }\n\n return (await response.json()) as GadgetDetail;\n }\n\n async getGadgetVersions(gadgetId: string): Promise<GadgetVersionInfo[]> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/get-gadget-versions`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ gadgetId }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get gadget versions: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { versions?: GadgetVersionInfo[] };\n return data.versions ?? [];\n }\n\n async getGadgetVersion(gadgetId: string, version: number): Promise<GadgetVersionDetail> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/get-gadget-version`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ gadgetId, version }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get gadget version: ${response.status} ${body}`);\n }\n\n return (await response.json()) as GadgetVersionDetail;\n }\n\n async revertGadgetVersion(gadgetId: string, version: number): Promise<GadgetDetail> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/revert-gadget-version`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ gadgetId, version }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to revert gadget version: ${response.status} ${body}`);\n }\n\n return (await response.json()) as GadgetDetail;\n }\n\n async refineGadgetCode(\n gadgetId: string,\n feedback: string,\n ): Promise<{ componentCode: string; reducerCode?: string; commitMessage: string; changeType?: string }> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/refine-gadget-code`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ gadgetId, feedback }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to refine gadget code: ${response.status} ${body}`);\n }\n\n return (await response.json()) as {\n componentCode: string;\n reducerCode?: string;\n commitMessage: string;\n changeType?: string;\n };\n }\n\n async generateStatefulGadget(intent: string, currentSpec?: Partial<StatefulGadgetSpec>): Promise<StatefulGadgetSpec> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/generate-stateful-gadget`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ intent, currentSpec }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to generate stateful gadget: ${response.status} ${body}`);\n }\n\n return (await response.json()) as StatefulGadgetSpec;\n }\n\n async createStatefulGadget(input: CreateStatefulGadgetInput): Promise<StatefulGadgetResult> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/create-stateful-gadget`, {\n method: 'POST',\n headers,\n body: JSON.stringify(input),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to create stateful gadget: ${response.status} ${body}`);\n }\n\n return (await response.json()) as StatefulGadgetResult;\n }\n\n // ===========================================================================\n // File uploads\n // ===========================================================================\n\n /**\n * Upload a file for use in chat messages.\n * Returns metadata that can be passed as `FileContent` to `conversation.sendMessage()`.\n */\n async uploadFile(conversationId: string, file: Blob, filename?: string): Promise<FileUploadResult> {\n const token = await this.config.getToken();\n const formData = new FormData();\n formData.append('file', file, filename);\n formData.append('conversationId', conversationId);\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${token}`,\n };\n if (this.config.userId) {\n headers['X-Chat-User-Id'] = this.config.userId;\n }\n\n const response = await fetch(`${this.config.apiUrl}/chat/upload-file`, {\n method: 'POST',\n headers,\n body: formData,\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to upload file: ${response.status} ${body}`);\n }\n\n return (await response.json()) as FileUploadResult;\n }\n\n // ===========================================================================\n // File access\n // ===========================================================================\n\n /**\n * Get a displayable URL for a file by its ID.\n *\n * Returns a public URL (permanent) or a signed URL (temporary, default 1 hour).\n * Use this to resolve file references in messages to URLs that can be displayed\n * in `<img>` tags or opened in the browser.\n *\n * @param fileId - The file ID (from FileContent.fileId or tool output)\n * @param opts.expiresIn - Signed URL duration in seconds (default: 3600, max: 86400)\n * @returns Object with url and file metadata\n *\n * @example\n * ```typescript\n * const file = await chat.getFileUrl('abc123');\n * // file.url is ready to use in <img src={file.url} />\n * ```\n */\n async getFileUrl(\n fileId: string,\n opts?: { expiresIn?: number },\n ): Promise<{ url: string; name: string; contentType: string; size: number; expiresAt?: string }> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/get-file-url`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ fileId, ...(opts?.expiresIn ? { expiresIn: opts.expiresIn } : {}) }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get file URL: ${response.status} ${body}`);\n }\n\n return (await response.json()) as {\n url: string;\n name: string;\n contentType: string;\n size: number;\n expiresAt?: string;\n };\n }\n\n // ===========================================================================\n // Push notifications\n // ===========================================================================\n\n /**\n * Enable push notifications for the current user.\n *\n * Handles the entire browser Push API flow in one call:\n * 1. Checks browser support\n * 2. Registers the service worker\n * 3. Fetches the VAPID public key from the server\n * 4. Requests notification permission from the user\n * 5. Subscribes via the browser Push API\n * 6. Sends the subscription to the server\n *\n * Call this from a user-initiated event (e.g. a \"Turn on notifications\" button).\n *\n * @param serviceWorkerPath - Path to your push service worker (default: '/sw.js')\n * @returns The push subscription state, or null if not supported/denied\n */\n async enablePushNotifications(\n serviceWorkerPath = '/sw.js',\n ): Promise<{ subscribed: true; endpoint: string } | { subscribed: false; reason: string }> {\n // 1. Check browser support\n if (typeof window === 'undefined' || !('serviceWorker' in navigator) || !('PushManager' in window)) {\n return { subscribed: false, reason: 'Push notifications are not supported in this browser.' };\n }\n\n // 2. Get VAPID public key from server\n const vapidKey = await this.getVapidPublicKey();\n if (!vapidKey) {\n return { subscribed: false, reason: 'Push notifications are not configured for this project.' };\n }\n\n // 3. Request notification permission\n const permission = await Notification.requestPermission();\n if (permission !== 'granted') {\n return { subscribed: false, reason: `Notification permission ${permission}.` };\n }\n\n // 4. Register service worker\n const registration = await navigator.serviceWorker.register(serviceWorkerPath);\n await navigator.serviceWorker.ready;\n\n // 5. Subscribe via browser Push API\n const applicationServerKey = urlBase64ToUint8Array(vapidKey);\n const subscription = await registration.pushManager.subscribe({\n userVisibleOnly: true,\n applicationServerKey: applicationServerKey as BufferSource,\n });\n\n // 6. Send subscription to server\n const subJson = subscription.toJSON();\n await this.registerPushSubscription(subJson as PushSubscriptionJSON);\n\n return { subscribed: true, endpoint: subscription.endpoint };\n }\n\n /**\n * Disable push notifications for the current user.\n *\n * Unsubscribes the browser and removes the subscription from the server.\n */\n async disablePushNotifications(): Promise<void> {\n if (typeof window === 'undefined' || !('serviceWorker' in navigator)) return;\n\n const registration = await navigator.serviceWorker.getRegistration();\n if (!registration) return;\n\n const subscription = await registration.pushManager.getSubscription();\n if (!subscription) return;\n\n // Unsubscribe browser\n await subscription.unsubscribe();\n\n // Remove from server\n await this.unregisterPushSubscription(subscription.endpoint);\n }\n\n /**\n * Check if the current user has an active push subscription.\n */\n async getPushNotificationState(): Promise<{ subscribed: boolean; endpoint?: string }> {\n if (typeof window === 'undefined' || !('serviceWorker' in navigator)) {\n return { subscribed: false };\n }\n\n const registration = await navigator.serviceWorker.getRegistration();\n if (!registration) return { subscribed: false };\n\n const subscription = await registration.pushManager.getSubscription();\n if (!subscription) return { subscribed: false };\n\n return { subscribed: true, endpoint: subscription.endpoint };\n }\n\n /**\n * Get the VAPID public key for subscribing to push notifications.\n * Returns null if push is not configured on the server.\n */\n async getVapidPublicKey(): Promise<string | null> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/get-vapid-key`, {\n method: 'POST',\n headers,\n body: JSON.stringify({}),\n });\n\n if (!response.ok) {\n if (response.status === 503) return null;\n const body = await response.text();\n throw new Error(`Failed to get VAPID key: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { vapidPublicKey?: string };\n return data.vapidPublicKey ?? null;\n }\n\n /**\n * Register a push subscription for the current user.\n * Prefer using enablePushNotifications() which handles the full flow.\n */\n async registerPushSubscription(subscription: PushSubscriptionJSON): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/register-push-subscription`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ subscription }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to register push subscription: ${response.status} ${body}`);\n }\n }\n\n /**\n * Unregister a push subscription by endpoint URL.\n * Prefer using disablePushNotifications() which handles the full flow.\n */\n async unregisterPushSubscription(endpoint: string): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/unregister-push-subscription`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ endpoint }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to unregister push subscription: ${response.status} ${body}`);\n }\n }\n\n // ===========================================================================\n // Unread summary\n // ===========================================================================\n\n /**\n * Get unread message counts across all conversations for the current user.\n */\n async getUnreadSummary(): Promise<{ totalUnread: number; totalMentions: number }> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/unread-summary`, {\n method: 'POST',\n headers,\n body: JSON.stringify({}),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get unread summary: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { totalUnread?: number; totalMentions?: number };\n return { totalUnread: data.totalUnread ?? 0, totalMentions: data.totalMentions ?? 0 };\n }\n\n // ===========================================================================\n // User stats\n // ===========================================================================\n\n /**\n * Get per-user conversation statistics.\n * Optionally filter to a single user by contextInstanceId.\n */\n async getUserStats(opts?: { contextInstanceId?: string }): Promise<UserStat[]> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/user-stats`, {\n method: 'POST',\n headers,\n body: JSON.stringify(opts ?? {}),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get user stats: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { users?: UserStat[] };\n return data.users ?? [];\n }\n\n // ===========================================================================\n // Lifecycle\n // ===========================================================================\n\n closeConversation(conversationId: string): void {\n const conversation = this.conversations.get(conversationId);\n if (conversation) {\n conversation.disconnect();\n this.conversations.delete(conversationId);\n }\n // Clean up conversation-specific listeners\n this.conversationListeners.delete(conversationId);\n }\n\n destroy(): void {\n this.disconnect();\n for (const [id] of this.conversations) {\n this.conversations.delete(id);\n }\n this.conversationListeners.clear();\n this.globalListeners.clear();\n this.recentEventIds.clear();\n this.eventIdQueue = [];\n this.lastSeenMessageId.clear();\n this.hasConnectedBefore = false;\n }\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/**\n * Convert a base64url-encoded string to a Uint8Array.\n * Used to convert VAPID public keys for the browser Push API.\n */\nfunction urlBase64ToUint8Array(base64String: string): Uint8Array {\n const padding = '='.repeat((4 - (base64String.length % 4)) % 4);\n const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');\n const rawData = atob(base64);\n const outputArray = new Uint8Array(rawData.length);\n for (let i = 0; i < rawData.length; ++i) {\n outputArray[i] = rawData.charCodeAt(i);\n }\n return outputArray;\n}\n", "/**\n * Chat SDK public type definitions.\n */\n\nexport interface GigabuddyChatConfig {\n /**\n * Customer project API URL.\n * e.g. 'https://api.gigabuddy.com/orgSlug/projectId'\n *\n * The SDK calls `/chat/<action>` under this URL. The server\n * resolves the target Conversations project from the project's\n * `gigabuddy-chat` connection config.\n */\n apiUrl: string;\n\n /**\n * Optional base URL for SSE streaming connections.\n * e.g. 'https://stream.gigabuddy.com'\n *\n * When set, SSE connections use this origin instead of the one\n * derived from `apiUrl`. Defaults to the origin of `apiUrl`.\n */\n streamUrl?: string;\n\n /** Return a valid auth token (API key or Firebase ID token). */\n getToken: () => Promise<string> | string;\n\n /**\n * Optional user identifier sent as `X-Chat-User-Id` header.\n * Used by the backend to scope conversations to a specific user\n * when authenticating with an API key.\n */\n userId?: string;\n}\n\n// =============================================================================\n// User profile\n// =============================================================================\n\nexport interface UserProfile {\n name?: string;\n email?: string;\n avatarUrl?: string;\n handle?: string;\n notificationPreference?: 'all' | 'mentions' | 'nothing';\n}\n\n// =============================================================================\n// v2 conversation types\n// =============================================================================\n\nexport type ConversationType = 'channel' | 'dm' | 'group_dm';\nexport type ConversationVisibility = 'public' | 'private';\nexport type MessageRole = 'member' | 'buddy' | 'operator' | 'system';\n\nexport interface FileContent {\n type: 'file';\n fileId: string;\n url: string;\n name: string;\n contentType: string;\n size: number;\n text?: string;\n}\n\n/** Rich message content \u2014 discriminated union */\nexport type MessageContent =\n | TextContent\n | GadgetContent\n | GadgetRefineContent\n | DataContent\n | SystemContent\n | CompositeContent\n | FileContent;\n\nexport interface TextContent {\n type: 'text';\n text: string;\n format?: 'plain' | 'markdown';\n}\n\nexport interface GadgetContent {\n type: 'gadget';\n gadgetRef: { type: 'gadget'; id: string };\n props?: Record<string, unknown>;\n renderHint?: 'inline' | 'modal' | 'replace';\n fallbackText?: string;\n}\n\nexport interface GadgetRefineContent {\n type: 'gadget_refine';\n gadgetRef: { type: string; id: string };\n feedback: string;\n text: string;\n}\n\nexport interface DataContent {\n type: 'data';\n key: string;\n value: unknown;\n displayAs?: string;\n}\n\nexport interface SystemContent {\n type: 'system';\n event: string;\n text: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface CompositeContent {\n type: 'composite';\n blocks: (TextContent | GadgetContent | DataContent | FileContent)[];\n}\n\nexport interface FileUploadResult {\n fileId: string;\n url: string;\n name: string;\n contentType: string;\n size: number;\n}\n\nexport interface MessageReaction {\n emoji: string;\n count: number;\n userIds: string[];\n}\n\nexport interface ConversationReadState {\n lastReadMessageId: string;\n lastReadAt: string;\n unreadCount: number;\n mentionCount: number;\n threadUnreadCount: number;\n}\n\nexport interface ConversationInfo {\n id: string;\n type?: ConversationType;\n visibility?: ConversationVisibility;\n name?: string;\n description?: string;\n topic?: string;\n pictureUrl?: string;\n status: string;\n participants: Array<{\n contextInstanceId: string;\n role: string;\n name?: string;\n }>;\n buddyRef?: { type: 'buddy'; id: string };\n messageCount: number;\n lastMessageAt?: string;\n lastMessagePreview?: string;\n readState?: ConversationReadState | null;\n metadata?: Record<string, unknown>;\n}\n\n// =============================================================================\n// Chat users\n// =============================================================================\n\nexport interface ChatUser {\n contextInstanceId: string;\n name?: string;\n email?: string;\n avatarUrl?: string;\n handle?: string;\n}\n\n// =============================================================================\n// Events\n// =============================================================================\n\nexport type ChannelEventType =\n | 'message'\n | 'gadget'\n | 'data'\n | 'instruction'\n | 'typing'\n | 'status'\n | 'error'\n | 'thread_reply'\n | 'reaction_added'\n | 'participant_joined'\n | 'participant_left'\n | 'message_updated'\n | 'message_deleted'\n | 'channel_created'\n | 'channel_updated'\n | 'channel_archived';\n\n/** User-stream-level event types (superset of channel events) */\nexport type UserStreamEventType =\n | ChannelEventType\n | 'conversation_added'\n | 'conversation_removed'\n | 'notification'\n | 'gadget_state_update';\n\n/** Data for gadget_state_update events (delivered via user stream) */\nexport interface GadgetStateUpdateEvent {\n type: 'gadget_state_update';\n deploymentId: string;\n gadgetId: string;\n missionStatus: string;\n sharedState: Record<string, unknown>;\n}\n\n/** Server-initiated notification delivered via the user stream. */\nexport interface ChatNotification {\n title: string;\n body: string;\n conversationId?: string;\n messageId?: string;\n type?: string;\n}\n\n/** Data for conversation_added events */\nexport interface ConversationAddedEvent {\n type: 'conversation_added';\n conversationId: string;\n conversation: ConversationInfo;\n}\n\n/** Data for conversation_removed events */\nexport interface ConversationRemovedEvent {\n type: 'conversation_removed';\n conversationId: string;\n}\n\n/**\n * Event bus interface for routing user-stream events to conversations.\n * GigabuddyChat implements this; Conversation uses it to filter events.\n */\nexport interface ChatEventBus {\n addConversationListener(conversationId: string, event: string, handler: (...args: unknown[]) => void): () => void;\n}\n\nexport interface ChannelEvent {\n type: ChannelEventType;\n conversationId: string;\n eventId: string;\n timestamp: string;\n data: Record<string, unknown>;\n source?: string;\n messageId?: string;\n senderName?: string;\n}\n\nexport interface Message {\n id: string;\n role: MessageRole;\n content: string | MessageContent;\n senderContextInstanceId?: string;\n senderName?: string;\n createdAt: string;\n metadata?: Record<string, unknown>;\n parentMessageId?: string;\n threadCount?: number;\n lastThreadReplyAt?: string;\n editedAt?: string;\n reactions?: MessageReaction[];\n}\n\nexport interface ConversationEventMap {\n message: (event: ChannelEvent) => void;\n gadget: (event: ChannelEvent) => void;\n typing: (event: ChannelEvent) => void;\n status: (event: ChannelEvent) => void;\n error: (event: ChannelEvent) => void;\n connected: () => void;\n disconnected: () => void;\n // v2 events\n thread_reply: (event: ChannelEvent) => void;\n reaction_added: (event: ChannelEvent) => void;\n participant_joined: (event: ChannelEvent) => void;\n participant_left: (event: ChannelEvent) => void;\n message_updated: (event: ChannelEvent) => void;\n message_deleted: (event: ChannelEvent) => void;\n}\n\n/** Global event map \u2014 includes conversation events plus user-stream-only events. */\nexport interface GlobalEventMap extends ConversationEventMap {\n conversation_added: (event: ConversationAddedEvent) => void;\n conversation_removed: (event: ConversationRemovedEvent) => void;\n notification: (event: ChatNotification) => void;\n gadget_state_update: (event: GadgetStateUpdateEvent) => void;\n channel_created: (event: ChannelEvent) => void;\n channel_updated: (event: ChannelEvent) => void;\n channel_archived: (event: ChannelEvent) => void;\n}\n\n// =============================================================================\n// Event type guards\n// =============================================================================\n\n/** Check if a ChannelEvent is a \"message\" event with Message data. */\nexport function isMessageEvent(event: ChannelEvent): event is ChannelEvent & { data: Message } {\n return event.type === 'message' && event.data != null && 'id' in event.data && 'role' in event.data;\n}\n\n/** Check if a ChannelEvent is a \"participant_joined\" event. */\nexport function isParticipantJoinedEvent(\n event: ChannelEvent,\n): event is ChannelEvent & { data: { contextInstanceId: string; name?: string; role?: string } } {\n return event.type === 'participant_joined' && event.data != null && 'contextInstanceId' in event.data;\n}\n\n/** Check if a ChannelEvent is a \"participant_left\" event. */\nexport function isParticipantLeftEvent(\n event: ChannelEvent,\n): event is ChannelEvent & { data: { contextInstanceId: string; name?: string } } {\n return event.type === 'participant_left' && event.data != null && 'contextInstanceId' in event.data;\n}\n\n/** Check if a ChannelEvent is a \"reaction_added\" event. */\nexport function isReactionEvent(\n event: ChannelEvent,\n): event is ChannelEvent & { data: { messageId: string; emoji: string; userId: string } } {\n return event.type === 'reaction_added' && event.data != null && 'emoji' in event.data;\n}\n\n// =============================================================================\n// Gadget management\n// =============================================================================\n\nexport interface GadgetInfo {\n id: string;\n name: string;\n description?: string;\n thumbnailUrl?: string;\n latestVersion: number;\n createdAt?: string;\n updatedAt?: string;\n}\n\nexport interface GadgetDetail extends GadgetInfo {\n data: {\n componentCode: string;\n componentLanguage?: 'javascript' | 'typescript';\n missionRef?: { type: string; id: string };\n actionRef?: { type: string; id: string };\n reducerCode?: string;\n stateBindings?: { shared?: { contextFields: string[] }; user?: { contextFields: string[] } };\n assets?: Record<string, { fileId: string; url: string; name: string; contentType: string }>;\n };\n /** Active share ID \u2014 enables context SDK via `/shared/:shareId/context/*` endpoints */\n shareId?: string;\n /** Whether the gadget's share has context SDK enabled */\n contextEnabled?: boolean;\n /** Whitelist of allowed context types (if contextEnabled) */\n contextTypes?: string[];\n /** Whether knowledge search is available via the share */\n knowledgeEnabled?: boolean;\n /** Source project ID (set for cross-project gadgets) */\n sourceProjectId?: string;\n}\n\nexport interface GadgetVersionInfo {\n version: number;\n name?: string;\n createdAt?: string;\n createdBy?: { userId: string; name?: string };\n commitMessage?: string;\n}\n\nexport interface GadgetVersionDetail extends GadgetVersionInfo {\n data: Record<string, unknown>;\n}\n\nexport interface CreateGadgetInput {\n name: string;\n description?: string;\n componentCode: string;\n}\n\nexport interface UpdateGadgetInput {\n gadgetId: string;\n name?: string;\n description?: string;\n componentCode?: string;\n reducerCode?: string;\n commitMessage?: string;\n}\n\n// =============================================================================\n// Stateful gadget types\n// =============================================================================\n\nexport interface StateFieldDefinition {\n name: string;\n type: 'string' | 'number' | 'boolean' | 'object' | 'array';\n scope: 'shared' | 'user';\n initialValue?: string | number | boolean;\n}\n\nexport interface StatefulGadgetSpec {\n componentCode: string;\n reducerCode: string;\n stateFields: StateFieldDefinition[];\n name?: string;\n description?: string;\n}\n\nexport interface CreateStatefulGadgetInput {\n name: string;\n description?: string;\n componentCode: string;\n reducerCode: string;\n stateFields: StateFieldDefinition[];\n}\n\nexport interface StatefulGadgetResult {\n gadgetId: string;\n missionId: string;\n actionId: string;\n name: string;\n description?: string;\n latestVersion: number;\n data: GadgetDetail['data'];\n}\n\n// =============================================================================\n// User stats\n// =============================================================================\n\nexport interface UserStat {\n contextInstanceId: string;\n name: string;\n conversationCount: number;\n lastActiveAt: string | null;\n firstSeenAt: string | null;\n}\n\n// =============================================================================\n// Buddy info\n// =============================================================================\n\nexport interface BuddyInfo {\n contextInstanceId: string;\n name?: string;\n avatarUrl?: string;\n purpose?: string;\n}\n\n// =============================================================================\n// Thread summaries\n// =============================================================================\n\nexport interface ThreadSummary {\n parentMessage: Message;\n threadCount: number;\n lastThreadReplyAt: string;\n lastReplySnippet?: string;\n lastReplySenderName?: string;\n unreadCount: number;\n}\n"],
5
- "mappings": ";AAkBO,IAAM,eAAN,MAAmB;AAAA,EACf;AAAA,EACD;AAAA,EACA;AAAA,EACA,eAA+B,CAAC;AAAA,EAExC,YAAY,gBAAwB,QAA6B,UAAyB;AACxF,SAAK,iBAAiB;AACtB,SAAK,SAAS;AACd,SAAK,WAAW,YAAY;AAAA,EAC9B;AAAA,EAEA,MAAc,eAAgD;AAC5D,UAAM,QAAQ,MAAM,KAAK,OAAO,SAAS;AACzC,UAAM,UAAkC;AAAA,MACtC,gBAAgB;AAAA,MAChB,eAAe,UAAU,KAAK;AAAA,IAChC;AACA,QAAI,KAAK,OAAO,QAAQ;AACtB,cAAQ,gBAAgB,IAAI,KAAK,OAAO;AAAA,IAC1C;AACA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,YAAY,SAAoD;AACpE,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,OAAgC;AAAA,MACpC,gBAAgB,KAAK;AAAA,IACvB;AAEA,QAAI,OAAO,YAAY,UAAU;AAC/B,WAAK,MAAM,IAAI;AAAA,IACjB,OAAO;AACL,WAAK,SAAS,IAAI;AAAA,IACpB;AAEA,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,sBAAsB;AAAA,MACtE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,IAC3B,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,eAAe,MAAM,SAAS,KAAK;AACzC,YAAM,IAAI,MAAM,2BAA2B,SAAS,MAAM,IAAI,YAAY,EAAE;AAAA,IAC9E;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,MAAM,QAAQ,WAAmB,SAAiD;AAChF,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,OAAgC;AAAA,MACpC,gBAAgB,KAAK;AAAA,MACrB,iBAAiB;AAAA,IACnB;AAEA,QAAI,OAAO,YAAY,UAAU;AAC/B,WAAK,MAAM,IAAI;AAAA,IACjB,OAAO;AACL,WAAK,SAAS,IAAI;AAAA,IACpB;AAEA,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,0BAA0B;AAAA,MAC1E,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,IAC3B,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,eAAe,MAAM,SAAS,KAAK;AACzC,YAAM,IAAI,MAAM,+BAA+B,SAAS,MAAM,IAAI,YAAY,EAAE;AAAA,IAClF;AAAA,EACF;AAAA,EAEA,MAAM,YAAY,WAAmB,SAAiD;AACpF,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,sBAAsB;AAAA,MACtE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,gBAAgB,KAAK;AAAA,QACrB;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,2BAA2B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACtE;AAAA,EACF;AAAA,EAEA,MAAM,cAAc,WAAkC;AACpD,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,wBAAwB;AAAA,MACxE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,gBAAgB,KAAK;AAAA,QACrB;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,6BAA6B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACxE;AAAA,EACF;AAAA,EAEA,MAAM,qBAAqB,cAAsB,SAAiD;AAChG,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,OAAgC;AAAA,MACpC,gBAAgB,KAAK;AAAA,MACrB;AAAA,IACF;AAEA,QAAI,OAAO,YAAY,UAAU;AAC/B,WAAK,MAAM,IAAI;AAAA,IACjB,OAAO;AACL,WAAK,SAAS,IAAI;AAAA,IACpB;AAEA,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,gCAAgC;AAAA,MAChF,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,IAC3B,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,eAAe,MAAM,SAAS,KAAK;AACzC,YAAM,IAAI,MAAM,qCAAqC,SAAS,MAAM,IAAI,YAAY,EAAE;AAAA,IACxF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,YAAY,WAAmB,OAA8B;AACjE,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,sBAAsB;AAAA,MACtE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,gBAAgB,KAAK;AAAA,QACrB;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,2BAA2B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACtE;AAAA,EACF;AAAA,EAEA,MAAM,eAAe,WAAmB,OAA8B;AACpE,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,yBAAyB;AAAA,MACzE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,gBAAgB,KAAK;AAAA,QACrB;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,8BAA8B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACzE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,SAAS,WAAmC;AAChD,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,mBAAmB;AAAA,MACnE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,gBAAgB,KAAK;AAAA,QACrB,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,MACnC,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,wBAAwB,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACnE;AAAA,EACF;AAAA,EAEA,MAAM,eAAe,iBAAyB,WAAmC;AAC/E,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,0BAA0B;AAAA,MAC1E,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,gBAAgB,KAAK;AAAA,QACrB;AAAA,QACA,GAAI,YAAY,EAAE,UAAU,IAAI,CAAC;AAAA,MACnC,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,+BAA+B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IAC1E;AAAA,EACF;AAAA,EAEA,MAAM,mBAAmB,MAAsE;AAC7F,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,8BAA8B;AAAA,MAC9E,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,gBAAgB,KAAK;AAAA,QACrB,GAAI,MAAM,QAAQ,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;AAAA,QAC3C,GAAI,MAAM,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;AAAA,MAChD,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,mCAAmC,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IAC9E;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK,WAAW,CAAC;AAAA,EAC1B;AAAA,EAEA,MAAM,WAAW,MAAoD;AACnE,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,qBAAqB;AAAA,MACrE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,gBAAgB,KAAK;AAAA,QACrB,GAAI,MAAM,kBAAkB,EAAE,iBAAiB,KAAK,gBAAgB,IAAI,CAAC;AAAA,MAC3E,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,0BAA0B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACrE;AAAA,EACF;AAAA,EAEA,MAAM,YAAY,OAA8B;AAC9C,WAAO,KAAK,cAAc,EAAE,MAAM,CAAC;AAAA,EACrC;AAAA,EAEA,MAAM,cAAc,QAKF;AAChB,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,wBAAwB;AAAA,MACxE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,gBAAgB,KAAK;AAAA,QACrB,GAAG;AAAA,MACL,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,6BAA6B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACxE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,YAAY,UAAmC,MAA2C;AAC9F,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,sBAAsB;AAAA,MACtE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,gBAAgB,KAAK;AAAA,QACrB;AAAA,QACA,OAAO,MAAM,SAAS;AAAA,MACxB,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,2BAA2B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACtE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,eAAe,QAA+B;AAClD,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,2BAA2B;AAAA,MAC3E,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,gBAAgB,KAAK;AAAA,QACrB;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,8BAA8B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACzE;AAAA,EACF;AAAA,EAEA,MAAM,kBAAkB,QAA+B;AACrD,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,4BAA4B;AAAA,MAC5E,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,gBAAgB,KAAK;AAAA,QACrB;AAAA,MACF,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,iCAAiC,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IAC5E;AAAA,EACF;AAAA,EAEA,MAAM,YAAY,SAAiB,MAAmD;AACpF,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,sBAAsB;AAAA,MACtE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,gBAAgB,KAAK;AAAA,QACrB;AAAA,QACA,GAAI,MAAM,iBAAiB,EAAE,gBAAgB,KAAK,eAAe,IAAI,CAAC;AAAA,MACxE,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,2BAA2B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACtE;AAAA,EACF;AAAA,EAEA,MAAM,cAA6B;AACjC,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,sBAAsB;AAAA,MACtE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,gBAAgB,KAAK;AAAA,MACvB,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,2BAA2B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACtE;AAAA,EACF;AAAA,EAEA,MAAM,QAAuB;AAC3B,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,4BAA4B;AAAA,MAC5E,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,gBAAgB,KAAK;AAAA,MACvB,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,iCAAiC,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IAC5E;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,YAAY,MAAgF;AAChG,UAAM,UAAU,MAAM,KAAK,aAAa;AACxC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,sBAAsB;AAAA,MACtE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,gBAAgB,KAAK;AAAA,QACrB,GAAI,MAAM,QAAQ,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;AAAA,QAC3C,GAAI,MAAM,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;AAAA,QAC9C,GAAI,MAAM,QAAQ,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;AAAA,MAC7C,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,2BAA2B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACtE;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK,YAAY,CAAC;AAAA,EAC3B;AAAA,EAEA,MAAM,iBAAiB,iBAAyB,MAAgE;AAC9G,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,4BAA4B;AAAA,MAC5E,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,gBAAgB,KAAK;AAAA,QACrB;AAAA,QACA,GAAI,MAAM,QAAQ,EAAE,OAAO,KAAK,MAAM,IAAI,CAAC;AAAA,QAC3C,GAAI,MAAM,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;AAAA,MAChD,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,iCAAiC,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IAC5E;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK,YAAY,CAAC;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,GAAyC,OAAU,SAA8C;AAC/F,QAAI,KAAK,UAAU;AACjB,YAAM,QAAQ,KAAK,SAAS;AAAA,QAC1B,KAAK;AAAA,QACL;AAAA,QACA;AAAA,MACF;AACA,WAAK,aAAa,KAAK,KAAK;AAC5B,aAAO;AAAA,IACT;AAGA,WAAO,MAAM;AAAA,IAAC;AAAA,EAChB;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,UAAyB;AAAA,EAE/B;AAAA;AAAA,EAGA,aAAmB;AAEjB,eAAW,SAAS,KAAK,cAAc;AACrC,YAAM;AAAA,IACR;AACA,SAAK,eAAe,CAAC;AAAA,EACvB;AACF;;;ACxfA,IAAM,yBAAyB;AAC/B,IAAM,qBAAqB;AAC3B,IAAM,iBAAiB;AAEhB,IAAM,YAAN,MAAgB;AAAA,EACb;AAAA,EACA,kBAA0C;AAAA,EAC1C,iBAAuD;AAAA,EACvD,mBAAmB;AAAA,EACnB,UAAU;AAAA,EAElB,YAAY,QAAyB;AACnC,SAAK,SAAS;AAAA,EAChB;AAAA,EAEA,MAAM,UAAyB;AAC7B,SAAK,UAAU;AACf,SAAK,mBAAmB;AACxB,UAAM,KAAK,YAAY;AAAA,EACzB;AAAA,EAEA,aAAmB;AACjB,SAAK,UAAU;AACf,QAAI,KAAK,gBAAgB;AACvB,mBAAa,KAAK,cAAc;AAChC,WAAK,iBAAiB;AAAA,IACxB;AACA,QAAI,KAAK,iBAAiB;AACxB,WAAK,gBAAgB,MAAM;AAC3B,WAAK,kBAAkB;AAAA,IACzB;AAAA,EACF;AAAA,EAEA,MAAM,YAAY,UAAiC;AAEjD,UAAM,mBAAmB,KAAK,OAAO;AACrC,SAAK,OAAO,WAAW,MAAM;AAG7B,QAAI,KAAK,iBAAiB;AACxB,WAAK,gBAAgB,MAAM;AAC3B,WAAK,kBAAkB;AACvB,WAAK,mBAAmB;AACxB,YAAM,KAAK,YAAY;AAAA,IACzB;AAGA,SAAK,OAAO,WAAW;AAAA,EACzB;AAAA,EAEA,MAAc,cAA6B;AACzC,QAAI,KAAK;AAAS;AAElB,QAAI;AACF,YAAM,QAAQ,MAAM,KAAK,OAAO,SAAS;AACzC,WAAK,kBAAkB,IAAI,gBAAgB;AAE3C,YAAM,UAAkC;AAAA,QACtC,eAAe,UAAU,KAAK;AAAA,QAC9B,QAAQ;AAAA,MACV;AACA,UAAI,KAAK,OAAO,QAAQ;AACtB,gBAAQ,gBAAgB,IAAI,KAAK,OAAO;AAAA,MAC1C;AAEA,YAAM,WAAW,MAAM,MAAM,KAAK,OAAO,KAAK;AAAA,QAC5C,QAAQ;AAAA,QACR;AAAA,QACA,QAAQ,KAAK,gBAAgB;AAAA,MAC/B,CAAC;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,IAAI,MAAM,0BAA0B,SAAS,MAAM,IAAI,SAAS,UAAU,EAAE;AAAA,MACpF;AAEA,UAAI,CAAC,SAAS,MAAM;AAClB,cAAM,IAAI,MAAM,0BAA0B;AAAA,MAC5C;AAEA,WAAK,mBAAmB;AAExB,WAAK,WAAW,SAAS,IAAI;AAAA,IAC/B,SAAS,OAAO;AACd,UAAI,KAAK;AAAS;AAClB,UAAI,iBAAiB,gBAAgB,MAAM,SAAS;AAAc;AAElE,WAAK,OAAO,eAAe;AAC3B,WAAK,kBAAkB;AAAA,IACzB;AAAA,EACF;AAAA,EAEA,MAAc,WAAW,MAAiD;AACxE,UAAM,SAAS,KAAK,UAAU;AAC9B,UAAM,UAAU,IAAI,YAAY;AAChC,QAAI,SAAS;AAEb,QAAI;AACF,aAAO,MAAM;AACX,cAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAC1C,YAAI;AAAM;AAEV,kBAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAGhD,cAAM,WAAW,OAAO,MAAM,MAAM;AACpC,iBAAS,SAAS,IAAI,KAAK;AAE3B,mBAAW,WAAW,UAAU;AAC9B,eAAK,gBAAgB,OAAO;AAAA,QAC9B;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,UAAI,KAAK;AAAS;AAClB,UAAI,iBAAiB,gBAAgB,MAAM,SAAS;AAAc;AAAA,IACpE,UAAE;AACA,aAAO,YAAY;AAAA,IACrB;AAGA,QAAI,CAAC,KAAK,SAAS;AACjB,WAAK,OAAO,eAAe;AAC3B,WAAK,kBAAkB;AAAA,IACzB;AAAA,EACF;AAAA,EAEQ,gBAAgB,SAAuB;AAE7C,QAAI,QAAQ,KAAK,EAAE,WAAW,GAAG;AAAG;AAEpC,QAAI,YAAY;AAChB,QAAI,OAAO;AAEX,eAAW,QAAQ,QAAQ,MAAM,IAAI,GAAG;AACtC,UAAI,KAAK,WAAW,SAAS,GAAG;AAC9B,oBAAY,KAAK,MAAM,CAAC,EAAE,KAAK;AAAA,MACjC,WAAW,KAAK,WAAW,QAAQ,GAAG;AACpC,eAAO,KAAK,MAAM,CAAC;AAAA,MACrB,WAAW,KAAK,WAAW,OAAO,GAAG;AACnC,eAAO,KAAK,MAAM,CAAC;AAAA,MACrB;AAAA,IACF;AAEA,QAAI,cAAc,aAAa;AAC7B,WAAK,OAAO,YAAY;AACxB;AAAA,IACF;AAEA,QAAI,CAAC;AAAM;AAEX,QAAI;AACF,YAAM,SAAS,KAAK,MAAM,IAAI;AAE9B,UAAI,CAAC,OAAO,MAAM;AAChB,eAAO,OAAO;AAAA,MAChB;AACA,WAAK,OAAO,QAAQ,MAAM;AAAA,IAC5B,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEQ,oBAA0B;AAChC,QAAI,KAAK;AAAS;AAClB,QAAI,KAAK,oBAAoB;AAAwB;AAErD,UAAM,UAAU,KAAK,IAAI,qBAAqB,KAAK,IAAI,GAAG,KAAK,gBAAgB,GAAG,cAAc;AAChG,SAAK;AAEL,SAAK,iBAAiB,WAAW,MAAM;AACrC,WAAK,iBAAiB;AACtB,WAAK,YAAY;AAAA,IACnB,GAAG,OAAO;AAAA,EACZ;AACF;;;AClKO,IAAM,gBAAN,MAAM,eAAsC;AAAA,EACzC;AAAA,EACA,gBAAgB,oBAAI,IAA0B;AAAA;AAAA,EAG9C,YAA8B;AAAA,EAC9B,oBAA2D;AAAA,EAC3D,aAAa;AAAA;AAAA,EAGb,kBAAkB,oBAAI,IAA+C;AAAA;AAAA,EAGrE,wBAAwB,oBAAI,IAA4D;AAAA;AAAA,EAGxF,iBAAiB,oBAAI,IAAY;AAAA,EACjC,eAAyB,CAAC;AAAA,EAClC,OAAwB,mBAAmB;AAAA;AAAA,EAGnC,oBAAoB,oBAAI,IAAoB;AAAA,EAC5C,qBAAqB;AAAA;AAAA,EAGrB;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,QAA6B;AACvC,SAAK,SAAS;AACd,UAAM,MAAM,IAAI,IAAI,OAAO,MAAM;AACjC,SAAK,UAAU,IAAI;AACnB,SAAK,gBACH,OAAO,cACN,IAAI,SAAS,WAAW,MAAM,IAAI,GAAG,IAAI,QAAQ,KAAK,IAAI,SAAS,QAAQ,UAAU,SAAS,CAAC,KAAK,IAAI;AAC3G,UAAM,YAAY,IAAI,SAAS,MAAM,GAAG,EAAE,OAAO,OAAO;AACxD,SAAK,YAAY,UAAU,UAAU,SAAS,CAAC;AAAA,EACjD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,UAAgB;AACd,QAAI,KAAK;AAAW;AAEpB,SAAK,YAAY,IAAI,UAAU;AAAA,MAC7B,KAAK,GAAG,KAAK,aAAa,8BAA8B,mBAAmB,KAAK,SAAS,CAAC;AAAA,MAC1F,UAAU,KAAK,OAAO;AAAA,MACtB,QAAQ,KAAK,OAAO;AAAA,MACpB,SAAS,CAAC,UAAwB,KAAK,kBAAkB,KAAK;AAAA,MAC9D,aAAa,MAAM;AACjB,aAAK,aAAa;AAClB,YAAI,KAAK,oBAAoB;AAC3B,eAAK,KAAK,sBAAsB;AAAA,QAClC;AACA,aAAK,qBAAqB;AAC1B,aAAK,WAAW,WAAW;AAAA,MAC7B;AAAA,MACA,gBAAgB,MAAM;AACpB,aAAK,aAAa;AAClB,aAAK,WAAW,cAAc;AAAA,MAChC;AAAA,IACF,CAAC;AAED,SAAK,KAAK,UAAU,QAAQ;AAG5B,SAAK,oBAAoB;AAAA,MACvB,YAAY;AACV,YAAI,KAAK,WAAW;AAClB,gBAAM,WAAW,MAAM,KAAK,OAAO,SAAS;AAC5C,gBAAM,KAAK,UAAU,YAAY,QAAQ;AAAA,QAC3C;AAAA,MACF;AAAA,MACA,KAAK,KAAK;AAAA,IACZ;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,cAAuB;AACrB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,aAAmB;AACjB,QAAI,KAAK,mBAAmB;AAC1B,oBAAc,KAAK,iBAAiB;AACpC,WAAK,oBAAoB;AAAA,IAC3B;AACA,QAAI,KAAK,WAAW;AAClB,WAAK,UAAU,WAAW;AAC1B,WAAK,YAAY;AAAA,IACnB;AACA,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,GAAmC,OAAU,SAAwC;AACnF,QAAI,CAAC,KAAK,gBAAgB,IAAI,KAAK,GAAG;AACpC,WAAK,gBAAgB,IAAI,OAAO,oBAAI,IAAI,CAAC;AAAA,IAC3C;AACA,SAAK,gBAAgB,IAAI,KAAK,EAAG,IAAI,OAAuC;AAC5E,WAAO,MAAM;AACX,WAAK,gBAAgB,IAAI,KAAK,GAAG,OAAO,OAAuC;AAAA,IACjF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,IAAoC,OAAU,SAAkC;AAC9E,SAAK,gBAAgB,IAAI,KAAK,GAAG,OAAO,OAAuC;AAAA,EACjF;AAAA;AAAA;AAAA;AAAA,EAMA,wBAAwB,gBAAwB,OAAe,SAAmD;AAChH,QAAI,CAAC,KAAK,sBAAsB,IAAI,cAAc,GAAG;AACnD,WAAK,sBAAsB,IAAI,gBAAgB,oBAAI,IAAI,CAAC;AAAA,IAC1D;AACA,UAAM,UAAU,KAAK,sBAAsB,IAAI,cAAc;AAC7D,QAAI,CAAC,QAAQ,IAAI,KAAK,GAAG;AACvB,cAAQ,IAAI,OAAO,oBAAI,IAAI,CAAC;AAAA,IAC9B;AACA,YAAQ,IAAI,KAAK,EAAG,IAAI,OAAO;AAE/B,WAAO,MAAM;AACX,cAAQ,IAAI,KAAK,GAAG,OAAO,OAAO;AAAA,IACpC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMQ,kBAAkB,OAA2B;AAEnD,UAAM,WAAW,MAAM,WAAW,MAAM;AACxC,QAAI,UAAU;AACZ,UAAI,KAAK,eAAe,IAAI,QAAQ;AAAG;AACvC,WAAK,eAAe,IAAI,QAAQ;AAChC,WAAK,aAAa,KAAK,QAAQ;AAC/B,UAAI,KAAK,aAAa,SAAS,eAAc,kBAAkB;AAC7D,cAAM,SAAS,KAAK,aAAa,MAAM;AACvC,aAAK,eAAe,OAAO,MAAM;AAAA,MACnC;AAAA,IACF;AAEA,UAAM,YAAY,MAAM;AACxB,UAAM,iBAAiB,MAAM;AAG7B,QAAI,kBAAkB,MAAM,cAAc,cAAc,aAAa,cAAc,iBAAiB;AAClG,WAAK,kBAAkB,IAAI,gBAAgB,MAAM,SAAS;AAAA,IAC5D;AAGA,SAAK,WAAW,WAAW,KAAK;AAGhC,QAAI,gBAAgB;AAClB,YAAM,UAAU,KAAK,sBAAsB,IAAI,cAAc;AAC7D,UAAI,SAAS;AACX,cAAM,WAAW,QAAQ,IAAI,SAAS;AACtC,YAAI,UAAU;AACZ,qBAAW,WAAW,UAAU;AAC9B,gBAAI;AACF,sBAAQ,KAAK;AAAA,YACf,QAAQ;AAAA,YAER;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,WAAW,UAAkB,MAAuB;AAC1D,UAAM,WAAW,KAAK,gBAAgB,IAAI,KAAK;AAC/C,QAAI,UAAU;AACZ,iBAAW,WAAW,UAAU;AAC9B,YAAI;AACF,kBAAQ,GAAG,IAAI;AAAA,QACjB,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAc,wBAAuC;AACnD,UAAM,UAAU,CAAC,GAAG,KAAK,kBAAkB,QAAQ,CAAC;AACpD,QAAI,QAAQ,WAAW;AAAG;AAG1B,UAAM,QAAQ;AAAA,MACZ,QAAQ,IAAI,OAAO,CAAC,gBAAgB,cAAc,MAAM;AACtD,YAAI;AACF,gBAAM,eAAe,KAAK,cAAc,IAAI,cAAc;AAC1D,cAAI,CAAC;AAAc;AAEnB,gBAAM,SAAS,MAAM,aAAa,YAAY,EAAE,OAAO,gBAAgB,OAAO,IAAI,CAAC;AACnF,qBAAW,OAAO,QAAQ;AAExB,iBAAK,kBAAkB;AAAA,cACrB,MAAM,IAAI,kBAAkB,iBAAiB;AAAA,cAC7C;AAAA,cACA,SAAS,IAAI;AAAA,cACb,WAAW,IAAI;AAAA,cACf,MAAM;AAAA,cACN,WAAW,IAAI;AAAA,cACf,YAAY,IAAI;AAAA,YAClB,CAAC;AAAA,UACH;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,eAAgD;AAC5D,UAAM,QAAQ,MAAM,KAAK,OAAO,SAAS;AACzC,UAAM,UAAkC;AAAA,MACtC,gBAAgB;AAAA,MAChB,eAAe,UAAU,KAAK;AAAA,IAChC;AACA,QAAI,KAAK,OAAO,QAAQ;AACtB,cAAQ,gBAAgB,IAAI,KAAK,OAAO;AAAA,IAC1C;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,sBAAsB,MAAuC;AACnE,QAAI,iBAAiB,KAAK,gBAAgB;AAC1C,QAAI,CAAC,kBAAkB,KAAK,cAAc,GAAG;AAC3C,UAAI,OAAO,KAAK,cAAc;AAC9B,UAAI,OAAO,SAAS,UAAU;AAC5B,YAAI;AACF,iBAAO,KAAK,MAAM,IAAI;AAAA,QACxB,QAAQ;AAAA,QAER;AAAA,MACF;AACA,UAAI,QAAQ,OAAO,SAAS,YAAY,QAAQ,MAAM;AACpD,yBAAkB,KAAwB;AAAA,MAC5C;AAAA,IACF;AACA,QAAI,CAAC,gBAAgB;AACnB,YAAM,IAAI,MAAM,qCAAqC;AAAA,IACvD;AACA,WAAO;AAAA,EACT;AAAA,EAEQ,kBAAkB,gBAAsC;AAC9D,UAAM,WAAW,KAAK,cAAc,IAAI,cAAc;AACtD,QAAI;AAAU,aAAO;AACrB,UAAM,eAAe,IAAI,aAAa,gBAAgB,KAAK,QAAQ,IAAI;AACvE,SAAK,cAAc,IAAI,gBAAgB,YAAY;AACnD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,gBAAgB,gBAAsC;AACpD,WAAO,KAAK,kBAAkB,cAAc;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,iBACJ,UAKI,CAAC,GACkB;AACvB,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,iBAAiB,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,6BAA6B;AAAA,MACnF,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU;AAAA,QACnB,GAAI,QAAQ,QAAQ,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;AAAA,QAChD,GAAI,QAAQ,WAAW,EAAE,UAAU,QAAQ,SAAS,IAAI,CAAC;AAAA,MAC3D,CAAC;AAAA,IACH,CAAC;AAED,QAAI,CAAC,eAAe,IAAI;AACtB,YAAM,OAAO,MAAM,eAAe,KAAK;AACvC,YAAM,IAAI,MAAM,kCAAkC,eAAe,MAAM,IAAI,IAAI,EAAE;AAAA,IACnF;AAEA,UAAM,aAAc,MAAM,eAAe,KAAK;AAC9C,UAAM,iBAAiB,KAAK,sBAAsB,UAAU;AAE5D,QAAI,QAAQ,SAAS;AACnB,YAAM,iBAAiB,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,sBAAsB;AAAA,QAC5E,QAAQ;AAAA,QACR;AAAA,QACA,MAAM,KAAK,UAAU;AAAA,UACnB;AAAA,UACA,SAAS,QAAQ;AAAA,UACjB,GAAI,QAAQ,iBAAiB,EAAE,gBAAgB,QAAQ,eAAe,IAAI,CAAC;AAAA,QAC7E,CAAC;AAAA,MACH,CAAC;AAED,UAAI,CAAC,eAAe,IAAI;AACtB,cAAM,OAAO,MAAM,eAAe,KAAK;AACvC,cAAM,IAAI,MAAM,2BAA2B,eAAe,MAAM,IAAI,IAAI,EAAE;AAAA,MAC5E;AAAA,IACF;AAEA,WAAO,KAAK,kBAAkB,cAAc;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,cAAc,MAKM;AACxB,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,wBAAwB;AAAA,MACxE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,IAC3B,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,6BAA6B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACxE;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,UAAM,iBAAiB,KAAK,sBAAsB,IAAI;AACtD,WAAO,KAAK,kBAAkB,cAAc;AAAA,EAC9C;AAAA,EAEA,MAAM,YAAY,gBAA+C;AAC/D,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,sBAAsB;AAAA,MACtE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,eAAe,CAAC;AAAA,IACzC,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,2BAA2B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACtE;AAEA,WAAO,KAAK,kBAAkB,cAAc;AAAA,EAC9C;AAAA,EAEA,MAAM,aAAa,MAA8F;AAC/G,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,uBAAuB;AAAA,MACvE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,QAAQ,CAAC,CAAC;AAAA,IACjC,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,4BAA4B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACvE;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK,iBAAiB,CAAC;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,OAAO,QAAuC;AAClD,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,mBAAmB;AAAA,MACnE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,eAAe,OAAO,CAAC;AAAA,IAChD,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,sBAAsB,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACjE;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,UAAM,iBAAiB,KAAK,sBAAsB,IAAI;AACtD,WAAO,KAAK,kBAAkB,cAAc;AAAA,EAC9C;AAAA,EAEA,MAAM,YAAY,SAA0C;AAC1D,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,yBAAyB;AAAA,MACzE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,gBAAgB,QAAQ,CAAC;AAAA,IAClD,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,4BAA4B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACvE;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,UAAM,iBAAiB,KAAK,sBAAsB,IAAI;AACtD,WAAO,KAAK,kBAAkB,cAAc;AAAA,EAC9C;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,kBAAkB,MAMyD;AAC/E,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,4BAA4B;AAAA,MAC5E,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,QAAQ,CAAC,CAAC;AAAA,IACjC,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,iCAAiC,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IAC5E;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,EAAE,eAAe,KAAK,iBAAiB,CAAC,GAAG,qBAAqB,KAAK,oBAAoB;AAAA,EAClG;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,YAAY,MAAiE;AACjF,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,sBAAsB;AAAA,MACtE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,QAAQ,CAAC,CAAC;AAAA,IACjC,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,2BAA2B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACtE;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK,SAAS,CAAC;AAAA,EACxB;AAAA,EAEA,MAAM,YAAY,MAAkD;AAClE,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,sBAAsB;AAAA,MACtE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,QAAQ,CAAC,CAAC;AAAA,IACjC,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,2BAA2B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACtE;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK,WAAW,CAAC;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,aAAoE;AACxE,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,qBAAqB;AAAA,MACrE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,CAAC,CAAC;AAAA,IACzB,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,0BAA0B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACrE;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,EAAE,GAAI,KAAK,WAAW,CAAC,GAAI,mBAAmB,KAAK,kBAAkB;AAAA,EAC9E;AAAA,EAEA,MAAM,YAAY,oBAAoE;AACpF,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,sBAAsB;AAAA,MACtE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,mBAAmB,CAAC;AAAA,IAC7C,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,2BAA2B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACtE;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK,YAAY,CAAC;AAAA,EAC3B;AAAA,EAEA,MAAM,cAAc,SAKK;AACvB,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,wBAAwB;AAAA,MACxE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,OAAO;AAAA,IAC9B,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,6BAA6B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACxE;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK,WAAW,CAAC;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,cAAqC;AACzC,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,sBAAsB;AAAA,MACtE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,CAAC,CAAC;AAAA,IACzB,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,2BAA2B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACtE;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK,YAAY,CAAC;AAAA,EAC3B;AAAA,EAEA,MAAM,UAAU,UAAyC;AACvD,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,oBAAoB;AAAA,MACpE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,SAAS,CAAC;AAAA,IACnC,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,yBAAyB,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACpE;AAEA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAAA,EAEA,MAAM,iBAAiB,SAAwC;AAC7D,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,6BAA6B;AAAA,MAC7E,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,QAAQ,CAAC;AAAA,IAClC,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,kCAAkC,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IAC7E;AAEA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAAA,EAEA,MAAM,aAAa,OAAiD;AAClE,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,uBAAuB;AAAA,MACvE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,KAAK;AAAA,IAC5B,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,4BAA4B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACvE;AAEA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAAA,EAEA,MAAM,aAAa,OAAiD;AAClE,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,uBAAuB;AAAA,MACvE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,KAAK;AAAA,IAC5B,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,4BAA4B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACvE;AAEA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAAA,EAEA,MAAM,aAAa,UAAiC;AAClD,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,uBAAuB;AAAA,MACvE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,SAAS,CAAC;AAAA,IACnC,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,4BAA4B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACvE;AAAA,EACF;AAAA,EAEA,MAAM,gBAAgB,UAAkB,SAAyC;AAC/E,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,0BAA0B;AAAA,MAC1E,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,UAAU,QAAQ,CAAC;AAAA,IAC5C,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,+BAA+B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IAC1E;AAEA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAAA,EAEA,MAAM,kBAAkB,UAAgD;AACtE,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,6BAA6B;AAAA,MAC7E,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,SAAS,CAAC;AAAA,IACnC,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,kCAAkC,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IAC7E;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK,YAAY,CAAC;AAAA,EAC3B;AAAA,EAEA,MAAM,iBAAiB,UAAkB,SAA+C;AACtF,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,4BAA4B;AAAA,MAC5E,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,UAAU,QAAQ,CAAC;AAAA,IAC5C,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,iCAAiC,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IAC5E;AAEA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAAA,EAEA,MAAM,oBAAoB,UAAkB,SAAwC;AAClF,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,+BAA+B;AAAA,MAC/E,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,UAAU,QAAQ,CAAC;AAAA,IAC5C,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,oCAAoC,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IAC/E;AAEA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAAA,EAEA,MAAM,iBACJ,UACA,UACsG;AACtG,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,4BAA4B;AAAA,MAC5E,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,UAAU,SAAS,CAAC;AAAA,IAC7C,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,iCAAiC,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IAC5E;AAEA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAM9B;AAAA,EAEA,MAAM,uBAAuB,QAAgB,aAAwE;AACnH,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,kCAAkC;AAAA,MAClF,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,QAAQ,YAAY,CAAC;AAAA,IAC9C,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,uCAAuC,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IAClF;AAEA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAAA,EAEA,MAAM,qBAAqB,OAAiE;AAC1F,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,gCAAgC;AAAA,MAChF,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,KAAK;AAAA,IAC5B,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,qCAAqC,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IAChF;AAEA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,WAAW,gBAAwB,MAAY,UAA8C;AACjG,UAAM,QAAQ,MAAM,KAAK,OAAO,SAAS;AACzC,UAAM,WAAW,IAAI,SAAS;AAC9B,aAAS,OAAO,QAAQ,MAAM,QAAQ;AACtC,aAAS,OAAO,kBAAkB,cAAc;AAEhD,UAAM,UAAkC;AAAA,MACtC,eAAe,UAAU,KAAK;AAAA,IAChC;AACA,QAAI,KAAK,OAAO,QAAQ;AACtB,cAAQ,gBAAgB,IAAI,KAAK,OAAO;AAAA,IAC1C;AAEA,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,qBAAqB;AAAA,MACrE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM;AAAA,IACR,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,0BAA0B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACrE;AAEA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAuBA,MAAM,WACJ,QACA,MAC+F;AAC/F,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,sBAAsB;AAAA,MACtE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,QAAQ,GAAI,MAAM,YAAY,EAAE,WAAW,KAAK,UAAU,IAAI,CAAC,EAAG,CAAC;AAAA,IAC5F,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,2BAA2B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACtE;AAEA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAO9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBA,MAAM,wBACJ,oBAAoB,UACqE;AAEzF,QAAI,OAAO,WAAW,eAAe,EAAE,mBAAmB,cAAc,EAAE,iBAAiB,SAAS;AAClG,aAAO,EAAE,YAAY,OAAO,QAAQ,wDAAwD;AAAA,IAC9F;AAGA,UAAM,WAAW,MAAM,KAAK,kBAAkB;AAC9C,QAAI,CAAC,UAAU;AACb,aAAO,EAAE,YAAY,OAAO,QAAQ,0DAA0D;AAAA,IAChG;AAGA,UAAM,aAAa,MAAM,aAAa,kBAAkB;AACxD,QAAI,eAAe,WAAW;AAC5B,aAAO,EAAE,YAAY,OAAO,QAAQ,2BAA2B,UAAU,IAAI;AAAA,IAC/E;AAGA,UAAM,eAAe,MAAM,UAAU,cAAc,SAAS,iBAAiB;AAC7E,UAAM,UAAU,cAAc;AAG9B,UAAM,uBAAuB,sBAAsB,QAAQ;AAC3D,UAAM,eAAe,MAAM,aAAa,YAAY,UAAU;AAAA,MAC5D,iBAAiB;AAAA,MACjB;AAAA,IACF,CAAC;AAGD,UAAM,UAAU,aAAa,OAAO;AACpC,UAAM,KAAK,yBAAyB,OAA+B;AAEnE,WAAO,EAAE,YAAY,MAAM,UAAU,aAAa,SAAS;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,2BAA0C;AAC9C,QAAI,OAAO,WAAW,eAAe,EAAE,mBAAmB;AAAY;AAEtE,UAAM,eAAe,MAAM,UAAU,cAAc,gBAAgB;AACnE,QAAI,CAAC;AAAc;AAEnB,UAAM,eAAe,MAAM,aAAa,YAAY,gBAAgB;AACpE,QAAI,CAAC;AAAc;AAGnB,UAAM,aAAa,YAAY;AAG/B,UAAM,KAAK,2BAA2B,aAAa,QAAQ;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,2BAAgF;AACpF,QAAI,OAAO,WAAW,eAAe,EAAE,mBAAmB,YAAY;AACpE,aAAO,EAAE,YAAY,MAAM;AAAA,IAC7B;AAEA,UAAM,eAAe,MAAM,UAAU,cAAc,gBAAgB;AACnE,QAAI,CAAC;AAAc,aAAO,EAAE,YAAY,MAAM;AAE9C,UAAM,eAAe,MAAM,aAAa,YAAY,gBAAgB;AACpE,QAAI,CAAC;AAAc,aAAO,EAAE,YAAY,MAAM;AAE9C,WAAO,EAAE,YAAY,MAAM,UAAU,aAAa,SAAS;AAAA,EAC7D;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,oBAA4C;AAChD,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,uBAAuB;AAAA,MACvE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,CAAC,CAAC;AAAA,IACzB,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,UAAI,SAAS,WAAW;AAAK,eAAO;AACpC,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,4BAA4B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACvE;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK,kBAAkB;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,yBAAyB,cAAmD;AAChF,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,oCAAoC;AAAA,MACpF,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,aAAa,CAAC;AAAA,IACvC,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,yCAAyC,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACpF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,2BAA2B,UAAiC;AAChE,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,sCAAsC;AAAA,MACtF,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,EAAE,SAAS,CAAC;AAAA,IACnC,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,2CAA2C,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACtF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,mBAA4E;AAChF,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,wBAAwB;AAAA,MACxE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,CAAC,CAAC;AAAA,IACzB,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,iCAAiC,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IAC5E;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,EAAE,aAAa,KAAK,eAAe,GAAG,eAAe,KAAK,iBAAiB,EAAE;AAAA,EACtF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAM,aAAa,MAA4D;AAC7E,UAAM,UAAU,MAAM,KAAK,aAAa;AAExC,UAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,MAAM,oBAAoB;AAAA,MACpE,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,QAAQ,CAAC,CAAC;AAAA,IACjC,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK;AACjC,YAAM,IAAI,MAAM,6BAA6B,SAAS,MAAM,IAAI,IAAI,EAAE;AAAA,IACxE;AAEA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,KAAK,SAAS,CAAC;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA,EAMA,kBAAkB,gBAA8B;AAC9C,UAAM,eAAe,KAAK,cAAc,IAAI,cAAc;AAC1D,QAAI,cAAc;AAChB,mBAAa,WAAW;AACxB,WAAK,cAAc,OAAO,cAAc;AAAA,IAC1C;AAEA,SAAK,sBAAsB,OAAO,cAAc;AAAA,EAClD;AAAA,EAEA,UAAgB;AACd,SAAK,WAAW;AAChB,eAAW,CAAC,EAAE,KAAK,KAAK,eAAe;AACrC,WAAK,cAAc,OAAO,EAAE;AAAA,IAC9B;AACA,SAAK,sBAAsB,MAAM;AACjC,SAAK,gBAAgB,MAAM;AAC3B,SAAK,eAAe,MAAM;AAC1B,SAAK,eAAe,CAAC;AACrB,SAAK,kBAAkB,MAAM;AAC7B,SAAK,qBAAqB;AAAA,EAC5B;AACF;AAUA,SAAS,sBAAsB,cAAkC;AAC/D,QAAM,UAAU,IAAI,QAAQ,IAAK,aAAa,SAAS,KAAM,CAAC;AAC9D,QAAM,UAAU,eAAe,SAAS,QAAQ,MAAM,GAAG,EAAE,QAAQ,MAAM,GAAG;AAC5E,QAAM,UAAU,KAAK,MAAM;AAC3B,QAAM,cAAc,IAAI,WAAW,QAAQ,MAAM;AACjD,WAAS,IAAI,GAAG,IAAI,QAAQ,QAAQ,EAAE,GAAG;AACvC,gBAAY,CAAC,IAAI,QAAQ,WAAW,CAAC;AAAA,EACvC;AACA,SAAO;AACT;;;ACz3BO,SAAS,eAAe,OAAgE;AAC7F,SAAO,MAAM,SAAS,aAAa,MAAM,QAAQ,QAAQ,QAAQ,MAAM,QAAQ,UAAU,MAAM;AACjG;AAGO,SAAS,yBACd,OAC+F;AAC/F,SAAO,MAAM,SAAS,wBAAwB,MAAM,QAAQ,QAAQ,uBAAuB,MAAM;AACnG;AAGO,SAAS,uBACd,OACgF;AAChF,SAAO,MAAM,SAAS,sBAAsB,MAAM,QAAQ,QAAQ,uBAAuB,MAAM;AACjG;AAGO,SAAS,gBACd,OACwF;AACxF,SAAO,MAAM,SAAS,oBAAoB,MAAM,QAAQ,QAAQ,WAAW,MAAM;AACnF;",
4
+ "sourcesContent": ["/**\n * Conversation\n *\n * Represents an active conversation. Events are received via the\n * parent GigabuddyChat's user stream \u2014 filtered by conversationId.\n * connect()/disconnect() are no-ops (kept for backward compat).\n */\n\nimport type {\n ChatEventBus,\n ChannelEvent,\n ConversationEventMap,\n GigabuddyChatConfig,\n Message,\n MessageContent,\n ThreadSummary,\n} from './types.js';\n\nexport class Conversation {\n readonly conversationId: string;\n private config: GigabuddyChatConfig;\n private eventBus: ChatEventBus | null;\n private unsubscribes: (() => void)[] = [];\n\n constructor(conversationId: string, config: GigabuddyChatConfig, eventBus?: ChatEventBus) {\n this.conversationId = conversationId;\n this.config = config;\n this.eventBus = eventBus ?? null;\n }\n\n private async buildHeaders(): Promise<Record<string, string>> {\n const token = await this.config.getToken();\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n };\n if (this.config.userId) {\n headers['X-Chat-User-Id'] = this.config.userId;\n }\n return headers;\n }\n\n // ===========================================================================\n // Messages\n // ===========================================================================\n\n async sendMessage(content: string | MessageContent): Promise<Message> {\n const headers = await this.buildHeaders();\n\n const body: Record<string, unknown> = {\n conversationId: this.conversationId,\n };\n\n if (typeof content === 'string') {\n body['text'] = content;\n } else {\n body['content'] = content;\n }\n\n const response = await fetch(`${this.config.apiUrl}/chat/send-message`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const responseBody = await response.text();\n throw new Error(`Failed to send message: ${response.status} ${responseBody}`);\n }\n\n const data = (await response.json()) as { message?: Message };\n return data.message!;\n }\n\n async replyTo(messageId: string, content: string | MessageContent): Promise<void> {\n const headers = await this.buildHeaders();\n\n const body: Record<string, unknown> = {\n conversationId: this.conversationId,\n parentMessageId: messageId,\n };\n\n if (typeof content === 'string') {\n body['text'] = content;\n } else {\n body['content'] = content;\n }\n\n const response = await fetch(`${this.config.apiUrl}/chat/reply-to-message`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const responseBody = await response.text();\n throw new Error(`Failed to reply to message: ${response.status} ${responseBody}`);\n }\n }\n\n async editMessage(messageId: string, content: string | MessageContent): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/edit-message`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n messageId,\n content,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to edit message: ${response.status} ${body}`);\n }\n }\n\n async deleteMessage(messageId: string): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/delete-message`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n messageId,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to delete message: ${response.status} ${body}`);\n }\n }\n\n async sendEphemeralMessage(targetUserId: string, content: string | MessageContent): Promise<void> {\n const headers = await this.buildHeaders();\n\n const body: Record<string, unknown> = {\n conversationId: this.conversationId,\n targetUserId,\n };\n\n if (typeof content === 'string') {\n body['text'] = content;\n } else {\n body['content'] = content;\n }\n\n const response = await fetch(`${this.config.apiUrl}/chat/send-ephemeral-message`, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const responseBody = await response.text();\n throw new Error(`Failed to send ephemeral message: ${response.status} ${responseBody}`);\n }\n }\n\n // ===========================================================================\n // Reactions\n // ===========================================================================\n\n async addReaction(messageId: string, emoji: string): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/add-reaction`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n messageId,\n emoji,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to add reaction: ${response.status} ${body}`);\n }\n }\n\n async removeReaction(messageId: string, emoji: string): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/remove-reaction`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n messageId,\n emoji,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to remove reaction: ${response.status} ${body}`);\n }\n }\n\n // ===========================================================================\n // Read state & metadata\n // ===========================================================================\n\n async markRead(messageId?: string): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/mark-read`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n ...(messageId ? { messageId } : {}),\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to mark read: ${response.status} ${body}`);\n }\n }\n\n async markThreadRead(parentMessageId: string, messageId?: string): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/mark-thread-read`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n parentMessageId,\n ...(messageId ? { messageId } : {}),\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to mark thread read: ${response.status} ${body}`);\n }\n }\n\n async getThreadSummaries(opts?: { limit?: number; before?: string }): Promise<ThreadSummary[]> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/get-thread-summaries`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n ...(opts?.limit ? { limit: opts.limit } : {}),\n ...(opts?.before ? { before: opts.before } : {}),\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get thread summaries: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { threads?: ThreadSummary[] };\n return data.threads ?? [];\n }\n\n async sendTyping(opts?: { parentMessageId?: string }): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/send-typing`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n ...(opts?.parentMessageId ? { parentMessageId: opts.parentMessageId } : {}),\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to send typing: ${response.status} ${body}`);\n }\n }\n\n async updateTopic(topic: string): Promise<void> {\n return this.updateChannel({ topic });\n }\n\n async updateChannel(fields: {\n name?: string;\n description?: string;\n topic?: string;\n pictureUrl?: string;\n }): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/update-channel`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n ...fields,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to update channel: ${response.status} ${body}`);\n }\n }\n\n // ===========================================================================\n // Metadata\n // ===========================================================================\n\n /**\n * Set app-specific metadata on this conversation.\n * Useful for providing buddy context, game state, or any application data.\n *\n * @param metadata - Key-value pairs to set\n * @param opts.merge - If true (default), shallow-merges into existing metadata. If false, replaces entirely.\n */\n async setMetadata(metadata: Record<string, unknown>, opts?: { merge?: boolean }): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/set-metadata`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n metadata,\n merge: opts?.merge ?? true,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to set metadata: ${response.status} ${body}`);\n }\n }\n\n // ===========================================================================\n // Participants\n // ===========================================================================\n\n async addParticipant(userId: string): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/invite-to-channel`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n userId,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to add participant: ${response.status} ${body}`);\n }\n }\n\n async removeParticipant(userId: string): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/remove-participant`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n userId,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to remove participant: ${response.status} ${body}`);\n }\n }\n\n async inviteBuddy(buddyId: string, opts?: { buddyProjectId?: string }): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/invite-buddy`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n buddyId,\n ...(opts?.buddyProjectId ? { buddyProjectId: opts.buddyProjectId } : {}),\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to invite buddy: ${response.status} ${body}`);\n }\n }\n\n async removeBuddy(): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/remove-buddy`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to remove buddy: ${response.status} ${body}`);\n }\n }\n\n async leave(): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/leave-conversation`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to leave conversation: ${response.status} ${body}`);\n }\n }\n\n // ===========================================================================\n // Message history\n // ===========================================================================\n\n async getMessages(opts?: { limit?: number; before?: string; after?: string }): Promise<Message[]> {\n const headers = await this.buildHeaders();\n const response = await fetch(`${this.config.apiUrl}/chat/get-messages`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n ...(opts?.limit ? { limit: opts.limit } : {}),\n ...(opts?.before ? { before: opts.before } : {}),\n ...(opts?.after ? { after: opts.after } : {}),\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get messages: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { messages?: Message[] };\n return data.messages ?? [];\n }\n\n async getThreadReplies(parentMessageId: string, opts?: { limit?: number; before?: string }): Promise<Message[]> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/get-thread-replies`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n parentMessageId,\n ...(opts?.limit ? { limit: opts.limit } : {}),\n ...(opts?.before ? { before: opts.before } : {}),\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get thread replies: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { messages?: Message[] };\n return data.messages ?? [];\n }\n\n // ===========================================================================\n // Event listeners \u2014 route through parent user stream\n // ===========================================================================\n\n /**\n * Register an event handler filtered to this conversation.\n * Events are received from the parent GigabuddyChat's user stream.\n * Returns an unsubscribe function.\n */\n on<T extends keyof ConversationEventMap>(event: T, handler: ConversationEventMap[T]): () => void {\n if (this.eventBus) {\n const unsub = this.eventBus.addConversationListener(\n this.conversationId,\n event,\n handler as (...args: unknown[]) => void,\n );\n this.unsubscribes.push(unsub);\n return unsub;\n }\n // No event bus \u2014 listener is a no-op\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n return () => {};\n }\n\n // ===========================================================================\n // SSE lifecycle \u2014 no-ops (stream managed by GigabuddyChat)\n // ===========================================================================\n\n /** @deprecated No-op \u2014 SSE is managed by GigabuddyChat.connect() */\n async connect(): Promise<void> {\n // No-op: user stream is managed at the GigabuddyChat level\n }\n\n /** @deprecated No-op \u2014 SSE is managed by GigabuddyChat.disconnect() */\n disconnect(): void {\n // Clean up any registered listeners\n for (const unsub of this.unsubscribes) {\n unsub();\n }\n this.unsubscribes = [];\n }\n}\n", "/**\n * SSE Client\n *\n * Uses fetch() + ReadableStream instead of native EventSource\n * to support Authorization headers. Handles reconnection with\n * exponential backoff.\n */\n\nimport type { ChannelEvent, ChannelEventType } from './types.js';\n\nexport type SSEEventHandler = (event: ChannelEvent) => void;\nexport type SSEConnectionHandler = () => void;\n\ninterface SSEClientConfig {\n url: string;\n getToken: () => Promise<string> | string;\n userId?: string;\n onEvent: SSEEventHandler;\n onConnected: SSEConnectionHandler;\n onDisconnected: SSEConnectionHandler;\n}\n\nconst MAX_RECONNECT_ATTEMPTS = 10;\nconst INITIAL_BACKOFF_MS = 1_000;\nconst MAX_BACKOFF_MS = 30_000;\n\nexport class SSEClient {\n private config: SSEClientConfig;\n private abortController: AbortController | null = null;\n private reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n private reconnectAttempt = 0;\n private stopped = false;\n\n constructor(config: SSEClientConfig) {\n this.config = config;\n }\n\n async connect(): Promise<void> {\n this.stopped = false;\n this.reconnectAttempt = 0;\n await this.startStream();\n }\n\n disconnect(): void {\n this.stopped = true;\n if (this.reconnectTimer) {\n clearTimeout(this.reconnectTimer);\n this.reconnectTimer = null;\n }\n if (this.abortController) {\n this.abortController.abort();\n this.abortController = null;\n }\n }\n\n async updateToken(newToken: string): Promise<void> {\n // Store the new token getter for reconnections\n const originalGetToken = this.config.getToken;\n this.config.getToken = () => newToken;\n\n // Reconnect with new token\n if (this.abortController) {\n this.abortController.abort();\n this.abortController = null;\n this.reconnectAttempt = 0;\n await this.startStream();\n }\n\n // Restore the dynamic getter for future reconnections\n this.config.getToken = originalGetToken;\n }\n\n private async startStream(): Promise<void> {\n if (this.stopped) return;\n\n try {\n const token = await this.config.getToken();\n this.abortController = new AbortController();\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${token}`,\n Accept: 'text/event-stream',\n };\n if (this.config.userId) {\n headers['X-Chat-User-Id'] = this.config.userId;\n }\n\n const response = await fetch(this.config.url, {\n method: 'GET',\n headers,\n signal: this.abortController.signal,\n });\n\n if (!response.ok) {\n throw new Error(`SSE connection failed: ${response.status} ${response.statusText}`);\n }\n\n if (!response.body) {\n throw new Error('SSE response has no body');\n }\n\n this.reconnectAttempt = 0;\n // Read stream in background \u2014 don't await (readStream blocks until stream ends)\n this.readStream(response.body);\n } catch (error) {\n if (this.stopped) return;\n if (error instanceof DOMException && error.name === 'AbortError') return;\n\n this.config.onDisconnected();\n this.scheduleReconnect();\n }\n }\n\n private async readStream(body: ReadableStream<Uint8Array>): Promise<void> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n // Process complete SSE messages (terminated by \\n\\n)\n const messages = buffer.split('\\n\\n');\n buffer = messages.pop() ?? '';\n\n for (const message of messages) {\n this.parseSSEMessage(message);\n }\n }\n } catch (error) {\n if (this.stopped) return;\n if (error instanceof DOMException && error.name === 'AbortError') return;\n } finally {\n reader.releaseLock();\n }\n\n // Stream ended \u2014 reconnect if not explicitly stopped\n if (!this.stopped) {\n this.config.onDisconnected();\n this.scheduleReconnect();\n }\n }\n\n private parseSSEMessage(message: string): void {\n // Skip heartbeat comments\n if (message.trim().startsWith(':')) return;\n\n let eventType = 'message';\n let data = '';\n\n for (const line of message.split('\\n')) {\n if (line.startsWith('event: ')) {\n eventType = line.slice(7).trim();\n } else if (line.startsWith('data: ')) {\n data = line.slice(6);\n } else if (line.startsWith('data:')) {\n data = line.slice(5);\n }\n }\n\n if (eventType === 'connected') {\n this.config.onConnected();\n return;\n }\n\n if (!data) return;\n\n try {\n const parsed = JSON.parse(data) as ChannelEvent;\n // Ensure type field matches event type\n if (!parsed.type) {\n parsed.type = eventType as ChannelEventType;\n }\n this.config.onEvent(parsed);\n } catch {\n // Ignore malformed events\n }\n }\n\n private scheduleReconnect(): void {\n if (this.stopped) return;\n if (this.reconnectAttempt >= MAX_RECONNECT_ATTEMPTS) return;\n\n const backoff = Math.min(INITIAL_BACKOFF_MS * Math.pow(2, this.reconnectAttempt), MAX_BACKOFF_MS);\n this.reconnectAttempt++;\n\n this.reconnectTimer = setTimeout(() => {\n this.reconnectTimer = null;\n this.startStream();\n }, backoff);\n }\n}\n", "/**\n * GigabuddyChat\n *\n * Main entry point for the Chat SDK. Manages a single user-level SSE\n * connection and routes events to conversation-level listeners.\n */\n\nimport { Conversation } from './Conversation.js';\nimport { SSEClient } from './SSEClient.js';\nimport type {\n BuddyInfo,\n ChatEventBus,\n ChatUser,\n ChannelEvent,\n ConversationInfo,\n ConversationType,\n ConversationVisibility,\n CreateGadgetInput,\n CreateStatefulGadgetInput,\n FileUploadResult,\n GadgetDetail,\n GadgetInfo,\n GadgetVersionDetail,\n GadgetVersionInfo,\n GigabuddyChatConfig,\n GlobalEventMap,\n StatefulGadgetResult,\n StatefulGadgetSpec,\n UpdateGadgetInput,\n UserProfile,\n UserStat,\n} from './types.js';\n\nexport class GigabuddyChat implements ChatEventBus {\n private config: GigabuddyChatConfig;\n private conversations = new Map<string, Conversation>();\n\n // User stream SSE\n private sseClient: SSEClient | null = null;\n private tokenRefreshTimer: ReturnType<typeof setInterval> | null = null;\n private _connected = false;\n\n // Global event listeners (across all conversations)\n private globalListeners = new Map<string, Set<(...args: unknown[]) => void>>();\n\n // Per-conversation event listeners\n private conversationListeners = new Map<string, Map<string, Set<(...args: unknown[]) => void>>>();\n\n // SSE event dedup \u2014 rolling window of recent eventIds to prevent duplicates\n private recentEventIds = new Set<string>();\n private eventIdQueue: string[] = [];\n private static readonly MAX_DEDUP_WINDOW = 200;\n\n // Per-conversation last seen message ID \u2014 used for catch-up on reconnect\n private lastSeenMessageId = new Map<string, string>();\n private hasConnectedBefore = false;\n\n // Derived URL parts\n private baseUrl: string;\n private streamBaseUrl: string;\n private projectId: string;\n\n constructor(config: GigabuddyChatConfig) {\n this.config = config;\n const url = new URL(config.apiUrl);\n this.baseUrl = url.origin;\n this.streamBaseUrl =\n config.streamUrl ??\n (url.hostname.startsWith('api.') ? `${url.protocol}//${url.hostname.replace(/^api\\./, 'stream.')}` : url.origin);\n const pathParts = url.pathname.split('/').filter(Boolean);\n this.projectId = pathParts[pathParts.length - 1];\n }\n\n // ===========================================================================\n // User stream connection\n // ===========================================================================\n\n /**\n * Connect the user stream SSE. Call once after auth.\n */\n connect(): void {\n if (this.sseClient) return;\n\n this.sseClient = new SSEClient({\n url: `${this.streamBaseUrl}/ext/stream/user?projectId=${encodeURIComponent(this.projectId)}`,\n getToken: this.config.getToken,\n userId: this.config.userId,\n onEvent: (event: ChannelEvent) => this.handleStreamEvent(event),\n onConnected: () => {\n this._connected = true;\n if (this.hasConnectedBefore) {\n void this.catchUpMissedMessages();\n }\n this.hasConnectedBefore = true;\n this.emitGlobal('connected');\n },\n onDisconnected: () => {\n this._connected = false;\n this.emitGlobal('disconnected');\n },\n });\n\n void this.sseClient.connect();\n\n // Refresh token every 50 minutes\n this.tokenRefreshTimer = setInterval(\n async () => {\n if (this.sseClient) {\n const newToken = await this.config.getToken();\n await this.sseClient.updateToken(newToken);\n }\n },\n 50 * 60 * 1_000,\n );\n }\n\n /**\n * Returns true if the user stream is connected.\n */\n isConnected(): boolean {\n return this._connected;\n }\n\n /**\n * Disconnect the user stream and clean up all listeners.\n */\n disconnect(): void {\n if (this.tokenRefreshTimer) {\n clearInterval(this.tokenRefreshTimer);\n this.tokenRefreshTimer = null;\n }\n if (this.sseClient) {\n this.sseClient.disconnect();\n this.sseClient = null;\n }\n this._connected = false;\n }\n\n // ===========================================================================\n // Global event listeners\n // ===========================================================================\n\n /**\n * Listen for events across all conversations.\n * Returns an unsubscribe function.\n */\n on<T extends keyof GlobalEventMap>(event: T, handler: GlobalEventMap[T]): () => void {\n if (!this.globalListeners.has(event)) {\n this.globalListeners.set(event, new Set());\n }\n this.globalListeners.get(event)!.add(handler as (...args: unknown[]) => void);\n return () => {\n this.globalListeners.get(event)?.delete(handler as (...args: unknown[]) => void);\n };\n }\n\n /**\n * Remove a global event listener.\n */\n off<T extends keyof GlobalEventMap>(event: T, handler: GlobalEventMap[T]): void {\n this.globalListeners.get(event)?.delete(handler as (...args: unknown[]) => void);\n }\n\n // ===========================================================================\n // ChatEventBus: per-conversation listeners (used by Conversation)\n // ===========================================================================\n\n addConversationListener(conversationId: string, event: string, handler: (...args: unknown[]) => void): () => void {\n if (!this.conversationListeners.has(conversationId)) {\n this.conversationListeners.set(conversationId, new Map());\n }\n const convMap = this.conversationListeners.get(conversationId)!;\n if (!convMap.has(event)) {\n convMap.set(event, new Set());\n }\n convMap.get(event)!.add(handler);\n\n return () => {\n convMap.get(event)?.delete(handler);\n };\n }\n\n // ===========================================================================\n // Internal event routing\n // ===========================================================================\n\n private handleStreamEvent(event: ChannelEvent): void {\n // Dedup: skip events we've already seen\n const dedupKey = event.eventId ?? event.messageId;\n if (dedupKey) {\n if (this.recentEventIds.has(dedupKey)) return;\n this.recentEventIds.add(dedupKey);\n this.eventIdQueue.push(dedupKey);\n if (this.eventIdQueue.length > GigabuddyChat.MAX_DEDUP_WINDOW) {\n const oldest = this.eventIdQueue.shift()!;\n this.recentEventIds.delete(oldest);\n }\n }\n\n const eventType = event.type;\n const conversationId = event.conversationId;\n\n // Track last seen message ID per conversation for reconnect catch-up\n if (conversationId && event.messageId && (eventType === 'message' || eventType === 'thread_reply')) {\n this.lastSeenMessageId.set(conversationId, event.messageId);\n }\n\n // Dispatch to global listeners\n this.emitGlobal(eventType, event);\n\n // Dispatch to conversation-specific listeners\n if (conversationId) {\n const convMap = this.conversationListeners.get(conversationId);\n if (convMap) {\n const handlers = convMap.get(eventType);\n if (handlers) {\n for (const handler of handlers) {\n try {\n handler(event);\n } catch {\n // Don't let listener errors break the stream\n }\n }\n }\n }\n }\n }\n\n private emitGlobal(event: string, ...args: unknown[]): void {\n const handlers = this.globalListeners.get(event);\n if (handlers) {\n for (const handler of handlers) {\n try {\n handler(...args);\n } catch {\n // Don't let listener errors break the stream\n }\n }\n }\n }\n\n // ===========================================================================\n // Reconnect catch-up\n // ===========================================================================\n\n /**\n * After an SSE reconnect, fetch any messages we missed per conversation\n * and replay them through the normal event pipeline.\n */\n private async catchUpMissedMessages(): Promise<void> {\n const entries = [...this.lastSeenMessageId.entries()];\n if (entries.length === 0) return;\n\n // Catch up each conversation in parallel\n await Promise.allSettled(\n entries.map(async ([conversationId, afterMessageId]) => {\n try {\n const conversation = this.conversations.get(conversationId);\n if (!conversation) return;\n\n const missed = await conversation.getMessages({ after: afterMessageId, limit: 100 });\n for (const msg of missed) {\n // Synthesize a ChannelEvent and route through normal pipeline (dedup will prevent doubles)\n this.handleStreamEvent({\n type: msg.parentMessageId ? 'thread_reply' : 'message',\n conversationId,\n eventId: msg.id,\n timestamp: msg.createdAt,\n data: msg as unknown as Record<string, unknown>,\n messageId: msg.id,\n senderName: msg.senderName,\n });\n }\n } catch {\n // Silently fail \u2014 better to miss catch-up than crash\n }\n }),\n );\n }\n\n // ===========================================================================\n // API helpers\n // ===========================================================================\n\n private async buildHeaders(): Promise<Record<string, string>> {\n const token = await this.config.getToken();\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n };\n if (this.config.userId) {\n headers['X-Chat-User-Id'] = this.config.userId;\n }\n return headers;\n }\n\n private extractConversationId(data: Record<string, unknown>): string {\n let conversationId = data['conversationId'] as string | undefined;\n if (!conversationId && data['conversation']) {\n let conv = data['conversation'];\n if (typeof conv === 'string') {\n try {\n conv = JSON.parse(conv);\n } catch {\n // not JSON\n }\n }\n if (conv && typeof conv === 'object' && 'id' in conv) {\n conversationId = (conv as { id: string }).id;\n }\n }\n if (!conversationId) {\n throw new Error('No conversationId returned from API');\n }\n return conversationId;\n }\n\n private trackConversation(conversationId: string): Conversation {\n const existing = this.conversations.get(conversationId);\n if (existing) return existing;\n const conversation = new Conversation(conversationId, this.config, this);\n this.conversations.set(conversationId, conversation);\n return conversation;\n }\n\n /**\n * Get or create a Conversation handle for a known conversationId.\n * Does not make any API calls \u2014 just returns a local wrapper.\n */\n getConversation(conversationId: string): Conversation {\n return this.trackConversation(conversationId);\n }\n\n // ===========================================================================\n // Conversation creation (v1 compat)\n // ===========================================================================\n\n async openConversation(\n options: {\n topic?: string;\n buddyId?: string;\n buddyProjectId?: string;\n userName?: string;\n } = {},\n ): Promise<Conversation> {\n const headers = await this.buildHeaders();\n\n const createResponse = await fetch(`${this.config.apiUrl}/chat/create-conversation`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n ...(options.topic ? { topic: options.topic } : {}),\n ...(options.userName ? { userName: options.userName } : {}),\n }),\n });\n\n if (!createResponse.ok) {\n const body = await createResponse.text();\n throw new Error(`Failed to create conversation: ${createResponse.status} ${body}`);\n }\n\n const createData = (await createResponse.json()) as Record<string, unknown>;\n const conversationId = this.extractConversationId(createData);\n\n if (options.buddyId) {\n const attachResponse = await fetch(`${this.config.apiUrl}/chat/attach-buddy`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId,\n buddyId: options.buddyId,\n ...(options.buddyProjectId ? { buddyProjectId: options.buddyProjectId } : {}),\n }),\n });\n\n if (!attachResponse.ok) {\n const body = await attachResponse.text();\n throw new Error(`Failed to attach buddy: ${attachResponse.status} ${body}`);\n }\n }\n\n return this.trackConversation(conversationId);\n }\n\n // ===========================================================================\n // v2: Channel management\n // ===========================================================================\n\n async createChannel(opts: {\n name: string;\n visibility?: ConversationVisibility;\n description?: string;\n topic?: string;\n }): Promise<Conversation> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/create-channel`, {\n method: 'POST',\n headers,\n body: JSON.stringify(opts),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to create channel: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as Record<string, unknown>;\n const conversationId = this.extractConversationId(data);\n return this.trackConversation(conversationId);\n }\n\n async joinChannel(conversationId: string): Promise<Conversation> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/join-channel`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ conversationId }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to join channel: ${response.status} ${body}`);\n }\n\n return this.trackConversation(conversationId);\n }\n\n async listChannels(opts?: { visibility?: ConversationVisibility; search?: string }): Promise<ConversationInfo[]> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/list-channels`, {\n method: 'POST',\n headers,\n body: JSON.stringify(opts ?? {}),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to list channels: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { conversations?: ConversationInfo[] };\n return data.conversations ?? [];\n }\n\n // ===========================================================================\n // v2: DMs\n // ===========================================================================\n\n async openDM(userId: string): Promise<Conversation> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/create-dm`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ participantId: userId }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to open DM: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as Record<string, unknown>;\n const conversationId = this.extractConversationId(data);\n return this.trackConversation(conversationId);\n }\n\n async openGroupDM(userIds: string[]): Promise<Conversation> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/create-group-dm`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ participantIds: userIds }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to open group DM: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as Record<string, unknown>;\n const conversationId = this.extractConversationId(data);\n return this.trackConversation(conversationId);\n }\n\n // ===========================================================================\n // v2: List conversations (all types)\n // ===========================================================================\n\n async listConversations(opts?: {\n type?: ConversationType;\n status?: string;\n visibility?: ConversationVisibility;\n limit?: number;\n offset?: number;\n }): Promise<{ conversations: ConversationInfo[]; myContextInstanceId?: string }> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/list-conversations`, {\n method: 'POST',\n headers,\n body: JSON.stringify(opts ?? {}),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to list conversations: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { conversations?: ConversationInfo[]; myContextInstanceId?: string };\n return { conversations: data.conversations ?? [], myContextInstanceId: data.myContextInstanceId };\n }\n\n // ===========================================================================\n // User search\n // ===========================================================================\n\n async searchUsers(opts?: { search?: string; limit?: number }): Promise<ChatUser[]> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/search-users`, {\n method: 'POST',\n headers,\n body: JSON.stringify(opts ?? {}),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to search users: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { users?: ChatUser[] };\n return data.users ?? [];\n }\n\n async listBuddies(opts?: { search?: string }): Promise<BuddyInfo[]> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/list-buddies`, {\n method: 'POST',\n headers,\n body: JSON.stringify(opts ?? {}),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to list buddies: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { buddies?: BuddyInfo[] };\n return data.buddies ?? [];\n }\n\n // ===========================================================================\n // User profile\n // ===========================================================================\n\n async getProfile(): Promise<UserProfile & { contextInstanceId?: string }> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/get-profile`, {\n method: 'POST',\n headers,\n body: JSON.stringify({}),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get profile: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { profile?: UserProfile; contextInstanceId?: string };\n return { ...(data.profile ?? {}), contextInstanceId: data.contextInstanceId };\n }\n\n async getProfiles(contextInstanceIds: string[]): Promise<Record<string, UserProfile>> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/get-profiles`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ contextInstanceIds }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get profiles: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { profiles?: Record<string, UserProfile> };\n return data.profiles ?? {};\n }\n\n async updateProfile(profile: {\n name?: string;\n avatarUrl?: string;\n handle?: string;\n notificationPreference?: 'all' | 'mentions' | 'nothing';\n }): Promise<UserProfile> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/update-profile`, {\n method: 'POST',\n headers,\n body: JSON.stringify(profile),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to update profile: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { profile?: UserProfile };\n return data.profile ?? {};\n }\n\n // ===========================================================================\n // Gadget management\n // ===========================================================================\n\n async listGadgets(): Promise<GadgetInfo[]> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/list-gadgets`, {\n method: 'POST',\n headers,\n body: JSON.stringify({}),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to list gadgets: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { entities?: GadgetInfo[] };\n return data.entities ?? [];\n }\n\n async getGadget(gadgetId: string): Promise<GadgetDetail> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/get-gadget`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ gadgetId }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get gadget: ${response.status} ${body}`);\n }\n\n return (await response.json()) as GadgetDetail;\n }\n\n async getGadgetByShare(shareId: string): Promise<GadgetDetail> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/get-gadget-by-share`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ shareId }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get gadget by share: ${response.status} ${body}`);\n }\n\n return (await response.json()) as GadgetDetail;\n }\n\n async createGadget(input: CreateGadgetInput): Promise<GadgetDetail> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/create-gadget`, {\n method: 'POST',\n headers,\n body: JSON.stringify(input),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to create gadget: ${response.status} ${body}`);\n }\n\n return (await response.json()) as GadgetDetail;\n }\n\n async updateGadget(input: UpdateGadgetInput): Promise<GadgetDetail> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/update-gadget`, {\n method: 'POST',\n headers,\n body: JSON.stringify(input),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to update gadget: ${response.status} ${body}`);\n }\n\n return (await response.json()) as GadgetDetail;\n }\n\n async deleteGadget(gadgetId: string): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/delete-gadget`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ gadgetId }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to delete gadget: ${response.status} ${body}`);\n }\n }\n\n async duplicateGadget(gadgetId: string, newName?: string): Promise<GadgetDetail> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/duplicate-gadget`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ gadgetId, newName }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to duplicate gadget: ${response.status} ${body}`);\n }\n\n return (await response.json()) as GadgetDetail;\n }\n\n async getGadgetVersions(gadgetId: string): Promise<GadgetVersionInfo[]> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/get-gadget-versions`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ gadgetId }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get gadget versions: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { versions?: GadgetVersionInfo[] };\n return data.versions ?? [];\n }\n\n async getGadgetVersion(gadgetId: string, version: number): Promise<GadgetVersionDetail> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/get-gadget-version`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ gadgetId, version }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get gadget version: ${response.status} ${body}`);\n }\n\n return (await response.json()) as GadgetVersionDetail;\n }\n\n async revertGadgetVersion(gadgetId: string, version: number): Promise<GadgetDetail> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/revert-gadget-version`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ gadgetId, version }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to revert gadget version: ${response.status} ${body}`);\n }\n\n return (await response.json()) as GadgetDetail;\n }\n\n async refineGadgetCode(\n gadgetId: string,\n feedback: string,\n ): Promise<{ componentCode: string; reducerCode?: string; commitMessage: string; changeType?: string }> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/refine-gadget-code`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ gadgetId, feedback }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to refine gadget code: ${response.status} ${body}`);\n }\n\n return (await response.json()) as {\n componentCode: string;\n reducerCode?: string;\n commitMessage: string;\n changeType?: string;\n };\n }\n\n async generateStatefulGadget(intent: string, currentSpec?: Partial<StatefulGadgetSpec>): Promise<StatefulGadgetSpec> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/generate-stateful-gadget`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ intent, currentSpec }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to generate stateful gadget: ${response.status} ${body}`);\n }\n\n return (await response.json()) as StatefulGadgetSpec;\n }\n\n async createStatefulGadget(input: CreateStatefulGadgetInput): Promise<StatefulGadgetResult> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/create-stateful-gadget`, {\n method: 'POST',\n headers,\n body: JSON.stringify(input),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to create stateful gadget: ${response.status} ${body}`);\n }\n\n return (await response.json()) as StatefulGadgetResult;\n }\n\n // ===========================================================================\n // File uploads\n // ===========================================================================\n\n /**\n * Upload a file for use in chat messages.\n * Returns metadata that can be passed as `FileContent` to `conversation.sendMessage()`.\n */\n async uploadFile(conversationId: string, file: Blob, filename?: string): Promise<FileUploadResult> {\n const token = await this.config.getToken();\n const formData = new FormData();\n formData.append('file', file, filename);\n formData.append('conversationId', conversationId);\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${token}`,\n };\n if (this.config.userId) {\n headers['X-Chat-User-Id'] = this.config.userId;\n }\n\n const response = await fetch(`${this.config.apiUrl}/chat/upload-file`, {\n method: 'POST',\n headers,\n body: formData,\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to upload file: ${response.status} ${body}`);\n }\n\n return (await response.json()) as FileUploadResult;\n }\n\n // ===========================================================================\n // File access\n // ===========================================================================\n\n /**\n * Get a displayable URL for a file by its ID.\n *\n * Returns a public URL (permanent) or a signed URL (temporary, default 1 hour).\n * Use this to resolve file references in messages to URLs that can be displayed\n * in `<img>` tags or opened in the browser.\n *\n * @param fileId - The file ID (from FileContent.fileId or tool output)\n * @param opts.expiresIn - Signed URL duration in seconds (default: 3600, max: 86400)\n * @returns Object with url and file metadata\n *\n * @example\n * ```typescript\n * const file = await chat.getFileUrl('abc123');\n * // file.url is ready to use in <img src={file.url} />\n * ```\n */\n async getFileUrl(\n fileId: string,\n opts?: { expiresIn?: number },\n ): Promise<{ url: string; name: string; contentType: string; size: number; expiresAt?: string }> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/get-file-url`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ fileId, ...(opts?.expiresIn ? { expiresIn: opts.expiresIn } : {}) }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get file URL: ${response.status} ${body}`);\n }\n\n return (await response.json()) as {\n url: string;\n name: string;\n contentType: string;\n size: number;\n expiresAt?: string;\n };\n }\n\n // ===========================================================================\n // Push notifications\n // ===========================================================================\n\n /**\n * Enable push notifications for the current user.\n *\n * Handles the entire browser Push API flow in one call:\n * 1. Checks browser support\n * 2. Registers the service worker\n * 3. Fetches the VAPID public key from the server\n * 4. Requests notification permission from the user\n * 5. Subscribes via the browser Push API\n * 6. Sends the subscription to the server\n *\n * Call this from a user-initiated event (e.g. a \"Turn on notifications\" button).\n *\n * @param serviceWorkerPath - Path to your push service worker (default: '/sw.js')\n * @returns The push subscription state, or null if not supported/denied\n */\n async enablePushNotifications(\n serviceWorkerPath = '/sw.js',\n ): Promise<{ subscribed: true; endpoint: string } | { subscribed: false; reason: string }> {\n // 1. Check browser support\n if (typeof window === 'undefined' || !('serviceWorker' in navigator) || !('PushManager' in window)) {\n return { subscribed: false, reason: 'Push notifications are not supported in this browser.' };\n }\n\n // 2. Get VAPID public key from server\n const vapidKey = await this.getVapidPublicKey();\n if (!vapidKey) {\n return { subscribed: false, reason: 'Push notifications are not configured for this project.' };\n }\n\n // 3. Request notification permission\n const permission = await Notification.requestPermission();\n if (permission !== 'granted') {\n return { subscribed: false, reason: `Notification permission ${permission}.` };\n }\n\n // 4. Register service worker\n const registration = await navigator.serviceWorker.register(serviceWorkerPath);\n await navigator.serviceWorker.ready;\n\n // 5. Subscribe via browser Push API\n const applicationServerKey = urlBase64ToUint8Array(vapidKey);\n const subscription = await registration.pushManager.subscribe({\n userVisibleOnly: true,\n applicationServerKey: applicationServerKey as BufferSource,\n });\n\n // 6. Send subscription to server\n const subJson = subscription.toJSON();\n await this.registerPushSubscription(subJson as PushSubscriptionJSON);\n\n return { subscribed: true, endpoint: subscription.endpoint };\n }\n\n /**\n * Disable push notifications for the current user.\n *\n * Unsubscribes the browser and removes the subscription from the server.\n */\n async disablePushNotifications(): Promise<void> {\n if (typeof window === 'undefined' || !('serviceWorker' in navigator)) return;\n\n const registration = await navigator.serviceWorker.getRegistration();\n if (!registration) return;\n\n const subscription = await registration.pushManager.getSubscription();\n if (!subscription) return;\n\n // Unsubscribe browser\n await subscription.unsubscribe();\n\n // Remove from server\n await this.unregisterPushSubscription(subscription.endpoint);\n }\n\n /**\n * Check if the current user has an active push subscription.\n */\n async getPushNotificationState(): Promise<{ subscribed: boolean; endpoint?: string }> {\n if (typeof window === 'undefined' || !('serviceWorker' in navigator)) {\n return { subscribed: false };\n }\n\n const registration = await navigator.serviceWorker.getRegistration();\n if (!registration) return { subscribed: false };\n\n const subscription = await registration.pushManager.getSubscription();\n if (!subscription) return { subscribed: false };\n\n return { subscribed: true, endpoint: subscription.endpoint };\n }\n\n /**\n * Get the VAPID public key for subscribing to push notifications.\n * Returns null if push is not configured on the server.\n */\n async getVapidPublicKey(): Promise<string | null> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/get-vapid-key`, {\n method: 'POST',\n headers,\n body: JSON.stringify({}),\n });\n\n if (!response.ok) {\n if (response.status === 503) return null;\n const body = await response.text();\n throw new Error(`Failed to get VAPID key: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { vapidPublicKey?: string };\n return data.vapidPublicKey ?? null;\n }\n\n /**\n * Register a push subscription for the current user.\n * Prefer using enablePushNotifications() which handles the full flow.\n */\n async registerPushSubscription(subscription: PushSubscriptionJSON): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/register-push-subscription`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ subscription }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to register push subscription: ${response.status} ${body}`);\n }\n }\n\n /**\n * Unregister a push subscription by endpoint URL.\n * Prefer using disablePushNotifications() which handles the full flow.\n */\n async unregisterPushSubscription(endpoint: string): Promise<void> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/unregister-push-subscription`, {\n method: 'POST',\n headers,\n body: JSON.stringify({ endpoint }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to unregister push subscription: ${response.status} ${body}`);\n }\n }\n\n // ===========================================================================\n // Unread summary\n // ===========================================================================\n\n /**\n * Get unread message counts across all conversations for the current user.\n */\n async getUnreadSummary(): Promise<{ totalUnread: number; totalMentions: number }> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/unread-summary`, {\n method: 'POST',\n headers,\n body: JSON.stringify({}),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get unread summary: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { totalUnread?: number; totalMentions?: number };\n return { totalUnread: data.totalUnread ?? 0, totalMentions: data.totalMentions ?? 0 };\n }\n\n // ===========================================================================\n // User stats\n // ===========================================================================\n\n /**\n * Get per-user conversation statistics.\n * Optionally filter to a single user by contextInstanceId.\n */\n async getUserStats(opts?: { contextInstanceId?: string }): Promise<UserStat[]> {\n const headers = await this.buildHeaders();\n\n const response = await fetch(`${this.config.apiUrl}/chat/user-stats`, {\n method: 'POST',\n headers,\n body: JSON.stringify(opts ?? {}),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get user stats: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { users?: UserStat[] };\n return data.users ?? [];\n }\n\n // ===========================================================================\n // Lifecycle\n // ===========================================================================\n\n closeConversation(conversationId: string): void {\n const conversation = this.conversations.get(conversationId);\n if (conversation) {\n conversation.disconnect();\n this.conversations.delete(conversationId);\n }\n // Clean up conversation-specific listeners\n this.conversationListeners.delete(conversationId);\n }\n\n destroy(): void {\n this.disconnect();\n for (const [id] of this.conversations) {\n this.conversations.delete(id);\n }\n this.conversationListeners.clear();\n this.globalListeners.clear();\n this.recentEventIds.clear();\n this.eventIdQueue = [];\n this.lastSeenMessageId.clear();\n this.hasConnectedBefore = false;\n }\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\n/**\n * Convert a base64url-encoded string to a Uint8Array.\n * Used to convert VAPID public keys for the browser Push API.\n */\nfunction urlBase64ToUint8Array(base64String: string): Uint8Array {\n const padding = '='.repeat((4 - (base64String.length % 4)) % 4);\n const base64 = (base64String + padding).replace(/-/g, '+').replace(/_/g, '/');\n const rawData = atob(base64);\n const outputArray = new Uint8Array(rawData.length);\n for (let i = 0; i < rawData.length; ++i) {\n outputArray[i] = rawData.charCodeAt(i);\n }\n return outputArray;\n}\n", "/**\n * Chat SDK public type definitions.\n */\n\nexport interface GigabuddyChatConfig {\n /**\n * Customer project API URL.\n * e.g. 'https://api.gigabuddy.com/orgSlug/projectId'\n *\n * The SDK calls `/chat/<action>` under this URL. The server\n * resolves the target Conversations project from the project's\n * `gigabuddy-chat` connection config.\n */\n apiUrl: string;\n\n /**\n * Optional base URL for SSE streaming connections.\n * e.g. 'https://stream.gigabuddy.com'\n *\n * When set, SSE connections use this origin instead of the one\n * derived from `apiUrl`. Defaults to the origin of `apiUrl`.\n */\n streamUrl?: string;\n\n /** Return a valid auth token (API key or Firebase ID token). */\n getToken: () => Promise<string> | string;\n\n /**\n * Optional user identifier sent as `X-Chat-User-Id` header.\n * Used by the backend to scope conversations to a specific user\n * when authenticating with an API key.\n */\n userId?: string;\n}\n\n// =============================================================================\n// User profile\n// =============================================================================\n\nexport interface UserProfile {\n name?: string;\n email?: string;\n avatarUrl?: string;\n handle?: string;\n notificationPreference?: 'all' | 'mentions' | 'nothing';\n}\n\n// =============================================================================\n// v2 conversation types\n// =============================================================================\n\nexport type ConversationType = 'channel' | 'dm' | 'group_dm';\nexport type ConversationVisibility = 'public' | 'private';\nexport type MessageRole = 'member' | 'buddy' | 'operator' | 'system';\n\nexport interface FileContent {\n type: 'file';\n fileId: string;\n url: string;\n name: string;\n contentType: string;\n size: number;\n text?: string;\n}\n\n/** Rich message content \u2014 discriminated union */\nexport type MessageContent =\n | TextContent\n | GadgetContent\n | GadgetRefineContent\n | DataContent\n | SystemContent\n | CompositeContent\n | FileContent;\n\nexport interface TextContent {\n type: 'text';\n text: string;\n format?: 'plain' | 'markdown';\n}\n\nexport interface GadgetContent {\n type: 'gadget';\n gadgetRef: { type: 'gadget'; id: string };\n props?: Record<string, unknown>;\n renderHint?: 'inline' | 'modal' | 'replace';\n fallbackText?: string;\n}\n\nexport interface GadgetRefineContent {\n type: 'gadget_refine';\n gadgetRef: { type: string; id: string };\n feedback: string;\n text: string;\n}\n\nexport interface DataContent {\n type: 'data';\n key: string;\n value: unknown;\n displayAs?: string;\n}\n\nexport interface SystemContent {\n type: 'system';\n event: string;\n text: string;\n metadata?: Record<string, unknown>;\n}\n\nexport interface CompositeContent {\n type: 'composite';\n blocks: (TextContent | GadgetContent | DataContent | FileContent)[];\n}\n\nexport interface FileUploadResult {\n fileId: string;\n url: string;\n name: string;\n contentType: string;\n size: number;\n}\n\nexport interface MessageReaction {\n emoji: string;\n count: number;\n userIds: string[];\n}\n\nexport interface ConversationReadState {\n lastReadMessageId: string;\n lastReadAt: string;\n unreadCount: number;\n mentionCount: number;\n threadUnreadCount: number;\n}\n\nexport interface ConversationInfo {\n id: string;\n type?: ConversationType;\n visibility?: ConversationVisibility;\n name?: string;\n description?: string;\n topic?: string;\n pictureUrl?: string;\n status: string;\n participants: Array<{\n contextInstanceId: string;\n role: string;\n name?: string;\n }>;\n buddyRef?: { type: 'buddy'; id: string };\n messageCount: number;\n lastMessageAt?: string;\n lastMessagePreview?: string;\n readState?: ConversationReadState | null;\n metadata?: Record<string, unknown>;\n}\n\n// =============================================================================\n// Chat users\n// =============================================================================\n\nexport interface ChatUser {\n contextInstanceId: string;\n name?: string;\n email?: string;\n avatarUrl?: string;\n handle?: string;\n}\n\n// =============================================================================\n// Events\n// =============================================================================\n\nexport type ChannelEventType =\n | 'message'\n | 'gadget'\n | 'data'\n | 'instruction'\n | 'typing'\n | 'status'\n | 'error'\n | 'thread_reply'\n | 'reaction_added'\n | 'participant_joined'\n | 'participant_left'\n | 'message_updated'\n | 'message_deleted'\n | 'channel_created'\n | 'channel_updated'\n | 'channel_archived';\n\n/** User-stream-level event types (superset of channel events) */\nexport type UserStreamEventType =\n | ChannelEventType\n | 'conversation_added'\n | 'conversation_removed'\n | 'read_state_changed'\n | 'notification'\n | 'gadget_state_update';\n\n/** Data for gadget_state_update events (delivered via user stream) */\nexport interface GadgetStateUpdateEvent {\n type: 'gadget_state_update';\n deploymentId: string;\n gadgetId: string;\n missionStatus: string;\n sharedState: Record<string, unknown>;\n}\n\n/** Server-initiated notification delivered via the user stream. */\nexport interface ChatNotification {\n title: string;\n body: string;\n conversationId?: string;\n messageId?: string;\n type?: string;\n}\n\n/** Data for read_state_changed events */\nexport interface ReadStateChangedEvent {\n type: 'read_state_changed';\n conversationId: string;\n readState: ConversationReadState | null;\n}\n\n/** Data for conversation_added events */\nexport interface ConversationAddedEvent {\n type: 'conversation_added';\n conversationId: string;\n conversation: ConversationInfo;\n}\n\n/** Data for conversation_removed events */\nexport interface ConversationRemovedEvent {\n type: 'conversation_removed';\n conversationId: string;\n}\n\n/**\n * Event bus interface for routing user-stream events to conversations.\n * GigabuddyChat implements this; Conversation uses it to filter events.\n */\nexport interface ChatEventBus {\n addConversationListener(conversationId: string, event: string, handler: (...args: unknown[]) => void): () => void;\n}\n\nexport interface ChannelEvent {\n type: ChannelEventType;\n conversationId: string;\n eventId: string;\n timestamp: string;\n data: Record<string, unknown>;\n source?: string;\n messageId?: string;\n senderName?: string;\n}\n\nexport interface Message {\n id: string;\n role: MessageRole;\n content: string | MessageContent;\n senderContextInstanceId?: string;\n senderName?: string;\n createdAt: string;\n metadata?: Record<string, unknown>;\n parentMessageId?: string;\n threadCount?: number;\n lastThreadReplyAt?: string;\n editedAt?: string;\n reactions?: MessageReaction[];\n}\n\nexport interface ConversationEventMap {\n message: (event: ChannelEvent) => void;\n gadget: (event: ChannelEvent) => void;\n typing: (event: ChannelEvent) => void;\n status: (event: ChannelEvent) => void;\n error: (event: ChannelEvent) => void;\n connected: () => void;\n disconnected: () => void;\n // v2 events\n thread_reply: (event: ChannelEvent) => void;\n reaction_added: (event: ChannelEvent) => void;\n participant_joined: (event: ChannelEvent) => void;\n participant_left: (event: ChannelEvent) => void;\n message_updated: (event: ChannelEvent) => void;\n message_deleted: (event: ChannelEvent) => void;\n}\n\n/** Global event map \u2014 includes conversation events plus user-stream-only events. */\nexport interface GlobalEventMap extends ConversationEventMap {\n conversation_added: (event: ConversationAddedEvent) => void;\n conversation_removed: (event: ConversationRemovedEvent) => void;\n read_state_changed: (event: ReadStateChangedEvent) => void;\n notification: (event: ChatNotification) => void;\n gadget_state_update: (event: GadgetStateUpdateEvent) => void;\n channel_created: (event: ChannelEvent) => void;\n channel_updated: (event: ChannelEvent) => void;\n channel_archived: (event: ChannelEvent) => void;\n}\n\n// =============================================================================\n// Event type guards\n// =============================================================================\n\n/** Check if a ChannelEvent is a \"message\" event with Message data. */\nexport function isMessageEvent(event: ChannelEvent): event is ChannelEvent & { data: Message } {\n return event.type === 'message' && event.data != null && 'id' in event.data && 'role' in event.data;\n}\n\n/** Check if a ChannelEvent is a \"participant_joined\" event. */\nexport function isParticipantJoinedEvent(\n event: ChannelEvent,\n): event is ChannelEvent & { data: { contextInstanceId: string; name?: string; role?: string } } {\n return event.type === 'participant_joined' && event.data != null && 'contextInstanceId' in event.data;\n}\n\n/** Check if a ChannelEvent is a \"participant_left\" event. */\nexport function isParticipantLeftEvent(\n event: ChannelEvent,\n): event is ChannelEvent & { data: { contextInstanceId: string; name?: string } } {\n return event.type === 'participant_left' && event.data != null && 'contextInstanceId' in event.data;\n}\n\n/** Check if a ChannelEvent is a \"reaction_added\" event. */\nexport function isReactionEvent(\n event: ChannelEvent,\n): event is ChannelEvent & { data: { messageId: string; emoji: string; userId: string } } {\n return event.type === 'reaction_added' && event.data != null && 'emoji' in event.data;\n}\n\n// =============================================================================\n// Gadget management\n// =============================================================================\n\nexport interface GadgetInfo {\n id: string;\n name: string;\n description?: string;\n thumbnailUrl?: string;\n latestVersion: number;\n createdAt?: string;\n updatedAt?: string;\n}\n\nexport interface GadgetDetail extends GadgetInfo {\n data: {\n componentCode: string;\n componentLanguage?: 'javascript' | 'typescript';\n missionRef?: { type: string; id: string };\n actionRef?: { type: string; id: string };\n reducerCode?: string;\n stateBindings?: { shared?: { contextFields: string[] }; user?: { contextFields: string[] } };\n assets?: Record<string, { fileId: string; url: string; name: string; contentType: string }>;\n };\n /** Active share ID \u2014 enables context SDK via `/shared/:shareId/context/*` endpoints */\n shareId?: string;\n /** Whether the gadget's share has context SDK enabled */\n contextEnabled?: boolean;\n /** Whitelist of allowed context types (if contextEnabled) */\n contextTypes?: string[];\n /** Whether knowledge search is available via the share */\n knowledgeEnabled?: boolean;\n /** Source project ID (set for cross-project gadgets) */\n sourceProjectId?: string;\n}\n\nexport interface GadgetVersionInfo {\n version: number;\n name?: string;\n createdAt?: string;\n createdBy?: { userId: string; name?: string };\n commitMessage?: string;\n}\n\nexport interface GadgetVersionDetail extends GadgetVersionInfo {\n data: Record<string, unknown>;\n}\n\nexport interface CreateGadgetInput {\n name: string;\n description?: string;\n componentCode: string;\n}\n\nexport interface UpdateGadgetInput {\n gadgetId: string;\n name?: string;\n description?: string;\n componentCode?: string;\n reducerCode?: string;\n commitMessage?: string;\n}\n\n// =============================================================================\n// Stateful gadget types\n// =============================================================================\n\nexport interface StateFieldDefinition {\n name: string;\n type: 'string' | 'number' | 'boolean' | 'object' | 'array';\n scope: 'shared' | 'user';\n initialValue?: string | number | boolean;\n /** Who to notify when this field changes. Omit or false for silent updates. */\n notify?: 'participants' | 'user' | 'watchers' | false;\n /** Human-readable notification message (e.g. \"It's your turn!\") */\n notifyMessage?: string;\n}\n\nexport interface StatefulGadgetSpec {\n componentCode: string;\n reducerCode: string;\n stateFields: StateFieldDefinition[];\n name?: string;\n description?: string;\n}\n\nexport interface CreateStatefulGadgetInput {\n name: string;\n description?: string;\n componentCode: string;\n reducerCode: string;\n stateFields: StateFieldDefinition[];\n}\n\nexport interface StatefulGadgetResult {\n gadgetId: string;\n missionId: string;\n actionId: string;\n name: string;\n description?: string;\n latestVersion: number;\n data: GadgetDetail['data'];\n}\n\n// =============================================================================\n// User stats\n// =============================================================================\n\nexport interface UserStat {\n contextInstanceId: string;\n name: string;\n conversationCount: number;\n lastActiveAt: string | null;\n firstSeenAt: string | null;\n}\n\n// =============================================================================\n// Buddy info\n// =============================================================================\n\nexport interface BuddyInfo {\n contextInstanceId: string;\n name?: string;\n avatarUrl?: string;\n purpose?: string;\n}\n\n// =============================================================================\n// Thread summaries\n// =============================================================================\n\nexport interface ThreadSummary {\n parentMessage: Message;\n threadCount: number;\n lastThreadReplyAt: string;\n lastReplySnippet?: string;\n lastReplySenderName?: string;\n unreadCount: number;\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gigabuddy/chat-sdk",
3
- "version": "0.1.33",
3
+ "version": "0.1.35",
4
4
  "description": "Gigabuddy Chat SDK — embed AI-powered conversations in your app",
5
5
  "type": "module",
6
6
  "main": "./index.js",
package/src/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { GigabuddyChat } from './lib/GigabuddyChat.js';
2
2
  export { Conversation } from './lib/Conversation.js';
3
- export type { GigabuddyChatConfig, ChannelEvent, ChannelEventType, ConversationEventMap, GlobalEventMap, Message, UserProfile, ConversationType, ConversationVisibility, MessageRole, MessageContent, TextContent, GadgetContent, GadgetRefineContent, DataContent, SystemContent, CompositeContent, FileContent, FileUploadResult, MessageReaction, ConversationReadState, ConversationInfo, ChatUser, UserStreamEventType, ConversationAddedEvent, ConversationRemovedEvent, ChatEventBus, ChatNotification, GadgetStateUpdateEvent, GadgetInfo, GadgetDetail, GadgetVersionInfo, GadgetVersionDetail, CreateGadgetInput, UpdateGadgetInput, StateFieldDefinition, StatefulGadgetSpec, CreateStatefulGadgetInput, StatefulGadgetResult, UserStat, BuddyInfo, ThreadSummary, } from './lib/types.js';
3
+ export type { GigabuddyChatConfig, ChannelEvent, ChannelEventType, ConversationEventMap, GlobalEventMap, Message, UserProfile, ConversationType, ConversationVisibility, MessageRole, MessageContent, TextContent, GadgetContent, GadgetRefineContent, DataContent, SystemContent, CompositeContent, FileContent, FileUploadResult, MessageReaction, ConversationReadState, ConversationInfo, ChatUser, UserStreamEventType, ConversationAddedEvent, ConversationRemovedEvent, ReadStateChangedEvent, ChatEventBus, ChatNotification, GadgetStateUpdateEvent, GadgetInfo, GadgetDetail, GadgetVersionInfo, GadgetVersionDetail, CreateGadgetInput, UpdateGadgetInput, StateFieldDefinition, StatefulGadgetSpec, CreateStatefulGadgetInput, StatefulGadgetResult, UserStat, BuddyInfo, ThreadSummary, } from './lib/types.js';
4
4
  export { isMessageEvent, isParticipantJoinedEvent, isParticipantLeftEvent, isReactionEvent } from './lib/types.js';
@@ -141,7 +141,7 @@ export interface ChatUser {
141
141
  }
142
142
  export type ChannelEventType = 'message' | 'gadget' | 'data' | 'instruction' | 'typing' | 'status' | 'error' | 'thread_reply' | 'reaction_added' | 'participant_joined' | 'participant_left' | 'message_updated' | 'message_deleted' | 'channel_created' | 'channel_updated' | 'channel_archived';
143
143
  /** User-stream-level event types (superset of channel events) */
144
- export type UserStreamEventType = ChannelEventType | 'conversation_added' | 'conversation_removed' | 'notification' | 'gadget_state_update';
144
+ export type UserStreamEventType = ChannelEventType | 'conversation_added' | 'conversation_removed' | 'read_state_changed' | 'notification' | 'gadget_state_update';
145
145
  /** Data for gadget_state_update events (delivered via user stream) */
146
146
  export interface GadgetStateUpdateEvent {
147
147
  type: 'gadget_state_update';
@@ -158,6 +158,12 @@ export interface ChatNotification {
158
158
  messageId?: string;
159
159
  type?: string;
160
160
  }
161
+ /** Data for read_state_changed events */
162
+ export interface ReadStateChangedEvent {
163
+ type: 'read_state_changed';
164
+ conversationId: string;
165
+ readState: ConversationReadState | null;
166
+ }
161
167
  /** Data for conversation_added events */
162
168
  export interface ConversationAddedEvent {
163
169
  type: 'conversation_added';
@@ -219,6 +225,7 @@ export interface ConversationEventMap {
219
225
  export interface GlobalEventMap extends ConversationEventMap {
220
226
  conversation_added: (event: ConversationAddedEvent) => void;
221
227
  conversation_removed: (event: ConversationRemovedEvent) => void;
228
+ read_state_changed: (event: ReadStateChangedEvent) => void;
222
229
  notification: (event: ChatNotification) => void;
223
230
  gadget_state_update: (event: GadgetStateUpdateEvent) => void;
224
231
  channel_created: (event: ChannelEvent) => void;
@@ -331,6 +338,10 @@ export interface StateFieldDefinition {
331
338
  type: 'string' | 'number' | 'boolean' | 'object' | 'array';
332
339
  scope: 'shared' | 'user';
333
340
  initialValue?: string | number | boolean;
341
+ /** Who to notify when this field changes. Omit or false for silent updates. */
342
+ notify?: 'participants' | 'user' | 'watchers' | false;
343
+ /** Human-readable notification message (e.g. "It's your turn!") */
344
+ notifyMessage?: string;
334
345
  }
335
346
  export interface StatefulGadgetSpec {
336
347
  componentCode: string;