@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 +31 -11
- package/index.js.map +2 -2
- package/package.json +1 -1
- package/src/lib/Conversation.d.ts +1 -0
- package/src/lib/types.d.ts +6 -0
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
|
|
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
|
|
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
|
-
|
|
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
|
@@ -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.
|
package/src/lib/types.d.ts
CHANGED
|
@@ -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 {
|