@gigabuddy/chat-sdk 0.1.26 → 0.1.28
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 +23 -2
- package/index.js.map +2 -2
- package/package.json +1 -1
- package/src/lib/Conversation.d.ts +1 -1
- package/src/lib/GigabuddyChat.d.ts +4 -0
- package/src/lib/types.d.ts +8 -0
package/index.js
CHANGED
|
@@ -42,6 +42,8 @@ var Conversation = class {
|
|
|
42
42
|
const responseBody = await response.text();
|
|
43
43
|
throw new Error(`Failed to send message: ${response.status} ${responseBody}`);
|
|
44
44
|
}
|
|
45
|
+
const data = await response.json();
|
|
46
|
+
return data.message;
|
|
45
47
|
}
|
|
46
48
|
async replyTo(messageId, content) {
|
|
47
49
|
const headers = await this.buildHeaders();
|
|
@@ -515,7 +517,7 @@ var SSEClient = class {
|
|
|
515
517
|
};
|
|
516
518
|
|
|
517
519
|
// libs/chat-sdk/src/lib/GigabuddyChat.ts
|
|
518
|
-
var GigabuddyChat = class {
|
|
520
|
+
var GigabuddyChat = class _GigabuddyChat {
|
|
519
521
|
config;
|
|
520
522
|
conversations = /* @__PURE__ */ new Map();
|
|
521
523
|
// User stream SSE
|
|
@@ -526,13 +528,19 @@ var GigabuddyChat = class {
|
|
|
526
528
|
globalListeners = /* @__PURE__ */ new Map();
|
|
527
529
|
// Per-conversation event listeners
|
|
528
530
|
conversationListeners = /* @__PURE__ */ new Map();
|
|
531
|
+
// SSE event dedup — rolling window of recent eventIds to prevent duplicates
|
|
532
|
+
recentEventIds = /* @__PURE__ */ new Set();
|
|
533
|
+
eventIdQueue = [];
|
|
534
|
+
static MAX_DEDUP_WINDOW = 200;
|
|
529
535
|
// Derived URL parts
|
|
530
536
|
baseUrl;
|
|
537
|
+
streamBaseUrl;
|
|
531
538
|
projectId;
|
|
532
539
|
constructor(config) {
|
|
533
540
|
this.config = config;
|
|
534
541
|
const url = new URL(config.apiUrl);
|
|
535
542
|
this.baseUrl = url.origin;
|
|
543
|
+
this.streamBaseUrl = config.streamUrl ?? url.origin;
|
|
536
544
|
const pathParts = url.pathname.split("/").filter(Boolean);
|
|
537
545
|
this.projectId = pathParts[pathParts.length - 1];
|
|
538
546
|
}
|
|
@@ -546,7 +554,7 @@ var GigabuddyChat = class {
|
|
|
546
554
|
if (this.sseClient)
|
|
547
555
|
return;
|
|
548
556
|
this.sseClient = new SSEClient({
|
|
549
|
-
url: `${this.
|
|
557
|
+
url: `${this.streamBaseUrl}/ext/stream/user?projectId=${encodeURIComponent(this.projectId)}`,
|
|
550
558
|
getToken: this.config.getToken,
|
|
551
559
|
userId: this.config.userId,
|
|
552
560
|
onEvent: (event) => this.handleStreamEvent(event),
|
|
@@ -632,6 +640,17 @@ var GigabuddyChat = class {
|
|
|
632
640
|
// Internal event routing
|
|
633
641
|
// ===========================================================================
|
|
634
642
|
handleStreamEvent(event) {
|
|
643
|
+
const dedupKey = event.eventId ?? event.messageId;
|
|
644
|
+
if (dedupKey) {
|
|
645
|
+
if (this.recentEventIds.has(dedupKey))
|
|
646
|
+
return;
|
|
647
|
+
this.recentEventIds.add(dedupKey);
|
|
648
|
+
this.eventIdQueue.push(dedupKey);
|
|
649
|
+
if (this.eventIdQueue.length > _GigabuddyChat.MAX_DEDUP_WINDOW) {
|
|
650
|
+
const oldest = this.eventIdQueue.shift();
|
|
651
|
+
this.recentEventIds.delete(oldest);
|
|
652
|
+
}
|
|
653
|
+
}
|
|
635
654
|
const eventType = event.type;
|
|
636
655
|
const conversationId = event.conversationId;
|
|
637
656
|
this.emitGlobal(eventType, event);
|
|
@@ -1290,6 +1309,8 @@ var GigabuddyChat = class {
|
|
|
1290
1309
|
}
|
|
1291
1310
|
this.conversationListeners.clear();
|
|
1292
1311
|
this.globalListeners.clear();
|
|
1312
|
+
this.recentEventIds.clear();
|
|
1313
|
+
this.eventIdQueue = [];
|
|
1293
1314
|
}
|
|
1294
1315
|
};
|
|
1295
1316
|
function urlBase64ToUint8Array(base64String) {
|
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} 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<void> {\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\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 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 // Derived URL parts\n private baseUrl: 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 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.baseUrl}/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 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 const eventType = event.type;\n const conversationId = event.conversationId;\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 // 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 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 // 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 }\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 /** 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)[];\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 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}\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"],
|
|
5
|
-
"mappings": "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",
|
|
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} 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 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 // 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 = config.streamUrl ?? 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 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 // 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 // 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 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 // 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 }\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)[];\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 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}\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"],
|
|
5
|
+
"mappings": ";AAiBO,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,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;;;AC5bA,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;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,QAA6B;AACvC,SAAK,SAAS;AACd,UAAM,MAAM,IAAI,IAAI,OAAO,MAAM;AACjC,SAAK,UAAU,IAAI;AACnB,SAAK,gBAAgB,OAAO,aAAa,IAAI;AAC7C,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,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;AAE7B,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,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,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,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;AAAA,EACvB;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;;;ACjwBO,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;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -13,7 +13,7 @@ export declare class Conversation {
|
|
|
13
13
|
private unsubscribes;
|
|
14
14
|
constructor(conversationId: string, config: GigabuddyChatConfig, eventBus?: ChatEventBus);
|
|
15
15
|
private buildHeaders;
|
|
16
|
-
sendMessage(content: string | MessageContent): Promise<
|
|
16
|
+
sendMessage(content: string | MessageContent): Promise<Message>;
|
|
17
17
|
replyTo(messageId: string, content: string | MessageContent): Promise<void>;
|
|
18
18
|
editMessage(messageId: string, content: string | MessageContent): Promise<void>;
|
|
19
19
|
deleteMessage(messageId: string): Promise<void>;
|
|
@@ -14,7 +14,11 @@ export declare class GigabuddyChat implements ChatEventBus {
|
|
|
14
14
|
private _connected;
|
|
15
15
|
private globalListeners;
|
|
16
16
|
private conversationListeners;
|
|
17
|
+
private recentEventIds;
|
|
18
|
+
private eventIdQueue;
|
|
19
|
+
private static readonly MAX_DEDUP_WINDOW;
|
|
17
20
|
private baseUrl;
|
|
21
|
+
private streamBaseUrl;
|
|
18
22
|
private projectId;
|
|
19
23
|
constructor(config: GigabuddyChatConfig);
|
|
20
24
|
/**
|
package/src/lib/types.d.ts
CHANGED
|
@@ -11,6 +11,14 @@ export interface GigabuddyChatConfig {
|
|
|
11
11
|
* `gigabuddy-chat` connection config.
|
|
12
12
|
*/
|
|
13
13
|
apiUrl: string;
|
|
14
|
+
/**
|
|
15
|
+
* Optional base URL for SSE streaming connections.
|
|
16
|
+
* e.g. 'https://stream.gigabuddy.com'
|
|
17
|
+
*
|
|
18
|
+
* When set, SSE connections use this origin instead of the one
|
|
19
|
+
* derived from `apiUrl`. Defaults to the origin of `apiUrl`.
|
|
20
|
+
*/
|
|
21
|
+
streamUrl?: string;
|
|
14
22
|
/** Return a valid auth token (API key or Firebase ID token). */
|
|
15
23
|
getToken: () => Promise<string> | string;
|
|
16
24
|
/**
|