@gigabuddy/chat-sdk 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -159,14 +159,22 @@ var Conversation = class {
159
159
  * Send a message to this conversation.
160
160
  * Calls the send-message action via the /chat/ proxy route.
161
161
  */
162
- async sendMessage(text) {
162
+ async buildHeaders() {
163
163
  const token = await this.config.getToken();
164
+ const headers = {
165
+ "Content-Type": "application/json",
166
+ Authorization: `Bearer ${token}`
167
+ };
168
+ if (this.config.userId) {
169
+ headers["X-Chat-User-Id"] = this.config.userId;
170
+ }
171
+ return headers;
172
+ }
173
+ async sendMessage(text) {
174
+ const headers = await this.buildHeaders();
164
175
  const response = await fetch(`${this.config.apiUrl}/chat/send-message`, {
165
176
  method: "POST",
166
- headers: {
167
- "Content-Type": "application/json",
168
- Authorization: `Bearer ${token}`
169
- },
177
+ headers,
170
178
  body: JSON.stringify({
171
179
  conversationId: this.conversationId,
172
180
  text
@@ -181,13 +189,10 @@ var Conversation = class {
181
189
  * Get messages for this conversation.
182
190
  */
183
191
  async getMessages(opts) {
184
- const token = await this.config.getToken();
192
+ const headers = await this.buildHeaders();
185
193
  const response = await fetch(`${this.config.apiUrl}/chat/get-messages`, {
186
194
  method: "POST",
187
- headers: {
188
- "Content-Type": "application/json",
189
- Authorization: `Bearer ${token}`
190
- },
195
+ headers,
191
196
  body: JSON.stringify({
192
197
  conversationId: this.conversationId,
193
198
  ...opts?.limit ? { limit: opts.limit } : {},
@@ -294,6 +299,9 @@ var GigabuddyChat = class {
294
299
  "Content-Type": "application/json",
295
300
  Authorization: `Bearer ${token}`
296
301
  };
302
+ if (this.config.userId) {
303
+ headers["X-Chat-User-Id"] = this.config.userId;
304
+ }
297
305
  const createResponse = await fetch(`${this.config.apiUrl}/chat/create-conversation`, {
298
306
  method: "POST",
299
307
  headers,
@@ -307,7 +315,19 @@ var GigabuddyChat = class {
307
315
  throw new Error(`Failed to create conversation: ${createResponse.status} ${body}`);
308
316
  }
309
317
  const createData = await createResponse.json();
310
- const conversationId = createData.conversationId ?? createData.conversation?.id;
318
+ let conversationId = createData["conversationId"];
319
+ if (!conversationId && createData["conversation"]) {
320
+ let conv = createData["conversation"];
321
+ if (typeof conv === "string") {
322
+ try {
323
+ conv = JSON.parse(conv);
324
+ } catch {
325
+ }
326
+ }
327
+ if (conv && typeof conv === "object" && "id" in conv) {
328
+ conversationId = conv.id;
329
+ }
330
+ }
311
331
  if (!conversationId) {
312
332
  throw new Error("No conversationId returned from create-conversation");
313
333
  }
package/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../libs/chat-sdk/src/lib/SSEClient.ts", "../../../libs/chat-sdk/src/lib/Conversation.ts", "../../../libs/chat-sdk/src/lib/GigabuddyChat.ts"],
4
- "sourcesContent": ["/**\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 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 response = await fetch(this.config.url, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: 'text/event-stream',\n },\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 await 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 * Conversation\n *\n * Represents an active conversation with realtime event streaming.\n * Wraps the SSE client and provides typed event handlers.\n */\n\nimport { SSEClient } from './SSEClient.js';\nimport type { ChannelEvent, ConversationEventMap, GigabuddyChatConfig, Message } from './types.js';\n\nexport class Conversation {\n readonly conversationId: string;\n private config: GigabuddyChatConfig;\n private baseUrl: string;\n private sseClient: SSEClient | null = null;\n private listeners = new Map<string, Set<(...args: unknown[]) => void>>();\n private tokenRefreshTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(conversationId: string, config: GigabuddyChatConfig) {\n this.conversationId = conversationId;\n this.config = config;\n // Derive base API URL by stripping the /:orgSlug/:projectId suffix\n // apiUrl = https://api.gigabuddy.com/orgSlug/projectId\n // baseUrl = https://api.gigabuddy.com\n const url = new URL(config.apiUrl);\n this.baseUrl = url.origin;\n }\n\n /**\n * Send a message to this conversation.\n * Calls the send-message action via the /chat/ proxy route.\n */\n async sendMessage(text: string): Promise<void> {\n const token = await this.config.getToken();\n const response = await fetch(`${this.config.apiUrl}/chat/send-message`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify({\n conversationId: this.conversationId,\n text,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to send message: ${response.status} ${body}`);\n }\n }\n\n /**\n * Get messages for this conversation.\n */\n async getMessages(opts?: { limit?: number; before?: string }): Promise<Message[]> {\n const token = await this.config.getToken();\n const response = await fetch(`${this.config.apiUrl}/chat/get-messages`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n },\n body: JSON.stringify({\n conversationId: this.conversationId,\n ...(opts?.limit ? { limit: opts.limit } : {}),\n ...(opts?.before ? { before: opts.before } : {}),\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to get messages: ${response.status} ${body}`);\n }\n\n const data = (await response.json()) as { messages?: Message[] };\n return data.messages ?? [];\n }\n\n /**\n * Register an event handler. Returns an unsubscribe function.\n */\n on<T extends keyof ConversationEventMap>(event: T, handler: ConversationEventMap[T]): () => void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n const handlers = this.listeners.get(event)!;\n handlers.add(handler as (...args: unknown[]) => void);\n\n return () => {\n handlers.delete(handler as (...args: unknown[]) => void);\n };\n }\n\n /**\n * Start the SSE stream for realtime events.\n */\n async connect(): Promise<void> {\n if (this.sseClient) return;\n\n this.sseClient = new SSEClient({\n url: `${this.baseUrl}/ext/stream/${this.conversationId}`,\n getToken: this.config.getToken,\n onEvent: (event: ChannelEvent) => this.handleEvent(event),\n onConnected: () => this.emit('connected'),\n onDisconnected: () => this.emit('disconnected'),\n });\n\n await this.sseClient.connect();\n\n // Refresh token every 50 minutes to keep the stream alive\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 * Close SSE stream and clean up.\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 }\n\n private handleEvent(event: ChannelEvent): void {\n // Dispatch to typed listeners\n const handlers = this.listeners.get(event.type);\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 private emit(event: 'connected' | 'disconnected'): void {\n const handlers = this.listeners.get(event);\n if (handlers) {\n for (const handler of handlers) {\n try {\n handler();\n } catch {\n // Don't let listener errors break the stream\n }\n }\n }\n }\n}\n", "/**\n * GigabuddyChat\n *\n * Main entry point for the Chat SDK. Manages conversation lifecycle.\n */\n\nimport { Conversation } from './Conversation.js';\nimport type { GigabuddyChatConfig } from './types.js';\n\nexport class GigabuddyChat {\n private config: GigabuddyChatConfig;\n private conversations = new Map<string, Conversation>();\n\n constructor(config: GigabuddyChatConfig) {\n this.config = config;\n }\n\n /**\n * Open a conversation.\n *\n * Creates a new conversation via the API and optionally attaches a buddy.\n * Returns a Conversation instance for sending messages and receiving events.\n */\n async openConversation(\n options: {\n topic?: string;\n buddyId?: string;\n buddyProjectId?: string;\n userName?: string;\n } = {},\n ): Promise<Conversation> {\n const token = await this.config.getToken();\n const headers = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n };\n\n // Create conversation\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 {\n conversationId?: string;\n conversation?: { id?: string };\n };\n const conversationId = createData.conversationId ?? createData.conversation?.id;\n if (!conversationId) {\n throw new Error('No conversationId returned from create-conversation');\n }\n\n // Attach buddy if requested\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 const conversation = new Conversation(conversationId, this.config);\n this.conversations.set(conversationId, conversation);\n return conversation;\n }\n\n /**\n * Close and clean up a conversation.\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 }\n\n /**\n * Close all conversations and clean up.\n */\n destroy(): void {\n for (const [id, conversation] of this.conversations) {\n conversation.disconnect();\n this.conversations.delete(id);\n }\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\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 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 response = await fetch(this.config.url, {\n method: 'GET',\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: 'text/event-stream',\n },\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 await 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 * Conversation\n *\n * Represents an active conversation with realtime event streaming.\n * Wraps the SSE client and provides typed event handlers.\n */\n\nimport { SSEClient } from './SSEClient.js';\nimport type { ChannelEvent, ConversationEventMap, GigabuddyChatConfig, Message } from './types.js';\n\nexport class Conversation {\n readonly conversationId: string;\n private config: GigabuddyChatConfig;\n private baseUrl: string;\n private sseClient: SSEClient | null = null;\n private listeners = new Map<string, Set<(...args: unknown[]) => void>>();\n private tokenRefreshTimer: ReturnType<typeof setInterval> | null = null;\n\n constructor(conversationId: string, config: GigabuddyChatConfig) {\n this.conversationId = conversationId;\n this.config = config;\n // Derive base API URL by stripping the /:orgSlug/:projectId suffix\n // apiUrl = https://api.gigabuddy.com/orgSlug/projectId\n // baseUrl = https://api.gigabuddy.com\n const url = new URL(config.apiUrl);\n this.baseUrl = url.origin;\n }\n\n /**\n * Send a message to this conversation.\n * Calls the send-message action via the /chat/ proxy route.\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 async sendMessage(text: string): Promise<void> {\n const headers = await this.buildHeaders();\n const response = await fetch(`${this.config.apiUrl}/chat/send-message`, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n conversationId: this.conversationId,\n text,\n }),\n });\n\n if (!response.ok) {\n const body = await response.text();\n throw new Error(`Failed to send message: ${response.status} ${body}`);\n }\n }\n\n /**\n * Get messages for this conversation.\n */\n async getMessages(opts?: { limit?: number; before?: 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 }),\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 /**\n * Register an event handler. Returns an unsubscribe function.\n */\n on<T extends keyof ConversationEventMap>(event: T, handler: ConversationEventMap[T]): () => void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n const handlers = this.listeners.get(event)!;\n handlers.add(handler as (...args: unknown[]) => void);\n\n return () => {\n handlers.delete(handler as (...args: unknown[]) => void);\n };\n }\n\n /**\n * Start the SSE stream for realtime events.\n */\n async connect(): Promise<void> {\n if (this.sseClient) return;\n\n this.sseClient = new SSEClient({\n url: `${this.baseUrl}/ext/stream/${this.conversationId}`,\n getToken: this.config.getToken,\n onEvent: (event: ChannelEvent) => this.handleEvent(event),\n onConnected: () => this.emit('connected'),\n onDisconnected: () => this.emit('disconnected'),\n });\n\n await this.sseClient.connect();\n\n // Refresh token every 50 minutes to keep the stream alive\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 * Close SSE stream and clean up.\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 }\n\n private handleEvent(event: ChannelEvent): void {\n // Dispatch to typed listeners\n const handlers = this.listeners.get(event.type);\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 private emit(event: 'connected' | 'disconnected'): void {\n const handlers = this.listeners.get(event);\n if (handlers) {\n for (const handler of handlers) {\n try {\n handler();\n } catch {\n // Don't let listener errors break the stream\n }\n }\n }\n }\n}\n", "/**\n * GigabuddyChat\n *\n * Main entry point for the Chat SDK. Manages conversation lifecycle.\n */\n\nimport { Conversation } from './Conversation.js';\nimport type { GigabuddyChatConfig } from './types.js';\n\nexport class GigabuddyChat {\n private config: GigabuddyChatConfig;\n private conversations = new Map<string, Conversation>();\n\n constructor(config: GigabuddyChatConfig) {\n this.config = config;\n }\n\n /**\n * Open a conversation.\n *\n * Creates a new conversation via the API and optionally attaches a buddy.\n * Returns a Conversation instance for sending messages and receiving events.\n */\n async openConversation(\n options: {\n topic?: string;\n buddyId?: string;\n buddyProjectId?: string;\n userName?: string;\n } = {},\n ): Promise<Conversation> {\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\n // Create conversation\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 let conversationId = createData['conversationId'] as string | undefined;\n if (!conversationId && createData['conversation']) {\n // The conversation field may be a parsed object or a stringified JSON string\n let conv = createData['conversation'];\n if (typeof conv === 'string') {\n try {\n conv = JSON.parse(conv);\n } catch {\n // not JSON \u2014 ignore\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 create-conversation');\n }\n\n // Attach buddy if requested\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 const conversation = new Conversation(conversationId, this.config);\n this.conversations.set(conversationId, conversation);\n return conversation;\n }\n\n /**\n * Close and clean up a conversation.\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 }\n\n /**\n * Close all conversations and clean up.\n */\n destroy(): void {\n for (const [id, conversation] of this.conversations) {\n conversation.disconnect();\n this.conversations.delete(id);\n }\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gigabuddy/chat-sdk",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Gigabuddy Chat SDK — embed AI-powered conversations in your app",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -17,6 +17,7 @@ export declare class Conversation {
17
17
  * Send a message to this conversation.
18
18
  * Calls the send-message action via the /chat/ proxy route.
19
19
  */
20
+ private buildHeaders;
20
21
  sendMessage(text: string): Promise<void>;
21
22
  /**
22
23
  * Get messages for this conversation.
@@ -13,6 +13,12 @@ export interface GigabuddyChatConfig {
13
13
  apiUrl: string;
14
14
  /** Return a valid auth token (API key or Firebase ID token). */
15
15
  getToken: () => Promise<string> | string;
16
+ /**
17
+ * Optional user identifier sent as `X-Chat-User-Id` header.
18
+ * Used by the backend to scope conversations to a specific user
19
+ * when authenticating with an API key.
20
+ */
21
+ userId?: string;
16
22
  }
17
23
  export type ChannelEventType = 'message' | 'widget' | 'data' | 'instruction' | 'typing' | 'status' | 'error';
18
24
  export interface ChannelEvent {