open-chat-studio-widget 0.5.2 → 0.6.0

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.
Files changed (34) hide show
  1. package/README.md +1 -0
  2. package/dist/cjs/{index-Ctja7z-R.js → index-CcvroTR_.js} +3 -3
  3. package/dist/cjs/{index-Ctja7z-R.js.map → index-CcvroTR_.js.map} +1 -1
  4. package/dist/cjs/loader.cjs.js +2 -2
  5. package/dist/cjs/open-chat-studio-widget.cjs.entry.js +45 -11
  6. package/dist/cjs/open-chat-studio-widget.cjs.entry.js.map +1 -1
  7. package/dist/cjs/open-chat-studio-widget.cjs.js +2 -2
  8. package/dist/cjs/open-chat-studio-widget.entry.cjs.js.map +1 -1
  9. package/dist/collection/components/ocs-chat/ocs-chat.js +53 -3
  10. package/dist/collection/components/ocs-chat/ocs-chat.js.map +1 -1
  11. package/dist/collection/services/chat-session-service.js +14 -6
  12. package/dist/collection/services/chat-session-service.js.map +1 -1
  13. package/dist/collection/services/file-attachment-manager.js +2 -1
  14. package/dist/collection/services/file-attachment-manager.js.map +1 -1
  15. package/dist/components/open-chat-studio-widget.js +46 -10
  16. package/dist/components/open-chat-studio-widget.js.map +1 -1
  17. package/dist/esm/{index-BbCwiO7g.js → index-BKVXO_5E.js} +3 -3
  18. package/dist/esm/{index-BbCwiO7g.js.map → index-BKVXO_5E.js.map} +1 -1
  19. package/dist/esm/loader.js +3 -3
  20. package/dist/esm/open-chat-studio-widget.entry.js +45 -11
  21. package/dist/esm/open-chat-studio-widget.entry.js.map +1 -1
  22. package/dist/esm/open-chat-studio-widget.js +3 -3
  23. package/dist/open-chat-studio-widget/open-chat-studio-widget.entry.esm.js.map +1 -1
  24. package/dist/open-chat-studio-widget/open-chat-studio-widget.esm.js +1 -1
  25. package/dist/open-chat-studio-widget/{p-BbCwiO7g.js → p-BKVXO_5E.js} +2 -2
  26. package/dist/open-chat-studio-widget/{p-BbCwiO7g.js.map → p-BKVXO_5E.js.map} +1 -1
  27. package/dist/open-chat-studio-widget/p-a0d04423.entry.js +4 -0
  28. package/dist/open-chat-studio-widget/p-a0d04423.entry.js.map +1 -0
  29. package/dist/types/components/ocs-chat/ocs-chat.d.ts +12 -0
  30. package/dist/types/components.d.ts +8 -0
  31. package/dist/types/services/chat-session-service.d.ts +1 -0
  32. package/package.json +1 -1
  33. package/dist/open-chat-studio-widget/p-96920183.entry.js +0 -4
  34. package/dist/open-chat-studio-widget/p-96920183.entry.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"chat-session-service.js","sourceRoot":"","sources":["../../src/services/chat-session-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { getCSRFToken } from '../utils/cookies';\n\nexport type ChatRole = 'system' | 'user' | 'assistant';\n\nexport interface ChatAttachment {\n name: string;\n content_type: string;\n size: number;\n}\n\nexport interface ChatMessage {\n created_at: string;\n role: ChatRole;\n content: string;\n metadata?: unknown;\n attachments?: ChatAttachment[];\n}\n\nexport interface ChatStartSessionResponse {\n session_id: string;\n chatbot: unknown;\n participant: unknown;\n}\n\nexport interface ChatSendMessageResponse {\n task_id: string;\n status: 'processing' | 'completed' | 'error';\n error?: string;\n}\n\nexport interface ChatTaskPollResponse {\n message?: ChatMessage;\n status: 'processing' | 'complete';\n error?: string;\n}\n\nexport interface ChatPollResponse {\n messages: ChatMessage[];\n has_more: boolean;\n session_status: 'active' | 'ended';\n}\n\nexport interface ChatSessionServiceOptions {\n apiBaseUrl: string;\n embedKey?: string;\n widgetVersion: string;\n csrfTokenProvider?: (apiBaseUrl: string) => string | undefined;\n taskPollingIntervalMs?: number;\n taskPollingMaxAttempts?: number;\n messagePollingIntervalMs?: number;\n}\n\nexport interface TaskPollingCallbacks {\n onMessage: (message: ChatMessage) => void;\n onTimeout?: () => void;\n onError?: (error: Error) => void;\n}\n\nexport interface TaskPollingHandle {\n cancel: () => void;\n}\n\nexport interface MessagePollingCallbacks {\n getSince: () => string | undefined;\n onMessages: (messages: ChatMessage[]) => void;\n onError?: (error: Error) => void;\n}\n\nexport interface MessagePollingHandle {\n stop: () => void;\n}\n\nexport class ChatSessionService {\n private readonly apiBaseUrl: string;\n private readonly embedKey?: string;\n private readonly widgetVersion: string;\n private readonly csrfTokenProvider: (apiBaseUrl: string) => string | undefined;\n private readonly taskPollingIntervalMs: number;\n private readonly taskPollingMaxAttempts: number;\n private readonly messagePollingIntervalMs: number;\n private messagePollingTimer?: ReturnType<typeof setInterval>;\n\n constructor(options: ChatSessionServiceOptions) {\n this.apiBaseUrl = options.apiBaseUrl;\n this.embedKey = options.embedKey;\n this.widgetVersion = options.widgetVersion;\n this.csrfTokenProvider = options.csrfTokenProvider ?? getCSRFToken;\n this.taskPollingIntervalMs = options.taskPollingIntervalMs ?? 1000;\n this.taskPollingMaxAttempts = options.taskPollingMaxAttempts ?? 120;\n this.messagePollingIntervalMs = options.messagePollingIntervalMs ?? 30000;\n }\n\n async startSession(requestBody: Record<string, unknown>): Promise<ChatStartSessionResponse> {\n const response = await fetch(`${this.apiBaseUrl}/api/chat/start/`, {\n method: 'POST',\n headers: this.getJsonHeaders(),\n body: JSON.stringify(requestBody),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to start session: ${response.statusText}`);\n }\n\n return response.json() as Promise<ChatStartSessionResponse>;\n }\n\n async sendMessage(sessionId: string, payload: Record<string, unknown>): Promise<ChatSendMessageResponse> {\n const response = await fetch(`${this.apiBaseUrl}/api/chat/${sessionId}/message/`, {\n method: 'POST',\n headers: this.getJsonHeaders(),\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to send message: ${response.statusText}`);\n }\n\n return response.json() as Promise<ChatSendMessageResponse>;\n }\n\n async pollTaskOnce(sessionId: string, taskId: string): Promise<ChatTaskPollResponse> {\n const response = await fetch(`${this.apiBaseUrl}/api/chat/${sessionId}/${taskId}/poll/`);\n\n if (!response.ok) {\n throw new Error(`Failed to poll task: ${response.statusText}`);\n }\n\n return response.json() as Promise<ChatTaskPollResponse>;\n }\n\n pollTask(sessionId: string, taskId: string, callbacks: TaskPollingCallbacks): TaskPollingHandle {\n let attempts = 0;\n let cancelled = false;\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const scheduleNextPoll = () => {\n timeoutId = setTimeout(() => {\n void poll();\n }, this.taskPollingIntervalMs);\n };\n\n const poll = async () => {\n if (cancelled) {\n return;\n }\n\n try {\n const data = await this.pollTaskOnce(sessionId, taskId);\n\n if (data.error) {\n throw new Error(data.error);\n }\n\n if (data.status === 'complete' && data.message) {\n callbacks.onMessage(data.message);\n return;\n }\n\n attempts += 1;\n if (attempts >= this.taskPollingMaxAttempts) {\n if (callbacks.onTimeout) {\n callbacks.onTimeout();\n }\n return;\n }\n\n scheduleNextPoll();\n } catch (error) {\n if (callbacks.onError) {\n callbacks.onError(error instanceof Error ? error : new Error('Failed to get response'));\n }\n }\n };\n\n void poll();\n\n return {\n cancel: () => {\n cancelled = true;\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n },\n };\n }\n\n async fetchMessages(sessionId: string, since?: string): Promise<ChatPollResponse> {\n const url = new URL(`${this.apiBaseUrl}/api/chat/${sessionId}/poll/`);\n if (since) {\n url.searchParams.set('since', since);\n }\n\n const response = await fetch(url.toString());\n if (!response.ok) {\n throw new Error(`Failed to poll messages: ${response.statusText}`);\n }\n\n return response.json() as Promise<ChatPollResponse>;\n }\n\n startMessagePolling(\n sessionId: string,\n callbacks: MessagePollingCallbacks,\n ): MessagePollingHandle {\n const poll = async () => {\n try {\n const since = callbacks.getSince();\n const data = await this.fetchMessages(sessionId, since);\n if (data.messages.length > 0) {\n callbacks.onMessages(data.messages);\n }\n } catch (error) {\n if (callbacks.onError) {\n callbacks.onError(error instanceof Error ? error : new Error('Failed to poll messages'));\n }\n }\n };\n\n // perform an initial poll immediately\n void poll();\n\n this.messagePollingTimer = setInterval(() => {\n void poll();\n }, this.messagePollingIntervalMs);\n\n return {\n stop: () => this.stopMessagePolling(),\n };\n }\n\n stopMessagePolling(): void {\n if (this.messagePollingTimer) {\n clearInterval(this.messagePollingTimer);\n this.messagePollingTimer = undefined;\n }\n }\n\n private getJsonHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n 'x-ocs-widget-version': this.widgetVersion,\n };\n\n const csrfToken = this.csrfTokenProvider(this.apiBaseUrl);\n if (csrfToken) {\n headers['X-CSRFToken'] = csrfToken;\n }\n\n if (this.embedKey) {\n headers['X-Embed-Key'] = this.embedKey;\n }\n\n return headers;\n }\n}\n"]}
1
+ {"version":3,"file":"chat-session-service.js","sourceRoot":"","sources":["../../src/services/chat-session-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { getCSRFToken } from '../utils/cookies';\n\nexport type ChatRole = 'system' | 'user' | 'assistant';\n\nexport interface ChatAttachment {\n name: string;\n content_type: string;\n size: number;\n}\n\nexport interface ChatMessage {\n created_at: string;\n role: ChatRole;\n content: string;\n metadata?: unknown;\n attachments?: ChatAttachment[];\n}\n\nexport interface ChatStartSessionResponse {\n session_id: string;\n chatbot: unknown;\n participant: unknown;\n}\n\nexport interface ChatSendMessageResponse {\n task_id: string;\n status: 'processing' | 'completed' | 'error';\n error?: string;\n}\n\nexport interface ChatTaskPollResponse {\n message?: ChatMessage;\n status: 'processing' | 'complete';\n error?: string;\n}\n\nexport interface ChatPollResponse {\n messages: ChatMessage[];\n has_more: boolean;\n session_status: 'active' | 'ended';\n}\n\nexport interface ChatSessionServiceOptions {\n apiBaseUrl: string;\n embedKey?: string;\n widgetVersion: string;\n csrfTokenProvider?: (apiBaseUrl: string) => string | undefined;\n taskPollingIntervalMs?: number;\n taskPollingMaxAttempts?: number;\n messagePollingIntervalMs?: number;\n}\n\nexport interface TaskPollingCallbacks {\n onMessage: (message: ChatMessage) => void;\n onTimeout?: () => void;\n onError?: (error: Error) => void;\n}\n\nexport interface TaskPollingHandle {\n cancel: () => void;\n}\n\nexport interface MessagePollingCallbacks {\n getSince: () => string | undefined;\n onMessages: (messages: ChatMessage[]) => void;\n onError?: (error: Error) => void;\n}\n\nexport interface MessagePollingHandle {\n stop: () => void;\n}\n\nexport class ChatSessionService {\n private readonly apiBaseUrl: string;\n private readonly embedKey?: string;\n private readonly widgetVersion: string;\n private readonly csrfTokenProvider: (apiBaseUrl: string) => string | undefined;\n private readonly taskPollingIntervalMs: number;\n private readonly taskPollingMaxAttempts: number;\n private readonly messagePollingIntervalMs: number;\n private messagePollingTimer?: ReturnType<typeof setInterval>;\n\n constructor(options: ChatSessionServiceOptions) {\n this.apiBaseUrl = options.apiBaseUrl;\n this.embedKey = options.embedKey;\n this.widgetVersion = options.widgetVersion;\n this.csrfTokenProvider = options.csrfTokenProvider ?? getCSRFToken;\n this.taskPollingIntervalMs = options.taskPollingIntervalMs ?? 1000;\n this.taskPollingMaxAttempts = options.taskPollingMaxAttempts ?? 120;\n this.messagePollingIntervalMs = options.messagePollingIntervalMs ?? 30000;\n }\n\n async startSession(requestBody: Record<string, unknown>): Promise<ChatStartSessionResponse> {\n const response = await fetch(`${this.apiBaseUrl}/api/chat/start/`, {\n method: 'POST',\n headers: this.getJsonHeaders(),\n body: JSON.stringify(requestBody),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to start session: ${response.statusText}`);\n }\n\n return response.json() as Promise<ChatStartSessionResponse>;\n }\n\n async sendMessage(sessionId: string, payload: Record<string, unknown>): Promise<ChatSendMessageResponse> {\n const response = await fetch(`${this.apiBaseUrl}/api/chat/${sessionId}/message/`, {\n method: 'POST',\n headers: this.getJsonHeaders(),\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to send message: ${response.statusText}`);\n }\n\n return response.json() as Promise<ChatSendMessageResponse>;\n }\n\n async pollTaskOnce(sessionId: string, taskId: string): Promise<ChatTaskPollResponse> {\n const response = await fetch(`${this.apiBaseUrl}/api/chat/${sessionId}/${taskId}/poll/`, {\n headers: this.getCommonHeaders(),\n });\n\n if (!response.ok) {\n throw new Error(`Failed to poll task: ${response.statusText}`);\n }\n\n return response.json() as Promise<ChatTaskPollResponse>;\n }\n\n pollTask(sessionId: string, taskId: string, callbacks: TaskPollingCallbacks): TaskPollingHandle {\n let attempts = 0;\n let cancelled = false;\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const scheduleNextPoll = () => {\n timeoutId = setTimeout(() => {\n void poll();\n }, this.taskPollingIntervalMs);\n };\n\n const poll = async () => {\n if (cancelled) {\n return;\n }\n\n try {\n const data = await this.pollTaskOnce(sessionId, taskId);\n\n if (data.error) {\n throw new Error(data.error);\n }\n\n if (data.status === 'complete' && data.message) {\n callbacks.onMessage(data.message);\n return;\n }\n\n attempts += 1;\n if (attempts >= this.taskPollingMaxAttempts) {\n if (callbacks.onTimeout) {\n callbacks.onTimeout();\n }\n return;\n }\n\n scheduleNextPoll();\n } catch (error) {\n if (callbacks.onError) {\n callbacks.onError(error instanceof Error ? error : new Error('Failed to get response'));\n }\n }\n };\n\n void poll();\n\n return {\n cancel: () => {\n cancelled = true;\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n },\n };\n }\n\n async fetchMessages(sessionId: string, since?: string): Promise<ChatPollResponse> {\n const url = new URL(`${this.apiBaseUrl}/api/chat/${sessionId}/poll/`);\n if (since) {\n url.searchParams.set('since', since);\n }\n\n const response = await fetch(url.toString(), {\n headers: this.getCommonHeaders(),\n });\n if (!response.ok) {\n throw new Error(`Failed to poll messages: ${response.statusText}`);\n }\n\n return response.json() as Promise<ChatPollResponse>;\n }\n\n startMessagePolling(\n sessionId: string,\n callbacks: MessagePollingCallbacks,\n ): MessagePollingHandle {\n const poll = async () => {\n try {\n const since = callbacks.getSince();\n const data = await this.fetchMessages(sessionId, since);\n if (data.messages.length > 0) {\n callbacks.onMessages(data.messages);\n }\n } catch (error) {\n if (callbacks.onError) {\n callbacks.onError(error instanceof Error ? error : new Error('Failed to poll messages'));\n }\n }\n };\n\n // perform an initial poll immediately\n void poll();\n\n this.messagePollingTimer = setInterval(() => {\n void poll();\n }, this.messagePollingIntervalMs);\n\n return {\n stop: () => this.stopMessagePolling(),\n };\n }\n\n stopMessagePolling(): void {\n if (this.messagePollingTimer) {\n clearInterval(this.messagePollingTimer);\n this.messagePollingTimer = undefined;\n }\n }\n\n private getJsonHeaders(): Record<string, string> {\n const headers = this.getCommonHeaders();\n headers['Content-Type'] = 'application/json'\n\n const csrfToken = this.csrfTokenProvider(this.apiBaseUrl);\n if (csrfToken) {\n headers['X-CSRFToken'] = csrfToken;\n }\n\n return headers;\n }\n\n private getCommonHeaders(): Record<string, string> {\n const headers: Record<string, string> = {\n 'x-ocs-widget-version': this.widgetVersion,\n };\n\n if (this.embedKey) {\n headers['X-Embed-Key'] = this.embedKey;\n }\n\n return headers;\n }\n}\n"]}
@@ -10,7 +10,8 @@ export class FileAttachmentManager {
10
10
  let totalSize = existingFiles.reduce((sum, f) => sum + f.file.size, 0);
11
11
  for (const file of fileArray) {
12
12
  const extension = this.getFileExtension(file.name);
13
- if (!this.supportedExtensions.includes(extension)) {
13
+ const contentType = file.type.split("/")[0];
14
+ if (contentType != "text" && !this.supportedExtensions.includes(extension)) {
14
15
  newSelected.push({ file, error: `File type ${extension} not supported` });
15
16
  continue;
16
17
  }
@@ -1 +1 @@
1
- {"version":3,"file":"file-attachment-manager.js","sourceRoot":"","sources":["../../src/services/file-attachment-manager.ts"],"names":[],"mappings":"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","sourcesContent":["export interface UploadedFile {\n id: number;\n name: string;\n size: number;\n content_type: string;\n}\n\nexport interface SelectedFile {\n file: File;\n uploaded?: UploadedFile;\n error?: string;\n}\n\nexport interface AttachmentValidationConfig {\n supportedExtensions: string[];\n maxFileSizeMb: number;\n maxTotalSizeMb: number;\n}\n\nexport interface UploadContext {\n apiBaseUrl: string;\n sessionId: string;\n participantId: string;\n participantName?: string;\n}\n\nexport interface UploadResult {\n selectedFiles: SelectedFile[];\n uploadedIds: number[];\n errorMessage?: string;\n}\n\nexport class FileAttachmentManager {\n private readonly supportedExtensions: string[];\n private readonly maxFileSizeMb: number;\n private readonly maxTotalSizeMb: number;\n\n constructor(config: AttachmentValidationConfig) {\n this.supportedExtensions = config.supportedExtensions;\n this.maxFileSizeMb = config.maxFileSizeMb;\n this.maxTotalSizeMb = config.maxTotalSizeMb;\n }\n\n addFiles(existingFiles: SelectedFile[], files: FileList | File[]): SelectedFile[] {\n const newSelected: SelectedFile[] = [];\n const fileArray = Array.from(files instanceof FileList ? Array.from(files) : files);\n let totalSize = existingFiles.reduce((sum, f) => sum + f.file.size, 0);\n\n for (const file of fileArray) {\n const extension = this.getFileExtension(file.name);\n if (!this.supportedExtensions.includes(extension)) {\n newSelected.push({ file, error: `File type ${extension} not supported` });\n continue;\n }\n\n const fileSizeMb = this.bytesToMb(file.size);\n if (fileSizeMb > this.maxFileSizeMb) {\n newSelected.push({ file, error: `File exceeds ${this.maxFileSizeMb}MB limit` });\n continue;\n }\n\n totalSize += file.size;\n const totalSizeMb = this.bytesToMb(totalSize);\n if (totalSizeMb > this.maxTotalSizeMb) {\n newSelected.push({ file, error: `Total size exceeds ${this.maxTotalSizeMb}MB limit` });\n continue;\n }\n\n newSelected.push({ file });\n }\n\n return [...existingFiles, ...newSelected];\n }\n\n removeFile(existingFiles: SelectedFile[], index: number): SelectedFile[] {\n return existingFiles.filter((_, i) => i !== index);\n }\n\n markPendingFilesWithError(existingFiles: SelectedFile[], errorMessage: string): SelectedFile[] {\n return existingFiles.map(file => {\n if (!file.error && !file.uploaded) {\n return { ...file, error: errorMessage };\n }\n return file;\n });\n }\n\n async uploadPendingFiles(\n existingFiles: SelectedFile[],\n context: UploadContext\n ): Promise<UploadResult> {\n if (existingFiles.length === 0) {\n return { selectedFiles: existingFiles, uploadedIds: [] };\n }\n\n const uploadCandidates = existingFiles.filter(file => !file.error && !file.uploaded);\n const uploadedIds: number[] = existingFiles\n .filter(file => file.uploaded)\n .map(file => file.uploaded!.id);\n\n if (uploadCandidates.length === 0) {\n return { selectedFiles: existingFiles, uploadedIds };\n }\n\n const formData = new FormData();\n for (const file of uploadCandidates) {\n formData.append('files', file.file);\n }\n formData.append('participant_remote_id', context.participantId);\n if (context.participantName) {\n formData.append('participant_name', context.participantName);\n }\n\n try {\n const response = await fetch(`${context.apiBaseUrl}/api/chat/${context.sessionId}/upload/`, {\n method: 'POST',\n body: formData,\n });\n\n if (!response.ok) {\n const errorData = await this.safeJson(response);\n const errorMessage =\n (errorData && typeof errorData === 'object' && 'error' in errorData && (errorData as { error?: string }).error) ||\n 'Failed to upload files';\n return {\n selectedFiles: this.markPendingFilesWithError(existingFiles, errorMessage),\n uploadedIds,\n errorMessage,\n };\n }\n\n const data = await this.safeJson(response);\n if (!data || typeof data !== 'object' || !Array.isArray((data as { files?: unknown }).files)) {\n const errorMessage = 'Unexpected upload response shape';\n return {\n selectedFiles: this.markPendingFilesWithError(existingFiles, errorMessage),\n uploadedIds,\n errorMessage,\n };\n }\n\n const uploadedFiles = (data as { files: UploadedFile[] }).files;\n let index = 0;\n const updatedSelected = existingFiles.map(file => {\n if (!file.error && !file.uploaded) {\n const uploaded = uploadedFiles[index];\n index += 1;\n return { ...file, uploaded };\n }\n return file;\n });\n uploadedIds.push(...uploadedFiles.map(file => file.id));\n\n return { selectedFiles: updatedSelected, uploadedIds };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Failed to upload files';\n return {\n selectedFiles: this.markPendingFilesWithError(existingFiles, errorMessage),\n uploadedIds,\n errorMessage,\n };\n }\n }\n\n private bytesToMb(bytes: number): number {\n return bytes / (1024 * 1024);\n }\n\n private getFileExtension(filename: string): string {\n const parts = filename.split('.');\n const ext = parts.pop();\n return ext ? `.${ext.toLowerCase()}` : '';\n }\n\n private async safeJson(response: Response): Promise<unknown> {\n try {\n return await response.json();\n } catch {\n return undefined;\n }\n }\n}\n"]}
1
+ {"version":3,"file":"file-attachment-manager.js","sourceRoot":"","sources":["../../src/services/file-attachment-manager.ts"],"names":[],"mappings":"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","sourcesContent":["export interface UploadedFile {\n id: number;\n name: string;\n size: number;\n content_type: string;\n}\n\nexport interface SelectedFile {\n file: File;\n uploaded?: UploadedFile;\n error?: string;\n}\n\nexport interface AttachmentValidationConfig {\n supportedExtensions: string[];\n maxFileSizeMb: number;\n maxTotalSizeMb: number;\n}\n\nexport interface UploadContext {\n apiBaseUrl: string;\n sessionId: string;\n participantId: string;\n participantName?: string;\n}\n\nexport interface UploadResult {\n selectedFiles: SelectedFile[];\n uploadedIds: number[];\n errorMessage?: string;\n}\n\nexport class FileAttachmentManager {\n private readonly supportedExtensions: string[];\n private readonly maxFileSizeMb: number;\n private readonly maxTotalSizeMb: number;\n\n constructor(config: AttachmentValidationConfig) {\n this.supportedExtensions = config.supportedExtensions;\n this.maxFileSizeMb = config.maxFileSizeMb;\n this.maxTotalSizeMb = config.maxTotalSizeMb;\n }\n\n addFiles(existingFiles: SelectedFile[], files: FileList | File[]): SelectedFile[] {\n const newSelected: SelectedFile[] = [];\n const fileArray = Array.from(files instanceof FileList ? Array.from(files) : files);\n let totalSize = existingFiles.reduce((sum, f) => sum + f.file.size, 0);\n\n for (const file of fileArray) {\n const extension = this.getFileExtension(file.name);\n const contentType = file.type.split(\"/\")[0];\n if (contentType != \"text\" && !this.supportedExtensions.includes(extension)) {\n newSelected.push({ file, error: `File type ${extension} not supported` });\n continue;\n }\n\n const fileSizeMb = this.bytesToMb(file.size);\n if (fileSizeMb > this.maxFileSizeMb) {\n newSelected.push({ file, error: `File exceeds ${this.maxFileSizeMb}MB limit` });\n continue;\n }\n\n totalSize += file.size;\n const totalSizeMb = this.bytesToMb(totalSize);\n if (totalSizeMb > this.maxTotalSizeMb) {\n newSelected.push({ file, error: `Total size exceeds ${this.maxTotalSizeMb}MB limit` });\n continue;\n }\n\n newSelected.push({ file });\n }\n\n return [...existingFiles, ...newSelected];\n }\n\n removeFile(existingFiles: SelectedFile[], index: number): SelectedFile[] {\n return existingFiles.filter((_, i) => i !== index);\n }\n\n markPendingFilesWithError(existingFiles: SelectedFile[], errorMessage: string): SelectedFile[] {\n return existingFiles.map(file => {\n if (!file.error && !file.uploaded) {\n return { ...file, error: errorMessage };\n }\n return file;\n });\n }\n\n async uploadPendingFiles(\n existingFiles: SelectedFile[],\n context: UploadContext\n ): Promise<UploadResult> {\n if (existingFiles.length === 0) {\n return { selectedFiles: existingFiles, uploadedIds: [] };\n }\n\n const uploadCandidates = existingFiles.filter(file => !file.error && !file.uploaded);\n const uploadedIds: number[] = existingFiles\n .filter(file => file.uploaded)\n .map(file => file.uploaded!.id);\n\n if (uploadCandidates.length === 0) {\n return { selectedFiles: existingFiles, uploadedIds };\n }\n\n const formData = new FormData();\n for (const file of uploadCandidates) {\n formData.append('files', file.file);\n }\n formData.append('participant_remote_id', context.participantId);\n if (context.participantName) {\n formData.append('participant_name', context.participantName);\n }\n\n try {\n const response = await fetch(`${context.apiBaseUrl}/api/chat/${context.sessionId}/upload/`, {\n method: 'POST',\n body: formData,\n });\n\n if (!response.ok) {\n const errorData = await this.safeJson(response);\n const errorMessage =\n (errorData && typeof errorData === 'object' && 'error' in errorData && (errorData as { error?: string }).error) ||\n 'Failed to upload files';\n return {\n selectedFiles: this.markPendingFilesWithError(existingFiles, errorMessage),\n uploadedIds,\n errorMessage,\n };\n }\n\n const data = await this.safeJson(response);\n if (!data || typeof data !== 'object' || !Array.isArray((data as { files?: unknown }).files)) {\n const errorMessage = 'Unexpected upload response shape';\n return {\n selectedFiles: this.markPendingFilesWithError(existingFiles, errorMessage),\n uploadedIds,\n errorMessage,\n };\n }\n\n const uploadedFiles = (data as { files: UploadedFile[] }).files;\n let index = 0;\n const updatedSelected = existingFiles.map(file => {\n if (!file.error && !file.uploaded) {\n const uploaded = uploadedFiles[index];\n index += 1;\n return { ...file, uploaded };\n }\n return file;\n });\n uploadedIds.push(...uploadedFiles.map(file => file.id));\n\n return { selectedFiles: updatedSelected, uploadedIds };\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'Failed to upload files';\n return {\n selectedFiles: this.markPendingFilesWithError(existingFiles, errorMessage),\n uploadedIds,\n errorMessage,\n };\n }\n }\n\n private bytesToMb(bytes: number): number {\n return bytes / (1024 * 1024);\n }\n\n private getFileExtension(filename: string): string {\n const parts = filename.split('.');\n const ext = parts.pop();\n return ext ? `.${ext.toLowerCase()}` : '';\n }\n\n private async safeJson(response: Response): Promise<unknown> {\n try {\n return await response.json();\n } catch {\n return undefined;\n }\n }\n}\n"]}
@@ -4933,7 +4933,9 @@ class ChatSessionService {
4933
4933
  return response.json();
4934
4934
  }
4935
4935
  async pollTaskOnce(sessionId, taskId) {
4936
- const response = await fetch(`${this.apiBaseUrl}/api/chat/${sessionId}/${taskId}/poll/`);
4936
+ const response = await fetch(`${this.apiBaseUrl}/api/chat/${sessionId}/${taskId}/poll/`, {
4937
+ headers: this.getCommonHeaders(),
4938
+ });
4937
4939
  if (!response.ok) {
4938
4940
  throw new Error(`Failed to poll task: ${response.statusText}`);
4939
4941
  }
@@ -4991,7 +4993,9 @@ class ChatSessionService {
4991
4993
  if (since) {
4992
4994
  url.searchParams.set('since', since);
4993
4995
  }
4994
- const response = await fetch(url.toString());
4996
+ const response = await fetch(url.toString(), {
4997
+ headers: this.getCommonHeaders(),
4998
+ });
4995
4999
  if (!response.ok) {
4996
5000
  throw new Error(`Failed to poll messages: ${response.statusText}`);
4997
5001
  }
@@ -5028,14 +5032,18 @@ class ChatSessionService {
5028
5032
  }
5029
5033
  }
5030
5034
  getJsonHeaders() {
5031
- const headers = {
5032
- 'Content-Type': 'application/json',
5033
- 'x-ocs-widget-version': this.widgetVersion,
5034
- };
5035
+ const headers = this.getCommonHeaders();
5036
+ headers['Content-Type'] = 'application/json';
5035
5037
  const csrfToken = this.csrfTokenProvider(this.apiBaseUrl);
5036
5038
  if (csrfToken) {
5037
5039
  headers['X-CSRFToken'] = csrfToken;
5038
5040
  }
5041
+ return headers;
5042
+ }
5043
+ getCommonHeaders() {
5044
+ const headers = {
5045
+ 'x-ocs-widget-version': this.widgetVersion,
5046
+ };
5039
5047
  if (this.embedKey) {
5040
5048
  headers['X-Embed-Key'] = this.embedKey;
5041
5049
  }
@@ -5055,7 +5063,8 @@ class FileAttachmentManager {
5055
5063
  let totalSize = existingFiles.reduce((sum, f) => sum + f.file.size, 0);
5056
5064
  for (const file of fileArray) {
5057
5065
  const extension = this.getFileExtension(file.name);
5058
- if (!this.supportedExtensions.includes(extension)) {
5066
+ const contentType = file.type.split("/")[0];
5067
+ if (contentType != "text" && !this.supportedExtensions.includes(extension)) {
5059
5068
  newSelected.push({ file, error: `File type ${extension} not supported` });
5060
5069
  continue;
5061
5070
  }
@@ -5391,6 +5400,7 @@ const OcsChat = /*@__PURE__*/ proxyCustomElement(class OcsChat extends HTMLEleme
5391
5400
  }
5392
5401
  this.parseWelcomeMessages();
5393
5402
  this.parseStarterQuestions();
5403
+ this.loadInternalPageContext();
5394
5404
  }
5395
5405
  componentDidLoad() {
5396
5406
  const computedStyle = getComputedStyle(this.host);
@@ -5483,6 +5493,16 @@ const OcsChat = /*@__PURE__*/ proxyCustomElement(class OcsChat extends HTMLEleme
5483
5493
  }
5484
5494
  this.translationManager = new TranslationManager(this.language, customTranslationsObj);
5485
5495
  }
5496
+ loadInternalPageContext() {
5497
+ if (this.pageContext === undefined || this.pageContext === null) {
5498
+ return;
5499
+ }
5500
+ if (typeof this.pageContext !== 'object' || Array.isArray(this.pageContext)) {
5501
+ console.error("pageContext is expected to be a plain JavaScript object.");
5502
+ return;
5503
+ }
5504
+ this.internalPageContext = this.pageContext;
5505
+ }
5486
5506
  async loadTranslationsFromUrl(url) {
5487
5507
  try {
5488
5508
  const response = await fetch(url);
@@ -5615,10 +5635,14 @@ const OcsChat = /*@__PURE__*/ proxyCustomElement(class OcsChat extends HTMLEleme
5615
5635
  if (this.allowAttachments && attachmentIds.length > 0) {
5616
5636
  requestBody.attachment_ids = attachmentIds;
5617
5637
  }
5638
+ if (this.internalPageContext) {
5639
+ requestBody.context = this.internalPageContext;
5640
+ }
5618
5641
  const data = await this.getChatService().sendMessage(this.sessionId, requestBody);
5619
5642
  if (data.status === 'error') {
5620
5643
  throw new Error(data.error || 'Failed to send message');
5621
5644
  }
5645
+ this.internalPageContext = undefined;
5622
5646
  this.startTaskPolling(data.task_id);
5623
5647
  }
5624
5648
  catch (error) {
@@ -5705,6 +5729,14 @@ const OcsChat = /*@__PURE__*/ proxyCustomElement(class OcsChat extends HTMLEleme
5705
5729
  toggleWindowVisibility() {
5706
5730
  this.visible = !this.visible;
5707
5731
  }
5732
+ /**
5733
+ * Watch for changes to the `pageContext` prop and sync to internal variable.
5734
+ *
5735
+ * @param pageContext - The new value for the field.
5736
+ */
5737
+ pageContextHandler() {
5738
+ this.loadInternalPageContext();
5739
+ }
5708
5740
  /**
5709
5741
  * Watch for changes to the `visible` attribute and update accordingly.
5710
5742
  *
@@ -6264,12 +6296,13 @@ const OcsChat = /*@__PURE__*/ proxyCustomElement(class OcsChat extends HTMLEleme
6264
6296
  if (el) {
6265
6297
  this.fileInputRef = el;
6266
6298
  }
6267
- }, id: "ocs-file-input", type: "file", multiple: true, accept: OcsChat.SUPPORTED_FILE_EXTENSIONS.join(','), onChange: (e) => this.handleFileSelect(e), class: "hidden" })), this.allowAttachments && (h("button", { class: "file-attachment-button", onClick: () => { var _a; return (_a = this.fileInputRef) === null || _a === void 0 ? void 0 : _a.click(); }, disabled: this.isTyping || this.isUploadingFiles || this.isLoading, title: this.translationManager.get('attach.add'), "aria-label": this.translationManager.get('attach.add') }, h(PaperClipIcon, null))), h("button", { class: `send-button ${!this.isTyping && !this.isLoading && !!this.messageInput.trim()
6299
+ }, id: "ocs-file-input", type: "file", multiple: true, accept: OcsChat.SUPPORTED_FILE_EXTENSIONS.join(',') + ',text/*', onChange: (e) => this.handleFileSelect(e), class: "hidden" })), this.allowAttachments && (h("button", { class: "file-attachment-button", onClick: () => { var _a; return (_a = this.fileInputRef) === null || _a === void 0 ? void 0 : _a.click(); }, disabled: this.isTyping || this.isUploadingFiles || this.isLoading, title: this.translationManager.get('attach.add'), "aria-label": this.translationManager.get('attach.add') }, h(PaperClipIcon, null))), h("button", { class: `send-button ${!this.isTyping && !this.isLoading && !!this.messageInput.trim()
6268
6300
  ? 'send-button-enabled'
6269
6301
  : 'send-button-disabled'}`, onClick: () => this.sendMessage(this.messageInput), disabled: this.isTyping || this.isUploadingFiles || this.isLoading || !this.messageInput.trim() }, this.isUploadingFiles ? `${this.translationManager.get('status.uploading')}...` : this.translationManager.get('composer.send')))), h("div", { class: "flex items-center justify-center text-[0.8em] font-light w-full text-slate-500 py-[2px]" }, h("p", null, this.translationManager.get('branding.poweredBy'), ' ', " ", h("a", { class: "underline", href: "https://www.dimagi.com", target: "_blank" }, "Dimagi"))))))));
6270
6302
  }
6271
6303
  get host() { return this; }
6272
6304
  static get watchers() { return {
6305
+ "pageContext": ["pageContextHandler"],
6273
6306
  "visible": ["visibilityHandler"]
6274
6307
  }; }
6275
6308
  static get style() { return ocsChatCss; }
@@ -6295,6 +6328,7 @@ const OcsChat = /*@__PURE__*/ proxyCustomElement(class OcsChat extends HTMLEleme
6295
6328
  "typingIndicatorText": [1, "typing-indicator-text"],
6296
6329
  "language": [1],
6297
6330
  "translationsUrl": [1, "translations-url"],
6331
+ "pageContext": [1040, "page-context"],
6298
6332
  "error": [32],
6299
6333
  "messages": [32],
6300
6334
  "sessionId": [32],
@@ -6316,6 +6350,7 @@ const OcsChat = /*@__PURE__*/ proxyCustomElement(class OcsChat extends HTMLEleme
6316
6350
  "isButtonDragging": [32],
6317
6351
  "buttonWasDragged": [32]
6318
6352
  }, undefined, {
6353
+ "pageContext": ["pageContextHandler"],
6319
6354
  "visible": ["visibilityHandler"]
6320
6355
  }]);
6321
6356
  OcsChat.TASK_POLLING_MAX_ATTEMPTS = 120;
@@ -6328,8 +6363,9 @@ OcsChat.WINDOW_MARGIN = 20;
6328
6363
  OcsChat.LOCALSTORAGE_TEST_KEY = '__ocs_test__';
6329
6364
  OcsChat.MAX_FILE_SIZE_MB = 50;
6330
6365
  OcsChat.MAX_TOTAL_SIZE_MB = 50;
6331
- OcsChat.SUPPORTED_FILE_EXTENSIONS = ['.txt', '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.csv', '.jpg', '.jpeg',
6332
- '.png', '.gif', '.bmp', '.webp', '.svg', '.mp4', '.mov', '.avi', '.mp3', '.wav'];
6366
+ OcsChat.SUPPORTED_FILE_EXTENSIONS = ['.txt', '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.csv', '.jpg',
6367
+ '.jpeg', '.png', '.gif', '.bmp', '.webp', '.svg', '.mp4', '.mov', '.avi', '.mp3', '.wav', '.html', '.htm', '.css',
6368
+ '.js', '.xml', '.md', '.ics', '.vcf', '.rtf', '.tsv', '.yaml', '.yml', '.py', '.c'];
6333
6369
  function defineCustomElement$1() {
6334
6370
  if (typeof customElements === "undefined") {
6335
6371
  return;