@blockspark/chat-widget 1.0.15 → 1.0.16

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 (180) hide show
  1. package/dist/{core/stateManager.esm.js → ChatWidget-CFvb5g7s.js} +450 -4
  2. package/dist/ChatWidget-CFvb5g7s.js.map +1 -0
  3. package/dist/ChatWidget-CdA7TymM.cjs +2 -0
  4. package/dist/ChatWidget-CdA7TymM.cjs.map +1 -0
  5. package/dist/index.cjs.js +1 -1
  6. package/dist/index.cjs.js.map +1 -1
  7. package/dist/index.esm.js +1357 -7
  8. package/dist/index.esm.js.map +1 -1
  9. package/dist/nuxt.cjs.js +1 -1
  10. package/dist/nuxt.esm.js +4 -5
  11. package/dist/nuxt.esm.js.map +1 -1
  12. package/dist/sanitize-DRKcO9o5.cjs +4 -0
  13. package/dist/sanitize-DRKcO9o5.cjs.map +1 -0
  14. package/dist/sanitize-NEykcppO.js +2835 -0
  15. package/dist/sanitize-NEykcppO.js.map +1 -0
  16. package/dist/utils/frameworkDetector.d.ts.map +1 -1
  17. package/dist/utils/sanitize.d.ts.map +1 -1
  18. package/dist/vue.cjs.js +1 -1
  19. package/dist/vue.esm.js +4 -5
  20. package/dist/vue.esm.js.map +1 -1
  21. package/package.json +14 -49
  22. package/dist/_virtual/_plugin-vue_export-helper.cjs.js +0 -2
  23. package/dist/_virtual/_plugin-vue_export-helper.cjs.js.map +0 -1
  24. package/dist/_virtual/_plugin-vue_export-helper.esm.js +0 -11
  25. package/dist/_virtual/_plugin-vue_export-helper.esm.js.map +0 -1
  26. package/dist/components/ChatWidget.cjs.js +0 -2
  27. package/dist/components/ChatWidget.cjs.js.map +0 -1
  28. package/dist/components/ChatWidget.esm.js +0 -1129
  29. package/dist/components/ChatWidget.esm.js.map +0 -1
  30. package/dist/components/ChatWidget.vue.cjs.js +0 -2
  31. package/dist/components/ChatWidget.vue.cjs.js.map +0 -1
  32. package/dist/components/ChatWidget.vue.cjs2.js +0 -2
  33. package/dist/components/ChatWidget.vue.cjs2.js.map +0 -1
  34. package/dist/components/ChatWidget.vue.esm.js +0 -8
  35. package/dist/components/ChatWidget.vue.esm.js.map +0 -1
  36. package/dist/components/ChatWidget.vue.esm2.js +0 -374
  37. package/dist/components/ChatWidget.vue.esm2.js.map +0 -1
  38. package/dist/composables/useChatWidget.cjs.js +0 -2
  39. package/dist/composables/useChatWidget.cjs.js.map +0 -1
  40. package/dist/composables/useChatWidget.esm.js +0 -75
  41. package/dist/composables/useChatWidget.esm.js.map +0 -1
  42. package/dist/core/stateManager.cjs.js +0 -2
  43. package/dist/core/stateManager.cjs.js.map +0 -1
  44. package/dist/core/stateManager.esm.js.map +0 -1
  45. package/dist/entry/vanilla.cjs.js +0 -2
  46. package/dist/entry/vanilla.cjs.js.map +0 -1
  47. package/dist/entry/vanilla.esm.js +0 -50
  48. package/dist/entry/vanilla.esm.js.map +0 -1
  49. package/dist/hooks/useChatMode.cjs.js +0 -2
  50. package/dist/hooks/useChatMode.cjs.js.map +0 -1
  51. package/dist/hooks/useChatMode.esm.js +0 -61
  52. package/dist/hooks/useChatMode.esm.js.map +0 -1
  53. package/dist/node_modules/jose/dist/browser/jws/compact/sign.cjs.js +0 -2
  54. package/dist/node_modules/jose/dist/browser/jws/compact/sign.cjs.js.map +0 -1
  55. package/dist/node_modules/jose/dist/browser/jws/compact/sign.esm.js +0 -21
  56. package/dist/node_modules/jose/dist/browser/jws/compact/sign.esm.js.map +0 -1
  57. package/dist/node_modules/jose/dist/browser/jws/flattened/sign.cjs.js +0 -2
  58. package/dist/node_modules/jose/dist/browser/jws/flattened/sign.cjs.js.map +0 -1
  59. package/dist/node_modules/jose/dist/browser/jws/flattened/sign.esm.js +0 -84
  60. package/dist/node_modules/jose/dist/browser/jws/flattened/sign.esm.js.map +0 -1
  61. package/dist/node_modules/jose/dist/browser/jwt/produce.cjs.js +0 -2
  62. package/dist/node_modules/jose/dist/browser/jwt/produce.cjs.js.map +0 -1
  63. package/dist/node_modules/jose/dist/browser/jwt/produce.esm.js +0 -72
  64. package/dist/node_modules/jose/dist/browser/jwt/produce.esm.js.map +0 -1
  65. package/dist/node_modules/jose/dist/browser/jwt/sign.cjs.js +0 -2
  66. package/dist/node_modules/jose/dist/browser/jwt/sign.cjs.js.map +0 -1
  67. package/dist/node_modules/jose/dist/browser/jwt/sign.esm.js +0 -22
  68. package/dist/node_modules/jose/dist/browser/jwt/sign.esm.js.map +0 -1
  69. package/dist/node_modules/jose/dist/browser/key/import.cjs.js +0 -2
  70. package/dist/node_modules/jose/dist/browser/key/import.cjs.js.map +0 -1
  71. package/dist/node_modules/jose/dist/browser/key/import.esm.js +0 -11
  72. package/dist/node_modules/jose/dist/browser/key/import.esm.js.map +0 -1
  73. package/dist/node_modules/jose/dist/browser/lib/buffer_utils.cjs.js +0 -2
  74. package/dist/node_modules/jose/dist/browser/lib/buffer_utils.cjs.js.map +0 -1
  75. package/dist/node_modules/jose/dist/browser/lib/buffer_utils.esm.js +0 -18
  76. package/dist/node_modules/jose/dist/browser/lib/buffer_utils.esm.js.map +0 -1
  77. package/dist/node_modules/jose/dist/browser/lib/check_key_type.cjs.js +0 -2
  78. package/dist/node_modules/jose/dist/browser/lib/check_key_type.cjs.js.map +0 -1
  79. package/dist/node_modules/jose/dist/browser/lib/check_key_type.esm.js +0 -77
  80. package/dist/node_modules/jose/dist/browser/lib/check_key_type.esm.js.map +0 -1
  81. package/dist/node_modules/jose/dist/browser/lib/crypto_key.cjs.js +0 -2
  82. package/dist/node_modules/jose/dist/browser/lib/crypto_key.cjs.js.map +0 -1
  83. package/dist/node_modules/jose/dist/browser/lib/crypto_key.esm.js +0 -101
  84. package/dist/node_modules/jose/dist/browser/lib/crypto_key.esm.js.map +0 -1
  85. package/dist/node_modules/jose/dist/browser/lib/epoch.cjs.js +0 -2
  86. package/dist/node_modules/jose/dist/browser/lib/epoch.cjs.js.map +0 -1
  87. package/dist/node_modules/jose/dist/browser/lib/epoch.esm.js +0 -5
  88. package/dist/node_modules/jose/dist/browser/lib/epoch.esm.js.map +0 -1
  89. package/dist/node_modules/jose/dist/browser/lib/invalid_key_input.cjs.js +0 -2
  90. package/dist/node_modules/jose/dist/browser/lib/invalid_key_input.cjs.js.map +0 -1
  91. package/dist/node_modules/jose/dist/browser/lib/invalid_key_input.esm.js +0 -32
  92. package/dist/node_modules/jose/dist/browser/lib/invalid_key_input.esm.js.map +0 -1
  93. package/dist/node_modules/jose/dist/browser/lib/is_disjoint.cjs.js +0 -2
  94. package/dist/node_modules/jose/dist/browser/lib/is_disjoint.cjs.js.map +0 -1
  95. package/dist/node_modules/jose/dist/browser/lib/is_disjoint.esm.js +0 -25
  96. package/dist/node_modules/jose/dist/browser/lib/is_disjoint.esm.js.map +0 -1
  97. package/dist/node_modules/jose/dist/browser/lib/is_jwk.cjs.js +0 -2
  98. package/dist/node_modules/jose/dist/browser/lib/is_jwk.cjs.js.map +0 -1
  99. package/dist/node_modules/jose/dist/browser/lib/is_jwk.esm.js +0 -20
  100. package/dist/node_modules/jose/dist/browser/lib/is_jwk.esm.js.map +0 -1
  101. package/dist/node_modules/jose/dist/browser/lib/is_object.cjs.js +0 -2
  102. package/dist/node_modules/jose/dist/browser/lib/is_object.cjs.js.map +0 -1
  103. package/dist/node_modules/jose/dist/browser/lib/is_object.esm.js +0 -20
  104. package/dist/node_modules/jose/dist/browser/lib/is_object.esm.js.map +0 -1
  105. package/dist/node_modules/jose/dist/browser/lib/secs.cjs.js +0 -2
  106. package/dist/node_modules/jose/dist/browser/lib/secs.cjs.js.map +0 -1
  107. package/dist/node_modules/jose/dist/browser/lib/secs.esm.js +0 -59
  108. package/dist/node_modules/jose/dist/browser/lib/secs.esm.js.map +0 -1
  109. package/dist/node_modules/jose/dist/browser/lib/validate_crit.cjs.js +0 -2
  110. package/dist/node_modules/jose/dist/browser/lib/validate_crit.cjs.js.map +0 -1
  111. package/dist/node_modules/jose/dist/browser/lib/validate_crit.esm.js +0 -34
  112. package/dist/node_modules/jose/dist/browser/lib/validate_crit.esm.js.map +0 -1
  113. package/dist/node_modules/jose/dist/browser/runtime/asn1.cjs.js +0 -2
  114. package/dist/node_modules/jose/dist/browser/runtime/asn1.cjs.js.map +0 -1
  115. package/dist/node_modules/jose/dist/browser/runtime/asn1.esm.js +0 -103
  116. package/dist/node_modules/jose/dist/browser/runtime/asn1.esm.js.map +0 -1
  117. package/dist/node_modules/jose/dist/browser/runtime/base64url.cjs.js +0 -2
  118. package/dist/node_modules/jose/dist/browser/runtime/base64url.cjs.js.map +0 -1
  119. package/dist/node_modules/jose/dist/browser/runtime/base64url.esm.js +0 -43
  120. package/dist/node_modules/jose/dist/browser/runtime/base64url.esm.js.map +0 -1
  121. package/dist/node_modules/jose/dist/browser/runtime/check_key_length.cjs.js +0 -2
  122. package/dist/node_modules/jose/dist/browser/runtime/check_key_length.cjs.js.map +0 -1
  123. package/dist/node_modules/jose/dist/browser/runtime/check_key_length.esm.js +0 -12
  124. package/dist/node_modules/jose/dist/browser/runtime/check_key_length.esm.js.map +0 -1
  125. package/dist/node_modules/jose/dist/browser/runtime/get_sign_verify_key.cjs.js +0 -2
  126. package/dist/node_modules/jose/dist/browser/runtime/get_sign_verify_key.cjs.js.map +0 -1
  127. package/dist/node_modules/jose/dist/browser/runtime/get_sign_verify_key.esm.js +0 -25
  128. package/dist/node_modules/jose/dist/browser/runtime/get_sign_verify_key.esm.js.map +0 -1
  129. package/dist/node_modules/jose/dist/browser/runtime/is_key_like.cjs.js +0 -2
  130. package/dist/node_modules/jose/dist/browser/runtime/is_key_like.cjs.js.map +0 -1
  131. package/dist/node_modules/jose/dist/browser/runtime/is_key_like.esm.js +0 -13
  132. package/dist/node_modules/jose/dist/browser/runtime/is_key_like.esm.js.map +0 -1
  133. package/dist/node_modules/jose/dist/browser/runtime/jwk_to_key.cjs.js +0 -2
  134. package/dist/node_modules/jose/dist/browser/runtime/jwk_to_key.cjs.js.map +0 -1
  135. package/dist/node_modules/jose/dist/browser/runtime/jwk_to_key.esm.js +0 -107
  136. package/dist/node_modules/jose/dist/browser/runtime/jwk_to_key.esm.js.map +0 -1
  137. package/dist/node_modules/jose/dist/browser/runtime/normalize_key.cjs.js +0 -2
  138. package/dist/node_modules/jose/dist/browser/runtime/normalize_key.cjs.js.map +0 -1
  139. package/dist/node_modules/jose/dist/browser/runtime/normalize_key.esm.js +0 -71
  140. package/dist/node_modules/jose/dist/browser/runtime/normalize_key.esm.js.map +0 -1
  141. package/dist/node_modules/jose/dist/browser/runtime/sign.cjs.js +0 -2
  142. package/dist/node_modules/jose/dist/browser/runtime/sign.cjs.js.map +0 -1
  143. package/dist/node_modules/jose/dist/browser/runtime/sign.esm.js +0 -14
  144. package/dist/node_modules/jose/dist/browser/runtime/sign.esm.js.map +0 -1
  145. package/dist/node_modules/jose/dist/browser/runtime/subtle_dsa.cjs.js +0 -2
  146. package/dist/node_modules/jose/dist/browser/runtime/subtle_dsa.cjs.js.map +0 -1
  147. package/dist/node_modules/jose/dist/browser/runtime/subtle_dsa.esm.js +0 -32
  148. package/dist/node_modules/jose/dist/browser/runtime/subtle_dsa.esm.js.map +0 -1
  149. package/dist/node_modules/jose/dist/browser/runtime/webcrypto.cjs.js +0 -2
  150. package/dist/node_modules/jose/dist/browser/runtime/webcrypto.cjs.js.map +0 -1
  151. package/dist/node_modules/jose/dist/browser/runtime/webcrypto.esm.js +0 -7
  152. package/dist/node_modules/jose/dist/browser/runtime/webcrypto.esm.js.map +0 -1
  153. package/dist/node_modules/jose/dist/browser/util/errors.cjs.js +0 -2
  154. package/dist/node_modules/jose/dist/browser/util/errors.cjs.js.map +0 -1
  155. package/dist/node_modules/jose/dist/browser/util/errors.esm.js +0 -131
  156. package/dist/node_modules/jose/dist/browser/util/errors.esm.js.map +0 -1
  157. package/dist/node_modules/react-dom/client.cjs.js +0 -2
  158. package/dist/node_modules/react-dom/client.cjs.js.map +0 -1
  159. package/dist/node_modules/react-dom/client.esm.js +0 -21
  160. package/dist/node_modules/react-dom/client.esm.js.map +0 -1
  161. package/dist/services/chatService.cjs.js +0 -2
  162. package/dist/services/chatService.cjs.js.map +0 -1
  163. package/dist/services/chatService.esm.js +0 -482
  164. package/dist/services/chatService.esm.js.map +0 -1
  165. package/dist/services/dialogflowClient.cjs.js +0 -2
  166. package/dist/services/dialogflowClient.cjs.js.map +0 -1
  167. package/dist/services/dialogflowClient.esm.js +0 -282
  168. package/dist/services/dialogflowClient.esm.js.map +0 -1
  169. package/dist/services/sessionManager.cjs.js +0 -2
  170. package/dist/services/sessionManager.cjs.js.map +0 -1
  171. package/dist/services/sessionManager.esm.js +0 -48
  172. package/dist/services/sessionManager.esm.js.map +0 -1
  173. package/dist/utils/frameworkDetector.cjs.js +0 -2
  174. package/dist/utils/frameworkDetector.cjs.js.map +0 -1
  175. package/dist/utils/frameworkDetector.esm.js +0 -125
  176. package/dist/utils/frameworkDetector.esm.js.map +0 -1
  177. package/dist/utils/sanitize.cjs.js +0 -2
  178. package/dist/utils/sanitize.cjs.js.map +0 -1
  179. package/dist/utils/sanitize.esm.js +0 -52
  180. package/dist/utils/sanitize.esm.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"chatService.cjs.js","sources":["../../src/services/chatService.ts"],"sourcesContent":["// services/chatService.ts\n\nimport { getSessionManager } from './sessionManager';\n\nexport interface SupportChatSession {\n chat_id: string;\n session_id: string;\n}\n\nexport interface SupportMessage {\n id?: string;\n sender_type: \"customer\" | \"agent\";\n content: string;\n timestamp: string;\n}\n\nexport interface ChatInfo {\n status: \"waiting\" | \"active\";\n agent_id?: string;\n}\n\nexport interface WebSocketMessage {\n type: \"message\" | \"chat_info\" | \"error\" | \"typing_start\" | \"typing_stop\" | \"agent_changed\" | \"agent_accepted\" | \"chat_resolved\" | \"chat_ended\" | \"ping\" | \"pong\";\n id?: string;\n chat_id?: string;\n sender_type?: \"customer\" | \"agent\";\n sender_id?: string;\n content?: string;\n timestamp?: string;\n status?: \"waiting\" | \"active\" | \"resolved\" | \"ended\";\n agent_id?: string;\n error?: string;\n from_agent?: string;\n to_agent?: string;\n to_agent_id?: string;\n reason?: string;\n message?: string;\n}\n\nexport class ChatResolvedError extends Error {\n reason = \"chat_resolved\" as const;\n constructor(message = \"chat_resolved\") {\n super(message);\n this.name = \"ChatResolvedError\";\n }\n}\n\nexport interface ChatServiceConfig {\n baseUrl?: string;\n wsUrl?: string;\n debug?: boolean;\n}\n\nexport interface StartSupportChatOptions {\n dialogflowSessionId?: string | null;\n customerName?: string | null;\n customerEmail?: string | null;\n customerMobile?: string | null;\n}\n\n// Get default URLs from environment variables or use fallbacks\n// Environment variables are injected at build time via webpack DefinePlugin\nconst DEFAULT_BASE_URL = \n (typeof process !== 'undefined' && (process as any).env?.REACT_APP_BACKEND_BASE_URL)\n \nconst DEFAULT_WS_URL = \n (typeof process !== 'undefined' && (process as any).env?.REACT_APP_BACKEND_WS_URL)\n\nclass ChatService {\n private baseUrl: string;\n private wsUrl: string;\n private debug: boolean;\n private ws: WebSocket | null = null;\n private wsReconnectAttempts = 0;\n private maxReconnectAttempts = 5;\n private reconnectTimeout: NodeJS.Timeout | null = null;\n private pingInterval: NodeJS.Timeout | null = null;\n private currentChatId: string | null = null;\n private currentSessionId: string | null = null;\n private messageHandlers: Set<(message: WebSocketMessage) => void> = new Set();\n private connectionHandlers: Set<(connected: boolean) => void> = new Set();\n private sessionManager: ReturnType<typeof getSessionManager>;\n\n constructor(config: ChatServiceConfig = {}) {\n this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;\n this.wsUrl = config.wsUrl || DEFAULT_WS_URL;\n this.debug = config.debug || false;\n this.sessionManager = getSessionManager();\n }\n\n /**\n * Start a new support chat session\n * @param dialogflowSessionId - Optional: Dialogflow session ID\n * @param customerName - Optional: Customer name\n * @param customerEmail - Optional: Customer email\n * @param customerMobile - Optional: Customer mobile number\n */\n async startSupportChat(\n dialogflowSessionId?: string | null,\n customerName?: string | null,\n customerEmail?: string | null,\n customerMobile?: string | null\n ): Promise<SupportChatSession> {\n try {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...this.sessionManager.getSessionHeader(),\n };\n\n if (dialogflowSessionId) {\n headers[\"X-Session-ID\"] = dialogflowSessionId;\n }\n\n const body: Record<string, any> = {};\n if (customerName) body.name = customerName;\n if (customerEmail) body.email = customerEmail;\n if (customerMobile) body.mobile = customerMobile;\n\n const response = await fetch(`${this.baseUrl}/api/support/chat/start`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(\n error.message || `HTTP error! status: ${response.status}`\n );\n }\n\n const data = await response.json();\n this.sessionManager.updateSessionFromResponse(data);\n\n if (data.status && data.data) {\n return {\n chat_id: data.data.chat_id,\n session_id: data.data.session_id,\n };\n }\n\n if (data.chat_id && data.session_id) {\n return {\n chat_id: data.chat_id,\n session_id: data.session_id,\n };\n }\n\n throw new Error(\"Invalid response format from chat start endpoint\");\n } catch (error: any) {\n console.error(\"Error starting support chat:\", error);\n throw new Error(\n error.message || \"Failed to start support chat session\"\n );\n }\n }\n\n /**\n * Request handoff to human agent\n * @param chatId - Chat ID (must be valid, not undefined)\n * @param sessionId - Session ID\n * @param reason - Optional: Reason for handoff\n * @param dialogflowSessionId - Optional: Dialogflow session ID\n * @param customerName - Optional: Customer name\n * @param customerEmail - Optional: Customer email\n * @param customerMobile - Optional: Customer mobile number\n */\n async requestHandoff(\n chatId: string,\n sessionId: string,\n reason?: string,\n dialogflowSessionId?: string | null,\n customerName?: string | null,\n customerEmail?: string | null,\n customerMobile?: string | null\n ): Promise<{ success: boolean; message?: string }> {\n if (!chatId || chatId === \"undefined\" || chatId === \"null\") {\n throw new Error(\"Invalid chat_id. Chat must be initialized first.\");\n }\n\n try {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...this.sessionManager.getSessionHeader(),\n };\n\n if (sessionId) {\n headers[\"X-Session-ID\"] = sessionId;\n } else if (dialogflowSessionId) {\n headers[\"X-Session-ID\"] = dialogflowSessionId;\n }\n\n const body: Record<string, any> = {};\n if (reason) body.reason = reason;\n if (customerName) body.name = customerName;\n if (customerEmail) body.email = customerEmail;\n if (customerMobile) body.mobile = customerMobile;\n\n const response = await fetch(\n `${this.baseUrl}/api/support/chat/${chatId}/handoff`,\n {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n }\n );\n\n if (response.status === 400 || response.status === 404) {\n const error = await response.json().catch(() => ({}));\n throw new Error(\n error.message || \"Invalid chat_id. Chat may have expired.\"\n );\n }\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(\n error.message || `HTTP error! status: ${response.status}`\n );\n }\n\n const data = await response.json();\n this.sessionManager.updateSessionFromResponse(data);\n\n if (data.status) {\n return {\n success: true,\n message: data.message || data.data?.message,\n };\n }\n\n return {\n success: true,\n message: data.message,\n };\n } catch (error: any) {\n console.error(\"Error requesting handoff:\", error);\n throw error;\n }\n }\n\n /**\n * Send message to agent via REST API\n */\n async sendMessageToAgent(\n chatId: string,\n sessionId: string,\n message: string\n ): Promise<SupportMessage> {\n try {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...this.sessionManager.getSessionHeader(),\n };\n\n if (sessionId) {\n headers[\"X-Session-ID\"] = sessionId;\n }\n\n const response = await fetch(\n `${this.baseUrl}/api/support/chat/${chatId}/message`,\n {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n content: message,\n }),\n }\n );\n\n if (response.status === 401 || response.status === 404) {\n const error = await response.json().catch(() => ({}));\n throw new Error(\n error.message || \"Chat not found or unauthorized\"\n );\n }\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(\n error.message || `HTTP error! status: ${response.status}`\n );\n }\n\n const data = await response.json();\n this.sessionManager.updateSessionFromResponse(data);\n\n // Surface this explicitly so the UI can enter the terminal \"resolved\" state and avoid retries.\n if ((data as any)?.ignored === true && (data as any)?.reason === \"chat_resolved\") {\n throw new ChatResolvedError();\n }\n\n return {\n id: data.id,\n sender_type: \"customer\",\n content: message,\n timestamp: data.timestamp || new Date().toISOString(),\n };\n } catch (error: any) {\n // chat_resolved is a normal terminal condition, not an error.\n if (error instanceof ChatResolvedError || error?.name === \"ChatResolvedError\") {\n throw error;\n }\n console.error(\"Error sending message to agent:\", error);\n throw new Error(error.message || \"Failed to send message to agent\");\n }\n }\n\n /**\n * Ensure chat is initialized - helper method\n * Returns existing chat_id if valid, otherwise initializes new chat\n * @param existingChatId - Existing chat ID to validate\n * @param existingSessionId - Existing session ID\n * @param dialogflowSessionId - Optional: Dialogflow session ID\n * @param customerName - Optional: Customer name\n * @param customerEmail - Optional: Customer email\n * @param customerMobile - Optional: Customer mobile number\n * @returns Promise with chat_id and session_id\n */\n async ensureChatInitialized(\n existingChatId?: string | null,\n existingSessionId?: string | null,\n dialogflowSessionId?: string | null,\n customerName?: string | null,\n customerEmail?: string | null,\n customerMobile?: string | null\n ): Promise<SupportChatSession> {\n // Validate existing chat_id\n if (existingChatId && \n existingChatId !== \"undefined\" && \n existingChatId !== \"null\" &&\n existingSessionId) {\n return {\n chat_id: existingChatId,\n session_id: existingSessionId,\n };\n }\n\n // Initialize new chat\n return await this.startSupportChat(\n dialogflowSessionId || null,\n customerName || null,\n customerEmail || null,\n customerMobile || null\n );\n }\n\n /**\n * Load message history\n */\n async loadMessageHistory(\n chatId: string,\n sessionId: string\n ): Promise<SupportMessage[]> {\n try {\n const headers: Record<string, string> = {\n ...this.sessionManager.getSessionHeader(),\n };\n\n if (sessionId) {\n headers[\"X-Session-ID\"] = sessionId;\n }\n\n const response = await fetch(\n `${this.baseUrl}/api/support/chat/${chatId}/messages`,\n {\n method: \"GET\",\n headers,\n }\n );\n\n if (response.status === 401 || response.status === 404) {\n const error = await response.json().catch(() => ({}));\n throw new Error(\n error.message || \"Chat not found or unauthorized\"\n );\n }\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(\n error.message || `HTTP error! status: ${response.status}`\n );\n }\n\n const data = await response.json();\n this.sessionManager.updateSessionFromResponse(data);\n\n return data.messages || [];\n } catch (error: any) {\n console.error(\"Error loading message history:\", error);\n throw new Error(error.message || \"Failed to load message history\");\n }\n }\n\n /**\n * Connect to WebSocket\n */\n connectWebSocket(\n chatId: string,\n sessionId: string,\n onMessage?: (message: WebSocketMessage) => void,\n onConnectionChange?: (connected: boolean) => void,\n onClose?: (event: CloseEvent) => void\n ): void {\n // Clear any existing reconnection timeout\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\n\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n if (this.debug) {\n console.log(\"WebSocket already connected\");\n }\n return;\n }\n\n // Store chatId and sessionId for reconnection\n this.currentChatId = chatId;\n this.currentSessionId = sessionId;\n\n // Add handlers\n if (onMessage) {\n this.messageHandlers.add(onMessage);\n }\n if (onConnectionChange) {\n this.connectionHandlers.add(onConnectionChange);\n }\n\n // CRITICAL: Get session_id from session manager (persisted from previous requests)\n // Use provided sessionId, or fallback to stored session, or empty string\n const sessionIdToUse = sessionId || this.sessionManager.getSessionId() || '';\n const wsUrl = `${this.wsUrl}/ws/support/${chatId}?X-Session-ID=${encodeURIComponent(sessionIdToUse)}`;\n\n try {\n this.ws = new WebSocket(wsUrl);\n\n this.ws.onopen = () => {\n if (this.debug) {\n console.log(\"WebSocket connected to:\", wsUrl);\n }\n console.log(\"✅ Customer WebSocket connected:\", { chatId, sessionId, wsUrl });\n this.wsReconnectAttempts = 0;\n this.connectionHandlers.forEach((handler) => handler(true));\n this.startPingInterval();\n };\n\n this.ws.onmessage = (event) => {\n try {\n const message: WebSocketMessage = JSON.parse(event.data);\n \n if (this.debug) {\n console.log(\"WebSocket raw message received:\", event.data);\n console.log(\"WebSocket parsed message:\", message);\n }\n \n // Update session_id from WebSocket messages if provided\n if ((message as any).session_id) {\n this.sessionManager.updateSessionFromResponse({ session_id: (message as any).session_id });\n }\n \n // Always log agent_accepted and chat_resolved messages for debugging\n if (message.type === \"agent_accepted\" || message.type === \"chat_resolved\" || message.type === \"chat_ended\") {\n console.log(\"🔔 Received notification message:\", {\n type: message.type,\n chat_id: message.chat_id,\n timestamp: message.timestamp,\n to_agent: (message as any).to_agent,\n to_agent_id: (message as any).to_agent_id\n });\n }\n \n // Handle pong messages silently (keep-alive response)\n if (message.type === \"pong\") {\n return;\n }\n \n // Log message type and sender info\n if (this.debug && message.type === \"message\") {\n console.log(\"Processing message type:\", {\n type: message.type,\n sender_type: message.sender_type,\n content: message.content?.substring(0, 50) + \"...\",\n id: message.id\n });\n }\n \n this.messageHandlers.forEach((handler) => handler(message));\n } catch (error) {\n console.error(\"Error parsing WebSocket message:\", error);\n if (this.debug) {\n console.error(\"Raw message data:\", event.data);\n }\n }\n };\n\n this.ws.onerror = (error) => {\n console.error(\"❌ WebSocket error:\", error);\n this.connectionHandlers.forEach((handler) => handler(false));\n };\n\n this.ws.onclose = (event) => {\n if (this.debug) {\n console.log(\"WebSocket closed:\", event.code, event.reason);\n }\n console.log(\"🔌 Customer WebSocket closed:\", { code: event.code, reason: event.reason, chatId });\n this.stopPingInterval();\n this.connectionHandlers.forEach((handler) => handler(false));\n this.ws = null;\n onClose?.(event);\n\n // Terminal close: backend uses 4000 to indicate chat was resolved.\n // Do NOT attempt to reconnect in this case.\n if (event.code === 4000) {\n this.wsReconnectAttempts = this.maxReconnectAttempts;\n this.currentChatId = null;\n this.currentSessionId = null;\n return;\n }\n\n // Attempt to reconnect with exponential backoff\n if (this.wsReconnectAttempts < this.maxReconnectAttempts && this.currentChatId && this.currentSessionId) {\n this.wsReconnectAttempts++;\n const delay = Math.min(1000 * Math.pow(2, this.wsReconnectAttempts), 30000);\n if (this.debug) {\n console.log(`Reconnecting in ${delay}ms (attempt ${this.wsReconnectAttempts}/${this.maxReconnectAttempts})`);\n }\n \n this.reconnectTimeout = setTimeout(() => {\n if (this.currentChatId && this.currentSessionId) {\n this.connectWebSocket(this.currentChatId, this.currentSessionId, onMessage, onConnectionChange, onClose);\n }\n }, delay);\n } else if (this.debug) {\n console.error(\"Max reconnection attempts reached\");\n }\n };\n } catch (error) {\n console.error(\"Error creating WebSocket connection:\", error);\n this.connectionHandlers.forEach((handler) => handler(false));\n }\n }\n\n /**\n * Send message via WebSocket\n */\n sendMessageViaWebSocket(message: string): boolean {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n return false;\n }\n\n try {\n this.ws.send(\n JSON.stringify({\n type: \"message\",\n content: message,\n })\n );\n return true;\n } catch (error) {\n console.error(\"Error sending message via WebSocket:\", error);\n return false;\n }\n }\n\n /**\n * Send typing indicator via WebSocket\n */\n sendTypingIndicator(type: \"typing_start\" | \"typing_stop\"): boolean {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n return false;\n }\n\n try {\n this.ws.send(\n JSON.stringify({\n type: type,\n })\n );\n return true;\n } catch (error) {\n console.error(`Error sending ${type} via WebSocket:`, error);\n return false;\n }\n }\n\n /**\n * Start ping interval to keep connection alive\n */\n private startPingInterval(): void {\n this.stopPingInterval(); // Clear any existing interval\n this.pingInterval = setInterval(() => {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n try {\n this.ws.send(JSON.stringify({ type: \"ping\" }));\n } catch (error) {\n console.error(\"Error sending ping:\", error);\n }\n }\n }, 30000); // Every 30 seconds\n }\n\n /**\n * Stop ping interval\n */\n private stopPingInterval(): void {\n if (this.pingInterval) {\n clearInterval(this.pingInterval);\n this.pingInterval = null;\n }\n }\n\n /**\n * Disconnect WebSocket\n */\n disconnectWebSocket(): void {\n // Clear reconnection timeout\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\n \n // Stop ping interval\n this.stopPingInterval();\n \n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n this.messageHandlers.clear();\n this.connectionHandlers.clear();\n this.wsReconnectAttempts = 0;\n this.currentChatId = null;\n this.currentSessionId = null;\n }\n\n /**\n * Check if WebSocket is connected\n */\n isWebSocketConnected(): boolean {\n return this.ws !== null && this.ws.readyState === WebSocket.OPEN;\n }\n\n /**\n * Remove message handler\n */\n removeMessageHandler(handler: (message: WebSocketMessage) => void): void {\n this.messageHandlers.delete(handler);\n }\n\n /**\n * Remove connection handler\n */\n removeConnectionHandler(handler: (connected: boolean) => void): void {\n this.connectionHandlers.delete(handler);\n }\n}\n\n// Export singleton instance\nlet chatServiceInstance: ChatService | null = null;\n\nexport function getChatService(config?: ChatServiceConfig): ChatService {\n if (!chatServiceInstance) {\n chatServiceInstance = new ChatService(config);\n }\n return chatServiceInstance;\n}\n\nexport function createChatService(config: ChatServiceConfig): ChatService {\n return new ChatService(config);\n}\n\n"],"names":["ChatResolvedError","Error","constructor","message","super","this","reason","name","DEFAULT_BASE_URL","process","env","REACT_APP_BACKEND_BASE_URL","DEFAULT_WS_URL","REACT_APP_BACKEND_WS_URL","ChatService","config","ws","wsReconnectAttempts","maxReconnectAttempts","reconnectTimeout","pingInterval","currentChatId","currentSessionId","messageHandlers","Set","connectionHandlers","baseUrl","wsUrl","debug","sessionManager","getSessionManager","startSupportChat","dialogflowSessionId","customerName","customerEmail","customerMobile","headers","getSessionHeader","body","email","mobile","response","fetch","method","JSON","stringify","ok","error","json","catch","status","data","updateSessionFromResponse","chat_id","session_id","requestHandoff","chatId","sessionId","success","sendMessageToAgent","content","ignored","id","sender_type","timestamp","Date","toISOString","ensureChatInitialized","existingChatId","existingSessionId","loadMessageHistory","messages","connectWebSocket","onMessage","onConnectionChange","onClose","clearTimeout","readyState","WebSocket","OPEN","add","sessionIdToUse","getSessionId","encodeURIComponent","onopen","forEach","handler","startPingInterval","onmessage","event","parse","type","onerror","onclose","stopPingInterval","code","delay","Math","min","pow","setTimeout","sendMessageViaWebSocket","send","sendTypingIndicator","setInterval","clearInterval","disconnectWebSocket","close","clear","isWebSocketConnected","removeMessageHandler","delete","removeConnectionHandler"],"mappings":"2HAuCO,MAAMA,UAA0BC,MAErC,WAAAC,CAAYC,EAAU,iBACpBC,MAAMD,GAFRE,KAAAC,OAAS,gBAGPD,KAAKE,KAAO,mBACd,EAkBF,MAAMC,EACgB,oBAAZC,SAA4BA,QAAgBC,KAAKC,2BAErDC,EACgB,oBAAZH,SAA4BA,QAAgBC,KAAKG,yBAE3D,MAAMC,EAeJ,WAAAZ,CAAYa,EAA4B,IAXxCV,KAAQW,GAAuB,KAC/BX,KAAQY,oBAAsB,EAC9BZ,KAAQa,qBAAuB,EAC/Bb,KAAQc,iBAA0C,KAClDd,KAAQe,aAAsC,KAC9Cf,KAAQgB,cAA+B,KACvChB,KAAQiB,iBAAkC,KAC1CjB,KAAQkB,oBAAgEC,IACxEnB,KAAQoB,uBAA4DD,IAIlEnB,KAAKqB,QAAUX,EAAOW,SAAWlB,EACjCH,KAAKsB,MAAQZ,EAAOY,OAASf,EAC7BP,KAAKuB,MAAQb,EAAOa,QAAS,EAC7BvB,KAAKwB,eAAiBC,qBACxB,CASA,sBAAMC,CACJC,EACAC,EACAC,EACAC,GAEA,IACE,MAAMC,EAAkC,CACtC,eAAgB,sBACb/B,KAAKwB,eAAeQ,oBAGrBL,IACFI,EAAQ,gBAAkBJ,GAG5B,MAAMM,EAA4B,CAAA,EAC9BL,MAAmB1B,KAAO0B,GAC1BC,MAAoBK,MAAQL,GAC5BC,MAAqBK,OAASL,GAElC,MAAMM,QAAiBC,MAAM,GAAGrC,KAAKqB,iCAAkC,CACrEiB,OAAQ,OACRP,UACAE,KAAMM,KAAKC,UAAUP,KAGvB,IAAKG,EAASK,GAAI,CAChB,MAAMC,QAAcN,EAASO,OAAOC,MAAM,KAAA,CAAO,IACjD,MAAM,IAAIhD,MACR8C,EAAM5C,SAAW,uBAAuBsC,EAASS,SAErD,CAEA,MAAMC,QAAaV,EAASO,OAG5B,GAFA3C,KAAKwB,eAAeuB,0BAA0BD,GAE1CA,EAAKD,QAAUC,EAAKA,KACtB,MAAO,CACLE,QAASF,EAAKA,KAAKE,QACnBC,WAAYH,EAAKA,KAAKG,YAI1B,GAAIH,EAAKE,SAAWF,EAAKG,WACvB,MAAO,CACLD,QAASF,EAAKE,QACdC,WAAYH,EAAKG,YAIrB,MAAM,IAAIrD,MAAM,mDAClB,OAAS8C,GAEP,MAAM,IAAI9C,MACR8C,EAAM5C,SAAW,uCAErB,CACF,CAYA,oBAAMoD,CACJC,EACAC,EACAnD,EACA0B,EACAC,EACAC,EACAC,GAEA,IAAKqB,GAAqB,cAAXA,GAAqC,SAAXA,EACvC,MAAM,IAAIvD,MAAM,oDAGlB,IACE,MAAMmC,EAAkC,CACtC,eAAgB,sBACb/B,KAAKwB,eAAeQ,oBAGrBoB,EACFrB,EAAQ,gBAAkBqB,EACjBzB,IACTI,EAAQ,gBAAkBJ,GAG5B,MAAMM,EAA4B,CAAA,EAC9BhC,MAAaA,OAASA,GACtB2B,MAAmB1B,KAAO0B,GAC1BC,MAAoBK,MAAQL,GAC5BC,MAAqBK,OAASL,GAElC,MAAMM,QAAiBC,MACrB,GAAGrC,KAAKqB,4BAA4B8B,YACpC,CACEb,OAAQ,OACRP,UACAE,KAAMM,KAAKC,UAAUP,KAIzB,GAAwB,MAApBG,EAASS,QAAsC,MAApBT,EAASS,OAAgB,CACtD,MAAMH,QAAcN,EAASO,OAAOC,MAAM,KAAA,CAAO,IACjD,MAAM,IAAIhD,MACR8C,EAAM5C,SAAW,0CAErB,CAEA,IAAKsC,EAASK,GAAI,CAChB,MAAMC,QAAcN,EAASO,OAAOC,MAAM,KAAA,CAAO,IACjD,MAAM,IAAIhD,MACR8C,EAAM5C,SAAW,uBAAuBsC,EAASS,SAErD,CAEA,MAAMC,QAAaV,EAASO,OAG5B,OAFA3C,KAAKwB,eAAeuB,0BAA0BD,GAE1CA,EAAKD,OACA,CACLQ,SAAS,EACTvD,QAASgD,EAAKhD,SAAWgD,EAAKA,MAAMhD,SAIjC,CACLuD,SAAS,EACTvD,QAASgD,EAAKhD,QAElB,OAAS4C,GAEP,MAAMA,CACR,CACF,CAKA,wBAAMY,CACJH,EACAC,EACAtD,GAEA,IACE,MAAMiC,EAAkC,CACtC,eAAgB,sBACb/B,KAAKwB,eAAeQ,oBAGrBoB,IACFrB,EAAQ,gBAAkBqB,GAG5B,MAAMhB,QAAiBC,MACrB,GAAGrC,KAAKqB,4BAA4B8B,YACpC,CACEb,OAAQ,OACRP,UACAE,KAAMM,KAAKC,UAAU,CACnBe,QAASzD,MAKf,GAAwB,MAApBsC,EAASS,QAAsC,MAApBT,EAASS,OAAgB,CACtD,MAAMH,QAAcN,EAASO,OAAOC,MAAM,KAAA,CAAO,IACjD,MAAM,IAAIhD,MACR8C,EAAM5C,SAAW,iCAErB,CAEA,IAAKsC,EAASK,GAAI,CAChB,MAAMC,QAAcN,EAASO,OAAOC,MAAM,KAAA,CAAO,IACjD,MAAM,IAAIhD,MACR8C,EAAM5C,SAAW,uBAAuBsC,EAASS,SAErD,CAEA,MAAMC,QAAaV,EAASO,OAI5B,GAHA3C,KAAKwB,eAAeuB,0BAA0BD,IAGf,IAA1BA,GAAcU,SAA8C,kBAAzBV,GAAc7C,OACpD,MAAM,IAAIN,EAGZ,MAAO,CACL8D,GAAIX,EAAKW,GACTC,YAAa,WACbH,QAASzD,EACT6D,UAAWb,EAAKa,YAAA,IAAiBC,MAAOC,cAE5C,OAASnB,GAEP,GAAIA,aAAiB/C,GAAqC,sBAAhB+C,GAAOxC,KAC/C,MAAMwC,EAGR,MAAM,IAAI9C,MAAM8C,EAAM5C,SAAW,kCACnC,CACF,CAaA,2BAAMgE,CACJC,EACAC,EACArC,EACAC,EACAC,EACAC,GAGA,OAAIiC,GACmB,cAAnBA,GACmB,SAAnBA,GACAC,EACK,CACLhB,QAASe,EACTd,WAAYe,SAKHhE,KAAK0B,iBAChBC,GAAuB,KACvBC,GAAgB,KAChBC,GAAiB,KACjBC,GAAkB,KAEtB,CAKA,wBAAMmC,CACJd,EACAC,GAEA,IACE,MAAMrB,EAAkC,IACnC/B,KAAKwB,eAAeQ,oBAGrBoB,IACFrB,EAAQ,gBAAkBqB,GAG5B,MAAMhB,QAAiBC,MACrB,GAAGrC,KAAKqB,4BAA4B8B,aACpC,CACEb,OAAQ,MACRP,YAIJ,GAAwB,MAApBK,EAASS,QAAsC,MAApBT,EAASS,OAAgB,CACtD,MAAMH,QAAcN,EAASO,OAAOC,MAAM,KAAA,CAAO,IACjD,MAAM,IAAIhD,MACR8C,EAAM5C,SAAW,iCAErB,CAEA,IAAKsC,EAASK,GAAI,CAChB,MAAMC,QAAcN,EAASO,OAAOC,MAAM,KAAA,CAAO,IACjD,MAAM,IAAIhD,MACR8C,EAAM5C,SAAW,uBAAuBsC,EAASS,SAErD,CAEA,MAAMC,QAAaV,EAASO,OAG5B,OAFA3C,KAAKwB,eAAeuB,0BAA0BD,GAEvCA,EAAKoB,UAAY,EAC1B,OAASxB,GAEP,MAAM,IAAI9C,MAAM8C,EAAM5C,SAAW,iCACnC,CACF,CAKA,gBAAAqE,CACEhB,EACAC,EACAgB,EACAC,EACAC,GAQA,GALItE,KAAKc,mBACPyD,aAAavE,KAAKc,kBAClBd,KAAKc,iBAAmB,MAGtBd,KAAKW,IAAMX,KAAKW,GAAG6D,aAAeC,UAAUC,KAI9C,YAHI1E,KAAKuB,MAOXvB,KAAKgB,cAAgBmC,EACrBnD,KAAKiB,iBAAmBmC,EAGpBgB,GACFpE,KAAKkB,gBAAgByD,IAAIP,GAEvBC,GACFrE,KAAKoB,mBAAmBuD,IAAIN,GAK9B,MAAMO,EAAiBxB,GAAapD,KAAKwB,eAAeqD,gBAAkB,GACpEvD,EAAQ,GAAGtB,KAAKsB,oBAAoB6B,kBAAuB2B,mBAAmBF,KAEpF,IACE5E,KAAKW,GAAK,IAAI8D,UAAUnD,GAExBtB,KAAKW,GAAGoE,OAAS,KACX/E,KAAKuB,MAITvB,KAAKY,oBAAsB,EAC3BZ,KAAKoB,mBAAmB4D,QAASC,GAAYA,GAAQ,IACrDjF,KAAKkF,qBAGPlF,KAAKW,GAAGwE,UAAaC,IACnB,IACE,MAAMtF,EAA4ByC,KAAK8C,MAAMD,EAAMtC,MAwBnD,GAtBI9C,KAAKuB,MAMJzB,EAAgBmD,YACnBjD,KAAKwB,eAAeuB,0BAA0B,CAAEE,WAAanD,EAAgBmD,aAI1D,mBAAjBnD,EAAQwF,MAA8C,kBAAjBxF,EAAQwF,MAA4BxF,EAAQwF,KAWhE,SAAjBxF,EAAQwF,KACV,OAIEtF,KAAKuB,OAASzB,EAAQwF,KAS1BtF,KAAKkB,gBAAgB8D,QAASC,GAAYA,EAAQnF,GACpD,OAAS4C,GAEH1C,KAAKuB,KAGX,GAGFvB,KAAKW,GAAG4E,QAAW7C,IAEjB1C,KAAKoB,mBAAmB4D,QAASC,GAAYA,GAAQ,KAGvDjF,KAAKW,GAAG6E,QAAWJ,IAYjB,GAXIpF,KAAKuB,MAITvB,KAAKyF,mBACLzF,KAAKoB,mBAAmB4D,QAASC,GAAYA,GAAQ,IACrDjF,KAAKW,GAAK,KACV2D,IAAUc,GAIS,MAAfA,EAAMM,KAIR,OAHA1F,KAAKY,oBAAsBZ,KAAKa,qBAChCb,KAAKgB,cAAgB,UACrBhB,KAAKiB,iBAAmB,MAK1B,GAAIjB,KAAKY,oBAAsBZ,KAAKa,sBAAwBb,KAAKgB,eAAiBhB,KAAKiB,iBAAkB,CACvGjB,KAAKY,sBACL,MAAM+E,EAAQC,KAAKC,IAAI,IAAOD,KAAKE,IAAI,EAAG9F,KAAKY,qBAAsB,KACjEZ,KAAKuB,MAITvB,KAAKc,iBAAmBiF,WAAW,KAC7B/F,KAAKgB,eAAiBhB,KAAKiB,kBAC7BjB,KAAKmE,iBAAiBnE,KAAKgB,cAAehB,KAAKiB,iBAAkBmD,EAAWC,EAAoBC,IAEjGqB,EACL,MAAW3F,KAAKuB,MAIpB,OAASmB,GAEP1C,KAAKoB,mBAAmB4D,QAASC,GAAYA,GAAQ,GACvD,CACF,CAKA,uBAAAe,CAAwBlG,GACtB,IAAKE,KAAKW,IAAMX,KAAKW,GAAG6D,aAAeC,UAAUC,KAC/C,OAAO,EAGT,IAOE,OANA1E,KAAKW,GAAGsF,KACN1D,KAAKC,UAAU,CACb8C,KAAM,UACN/B,QAASzD,MAGN,CACT,OAAS4C,GAEP,OAAO,CACT,CACF,CAKA,mBAAAwD,CAAoBZ,GAClB,IAAKtF,KAAKW,IAAMX,KAAKW,GAAG6D,aAAeC,UAAUC,KAC/C,OAAO,EAGT,IAME,OALA1E,KAAKW,GAAGsF,KACN1D,KAAKC,UAAU,CACb8C,WAGG,CACT,OAAS5C,GAEP,OAAO,CACT,CACF,CAKQ,iBAAAwC,GACNlF,KAAKyF,mBACLzF,KAAKe,aAAeoF,YAAY,KAC9B,GAAInG,KAAKW,IAAMX,KAAKW,GAAG6D,aAAeC,UAAUC,KAC9C,IACE1E,KAAKW,GAAGsF,KAAK1D,KAAKC,UAAU,CAAE8C,KAAM,SACtC,OAAS5C,GAET,GAED,IACL,CAKQ,gBAAA+C,GACFzF,KAAKe,eACPqF,cAAcpG,KAAKe,cACnBf,KAAKe,aAAe,KAExB,CAKA,mBAAAsF,GAEMrG,KAAKc,mBACPyD,aAAavE,KAAKc,kBAClBd,KAAKc,iBAAmB,MAI1Bd,KAAKyF,mBAEDzF,KAAKW,KACPX,KAAKW,GAAG2F,QACRtG,KAAKW,GAAK,MAEZX,KAAKkB,gBAAgBqF,QACrBvG,KAAKoB,mBAAmBmF,QACxBvG,KAAKY,oBAAsB,EAC3BZ,KAAKgB,cAAgB,KACrBhB,KAAKiB,iBAAmB,IAC1B,CAKA,oBAAAuF,GACE,OAAmB,OAAZxG,KAAKW,IAAeX,KAAKW,GAAG6D,aAAeC,UAAUC,IAC9D,CAKA,oBAAA+B,CAAqBxB,GACnBjF,KAAKkB,gBAAgBwF,OAAOzB,EAC9B,CAKA,uBAAA0B,CAAwB1B,GACtBjF,KAAKoB,mBAAmBsF,OAAOzB,EACjC,wDAaK,SAA2BvE,GAChC,OAAO,IAAID,EAAYC,EACzB"}
@@ -1,482 +0,0 @@
1
- import { getSessionManager } from "./sessionManager.esm.js";
2
- class ChatResolvedError extends Error {
3
- constructor(message = "chat_resolved") {
4
- super(message);
5
- this.reason = "chat_resolved";
6
- this.name = "ChatResolvedError";
7
- }
8
- }
9
- const DEFAULT_BASE_URL = typeof process !== "undefined" && process.env?.REACT_APP_BACKEND_BASE_URL;
10
- const DEFAULT_WS_URL = typeof process !== "undefined" && process.env?.REACT_APP_BACKEND_WS_URL;
11
- class ChatService {
12
- constructor(config = {}) {
13
- this.ws = null;
14
- this.wsReconnectAttempts = 0;
15
- this.maxReconnectAttempts = 5;
16
- this.reconnectTimeout = null;
17
- this.pingInterval = null;
18
- this.currentChatId = null;
19
- this.currentSessionId = null;
20
- this.messageHandlers = /* @__PURE__ */ new Set();
21
- this.connectionHandlers = /* @__PURE__ */ new Set();
22
- this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;
23
- this.wsUrl = config.wsUrl || DEFAULT_WS_URL;
24
- this.debug = config.debug || false;
25
- this.sessionManager = getSessionManager();
26
- }
27
- /**
28
- * Start a new support chat session
29
- * @param dialogflowSessionId - Optional: Dialogflow session ID
30
- * @param customerName - Optional: Customer name
31
- * @param customerEmail - Optional: Customer email
32
- * @param customerMobile - Optional: Customer mobile number
33
- */
34
- async startSupportChat(dialogflowSessionId, customerName, customerEmail, customerMobile) {
35
- try {
36
- const headers = {
37
- "Content-Type": "application/json",
38
- ...this.sessionManager.getSessionHeader()
39
- };
40
- if (dialogflowSessionId) {
41
- headers["X-Session-ID"] = dialogflowSessionId;
42
- }
43
- const body = {};
44
- if (customerName) body.name = customerName;
45
- if (customerEmail) body.email = customerEmail;
46
- if (customerMobile) body.mobile = customerMobile;
47
- const response = await fetch(`${this.baseUrl}/api/support/chat/start`, {
48
- method: "POST",
49
- headers,
50
- body: JSON.stringify(body)
51
- });
52
- if (!response.ok) {
53
- const error = await response.json().catch(() => ({}));
54
- throw new Error(
55
- error.message || `HTTP error! status: ${response.status}`
56
- );
57
- }
58
- const data = await response.json();
59
- this.sessionManager.updateSessionFromResponse(data);
60
- if (data.status && data.data) {
61
- return {
62
- chat_id: data.data.chat_id,
63
- session_id: data.data.session_id
64
- };
65
- }
66
- if (data.chat_id && data.session_id) {
67
- return {
68
- chat_id: data.chat_id,
69
- session_id: data.session_id
70
- };
71
- }
72
- throw new Error("Invalid response format from chat start endpoint");
73
- } catch (error) {
74
- console.error("Error starting support chat:", error);
75
- throw new Error(
76
- error.message || "Failed to start support chat session"
77
- );
78
- }
79
- }
80
- /**
81
- * Request handoff to human agent
82
- * @param chatId - Chat ID (must be valid, not undefined)
83
- * @param sessionId - Session ID
84
- * @param reason - Optional: Reason for handoff
85
- * @param dialogflowSessionId - Optional: Dialogflow session ID
86
- * @param customerName - Optional: Customer name
87
- * @param customerEmail - Optional: Customer email
88
- * @param customerMobile - Optional: Customer mobile number
89
- */
90
- async requestHandoff(chatId, sessionId, reason, dialogflowSessionId, customerName, customerEmail, customerMobile) {
91
- if (!chatId || chatId === "undefined" || chatId === "null") {
92
- throw new Error("Invalid chat_id. Chat must be initialized first.");
93
- }
94
- try {
95
- const headers = {
96
- "Content-Type": "application/json",
97
- ...this.sessionManager.getSessionHeader()
98
- };
99
- if (sessionId) {
100
- headers["X-Session-ID"] = sessionId;
101
- } else if (dialogflowSessionId) {
102
- headers["X-Session-ID"] = dialogflowSessionId;
103
- }
104
- const body = {};
105
- if (reason) body.reason = reason;
106
- if (customerName) body.name = customerName;
107
- if (customerEmail) body.email = customerEmail;
108
- if (customerMobile) body.mobile = customerMobile;
109
- const response = await fetch(
110
- `${this.baseUrl}/api/support/chat/${chatId}/handoff`,
111
- {
112
- method: "POST",
113
- headers,
114
- body: JSON.stringify(body)
115
- }
116
- );
117
- if (response.status === 400 || response.status === 404) {
118
- const error = await response.json().catch(() => ({}));
119
- throw new Error(
120
- error.message || "Invalid chat_id. Chat may have expired."
121
- );
122
- }
123
- if (!response.ok) {
124
- const error = await response.json().catch(() => ({}));
125
- throw new Error(
126
- error.message || `HTTP error! status: ${response.status}`
127
- );
128
- }
129
- const data = await response.json();
130
- this.sessionManager.updateSessionFromResponse(data);
131
- if (data.status) {
132
- return {
133
- success: true,
134
- message: data.message || data.data?.message
135
- };
136
- }
137
- return {
138
- success: true,
139
- message: data.message
140
- };
141
- } catch (error) {
142
- console.error("Error requesting handoff:", error);
143
- throw error;
144
- }
145
- }
146
- /**
147
- * Send message to agent via REST API
148
- */
149
- async sendMessageToAgent(chatId, sessionId, message) {
150
- try {
151
- const headers = {
152
- "Content-Type": "application/json",
153
- ...this.sessionManager.getSessionHeader()
154
- };
155
- if (sessionId) {
156
- headers["X-Session-ID"] = sessionId;
157
- }
158
- const response = await fetch(
159
- `${this.baseUrl}/api/support/chat/${chatId}/message`,
160
- {
161
- method: "POST",
162
- headers,
163
- body: JSON.stringify({
164
- content: message
165
- })
166
- }
167
- );
168
- if (response.status === 401 || response.status === 404) {
169
- const error = await response.json().catch(() => ({}));
170
- throw new Error(
171
- error.message || "Chat not found or unauthorized"
172
- );
173
- }
174
- if (!response.ok) {
175
- const error = await response.json().catch(() => ({}));
176
- throw new Error(
177
- error.message || `HTTP error! status: ${response.status}`
178
- );
179
- }
180
- const data = await response.json();
181
- this.sessionManager.updateSessionFromResponse(data);
182
- if (data?.ignored === true && data?.reason === "chat_resolved") {
183
- throw new ChatResolvedError();
184
- }
185
- return {
186
- id: data.id,
187
- sender_type: "customer",
188
- content: message,
189
- timestamp: data.timestamp || (/* @__PURE__ */ new Date()).toISOString()
190
- };
191
- } catch (error) {
192
- if (error instanceof ChatResolvedError || error?.name === "ChatResolvedError") {
193
- throw error;
194
- }
195
- console.error("Error sending message to agent:", error);
196
- throw new Error(error.message || "Failed to send message to agent");
197
- }
198
- }
199
- /**
200
- * Ensure chat is initialized - helper method
201
- * Returns existing chat_id if valid, otherwise initializes new chat
202
- * @param existingChatId - Existing chat ID to validate
203
- * @param existingSessionId - Existing session ID
204
- * @param dialogflowSessionId - Optional: Dialogflow session ID
205
- * @param customerName - Optional: Customer name
206
- * @param customerEmail - Optional: Customer email
207
- * @param customerMobile - Optional: Customer mobile number
208
- * @returns Promise with chat_id and session_id
209
- */
210
- async ensureChatInitialized(existingChatId, existingSessionId, dialogflowSessionId, customerName, customerEmail, customerMobile) {
211
- if (existingChatId && existingChatId !== "undefined" && existingChatId !== "null" && existingSessionId) {
212
- return {
213
- chat_id: existingChatId,
214
- session_id: existingSessionId
215
- };
216
- }
217
- return await this.startSupportChat(
218
- dialogflowSessionId || null,
219
- customerName || null,
220
- customerEmail || null,
221
- customerMobile || null
222
- );
223
- }
224
- /**
225
- * Load message history
226
- */
227
- async loadMessageHistory(chatId, sessionId) {
228
- try {
229
- const headers = {
230
- ...this.sessionManager.getSessionHeader()
231
- };
232
- if (sessionId) {
233
- headers["X-Session-ID"] = sessionId;
234
- }
235
- const response = await fetch(
236
- `${this.baseUrl}/api/support/chat/${chatId}/messages`,
237
- {
238
- method: "GET",
239
- headers
240
- }
241
- );
242
- if (response.status === 401 || response.status === 404) {
243
- const error = await response.json().catch(() => ({}));
244
- throw new Error(
245
- error.message || "Chat not found or unauthorized"
246
- );
247
- }
248
- if (!response.ok) {
249
- const error = await response.json().catch(() => ({}));
250
- throw new Error(
251
- error.message || `HTTP error! status: ${response.status}`
252
- );
253
- }
254
- const data = await response.json();
255
- this.sessionManager.updateSessionFromResponse(data);
256
- return data.messages || [];
257
- } catch (error) {
258
- console.error("Error loading message history:", error);
259
- throw new Error(error.message || "Failed to load message history");
260
- }
261
- }
262
- /**
263
- * Connect to WebSocket
264
- */
265
- connectWebSocket(chatId, sessionId, onMessage, onConnectionChange, onClose) {
266
- if (this.reconnectTimeout) {
267
- clearTimeout(this.reconnectTimeout);
268
- this.reconnectTimeout = null;
269
- }
270
- if (this.ws && this.ws.readyState === WebSocket.OPEN) {
271
- if (this.debug) {
272
- console.log("WebSocket already connected");
273
- }
274
- return;
275
- }
276
- this.currentChatId = chatId;
277
- this.currentSessionId = sessionId;
278
- if (onMessage) {
279
- this.messageHandlers.add(onMessage);
280
- }
281
- if (onConnectionChange) {
282
- this.connectionHandlers.add(onConnectionChange);
283
- }
284
- const sessionIdToUse = sessionId || this.sessionManager.getSessionId() || "";
285
- const wsUrl = `${this.wsUrl}/ws/support/${chatId}?X-Session-ID=${encodeURIComponent(sessionIdToUse)}`;
286
- try {
287
- this.ws = new WebSocket(wsUrl);
288
- this.ws.onopen = () => {
289
- if (this.debug) {
290
- console.log("WebSocket connected to:", wsUrl);
291
- }
292
- console.log("✅ Customer WebSocket connected:", { chatId, sessionId, wsUrl });
293
- this.wsReconnectAttempts = 0;
294
- this.connectionHandlers.forEach((handler) => handler(true));
295
- this.startPingInterval();
296
- };
297
- this.ws.onmessage = (event) => {
298
- try {
299
- const message = JSON.parse(event.data);
300
- if (this.debug) {
301
- console.log("WebSocket raw message received:", event.data);
302
- console.log("WebSocket parsed message:", message);
303
- }
304
- if (message.session_id) {
305
- this.sessionManager.updateSessionFromResponse({ session_id: message.session_id });
306
- }
307
- if (message.type === "agent_accepted" || message.type === "chat_resolved" || message.type === "chat_ended") {
308
- console.log("🔔 Received notification message:", {
309
- type: message.type,
310
- chat_id: message.chat_id,
311
- timestamp: message.timestamp,
312
- to_agent: message.to_agent,
313
- to_agent_id: message.to_agent_id
314
- });
315
- }
316
- if (message.type === "pong") {
317
- return;
318
- }
319
- if (this.debug && message.type === "message") {
320
- console.log("Processing message type:", {
321
- type: message.type,
322
- sender_type: message.sender_type,
323
- content: message.content?.substring(0, 50) + "...",
324
- id: message.id
325
- });
326
- }
327
- this.messageHandlers.forEach((handler) => handler(message));
328
- } catch (error) {
329
- console.error("Error parsing WebSocket message:", error);
330
- if (this.debug) {
331
- console.error("Raw message data:", event.data);
332
- }
333
- }
334
- };
335
- this.ws.onerror = (error) => {
336
- console.error("❌ WebSocket error:", error);
337
- this.connectionHandlers.forEach((handler) => handler(false));
338
- };
339
- this.ws.onclose = (event) => {
340
- if (this.debug) {
341
- console.log("WebSocket closed:", event.code, event.reason);
342
- }
343
- console.log("🔌 Customer WebSocket closed:", { code: event.code, reason: event.reason, chatId });
344
- this.stopPingInterval();
345
- this.connectionHandlers.forEach((handler) => handler(false));
346
- this.ws = null;
347
- onClose?.(event);
348
- if (event.code === 4e3) {
349
- this.wsReconnectAttempts = this.maxReconnectAttempts;
350
- this.currentChatId = null;
351
- this.currentSessionId = null;
352
- return;
353
- }
354
- if (this.wsReconnectAttempts < this.maxReconnectAttempts && this.currentChatId && this.currentSessionId) {
355
- this.wsReconnectAttempts++;
356
- const delay = Math.min(1e3 * Math.pow(2, this.wsReconnectAttempts), 3e4);
357
- if (this.debug) {
358
- console.log(`Reconnecting in ${delay}ms (attempt ${this.wsReconnectAttempts}/${this.maxReconnectAttempts})`);
359
- }
360
- this.reconnectTimeout = setTimeout(() => {
361
- if (this.currentChatId && this.currentSessionId) {
362
- this.connectWebSocket(this.currentChatId, this.currentSessionId, onMessage, onConnectionChange, onClose);
363
- }
364
- }, delay);
365
- } else if (this.debug) {
366
- console.error("Max reconnection attempts reached");
367
- }
368
- };
369
- } catch (error) {
370
- console.error("Error creating WebSocket connection:", error);
371
- this.connectionHandlers.forEach((handler) => handler(false));
372
- }
373
- }
374
- /**
375
- * Send message via WebSocket
376
- */
377
- sendMessageViaWebSocket(message) {
378
- if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
379
- return false;
380
- }
381
- try {
382
- this.ws.send(
383
- JSON.stringify({
384
- type: "message",
385
- content: message
386
- })
387
- );
388
- return true;
389
- } catch (error) {
390
- console.error("Error sending message via WebSocket:", error);
391
- return false;
392
- }
393
- }
394
- /**
395
- * Send typing indicator via WebSocket
396
- */
397
- sendTypingIndicator(type) {
398
- if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
399
- return false;
400
- }
401
- try {
402
- this.ws.send(
403
- JSON.stringify({
404
- type
405
- })
406
- );
407
- return true;
408
- } catch (error) {
409
- console.error(`Error sending ${type} via WebSocket:`, error);
410
- return false;
411
- }
412
- }
413
- /**
414
- * Start ping interval to keep connection alive
415
- */
416
- startPingInterval() {
417
- this.stopPingInterval();
418
- this.pingInterval = setInterval(() => {
419
- if (this.ws && this.ws.readyState === WebSocket.OPEN) {
420
- try {
421
- this.ws.send(JSON.stringify({ type: "ping" }));
422
- } catch (error) {
423
- console.error("Error sending ping:", error);
424
- }
425
- }
426
- }, 3e4);
427
- }
428
- /**
429
- * Stop ping interval
430
- */
431
- stopPingInterval() {
432
- if (this.pingInterval) {
433
- clearInterval(this.pingInterval);
434
- this.pingInterval = null;
435
- }
436
- }
437
- /**
438
- * Disconnect WebSocket
439
- */
440
- disconnectWebSocket() {
441
- if (this.reconnectTimeout) {
442
- clearTimeout(this.reconnectTimeout);
443
- this.reconnectTimeout = null;
444
- }
445
- this.stopPingInterval();
446
- if (this.ws) {
447
- this.ws.close();
448
- this.ws = null;
449
- }
450
- this.messageHandlers.clear();
451
- this.connectionHandlers.clear();
452
- this.wsReconnectAttempts = 0;
453
- this.currentChatId = null;
454
- this.currentSessionId = null;
455
- }
456
- /**
457
- * Check if WebSocket is connected
458
- */
459
- isWebSocketConnected() {
460
- return this.ws !== null && this.ws.readyState === WebSocket.OPEN;
461
- }
462
- /**
463
- * Remove message handler
464
- */
465
- removeMessageHandler(handler) {
466
- this.messageHandlers.delete(handler);
467
- }
468
- /**
469
- * Remove connection handler
470
- */
471
- removeConnectionHandler(handler) {
472
- this.connectionHandlers.delete(handler);
473
- }
474
- }
475
- function createChatService(config) {
476
- return new ChatService(config);
477
- }
478
- export {
479
- ChatResolvedError,
480
- createChatService
481
- };
482
- //# sourceMappingURL=chatService.esm.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"chatService.esm.js","sources":["../../src/services/chatService.ts"],"sourcesContent":["// services/chatService.ts\n\nimport { getSessionManager } from './sessionManager';\n\nexport interface SupportChatSession {\n chat_id: string;\n session_id: string;\n}\n\nexport interface SupportMessage {\n id?: string;\n sender_type: \"customer\" | \"agent\";\n content: string;\n timestamp: string;\n}\n\nexport interface ChatInfo {\n status: \"waiting\" | \"active\";\n agent_id?: string;\n}\n\nexport interface WebSocketMessage {\n type: \"message\" | \"chat_info\" | \"error\" | \"typing_start\" | \"typing_stop\" | \"agent_changed\" | \"agent_accepted\" | \"chat_resolved\" | \"chat_ended\" | \"ping\" | \"pong\";\n id?: string;\n chat_id?: string;\n sender_type?: \"customer\" | \"agent\";\n sender_id?: string;\n content?: string;\n timestamp?: string;\n status?: \"waiting\" | \"active\" | \"resolved\" | \"ended\";\n agent_id?: string;\n error?: string;\n from_agent?: string;\n to_agent?: string;\n to_agent_id?: string;\n reason?: string;\n message?: string;\n}\n\nexport class ChatResolvedError extends Error {\n reason = \"chat_resolved\" as const;\n constructor(message = \"chat_resolved\") {\n super(message);\n this.name = \"ChatResolvedError\";\n }\n}\n\nexport interface ChatServiceConfig {\n baseUrl?: string;\n wsUrl?: string;\n debug?: boolean;\n}\n\nexport interface StartSupportChatOptions {\n dialogflowSessionId?: string | null;\n customerName?: string | null;\n customerEmail?: string | null;\n customerMobile?: string | null;\n}\n\n// Get default URLs from environment variables or use fallbacks\n// Environment variables are injected at build time via webpack DefinePlugin\nconst DEFAULT_BASE_URL = \n (typeof process !== 'undefined' && (process as any).env?.REACT_APP_BACKEND_BASE_URL)\n \nconst DEFAULT_WS_URL = \n (typeof process !== 'undefined' && (process as any).env?.REACT_APP_BACKEND_WS_URL)\n\nclass ChatService {\n private baseUrl: string;\n private wsUrl: string;\n private debug: boolean;\n private ws: WebSocket | null = null;\n private wsReconnectAttempts = 0;\n private maxReconnectAttempts = 5;\n private reconnectTimeout: NodeJS.Timeout | null = null;\n private pingInterval: NodeJS.Timeout | null = null;\n private currentChatId: string | null = null;\n private currentSessionId: string | null = null;\n private messageHandlers: Set<(message: WebSocketMessage) => void> = new Set();\n private connectionHandlers: Set<(connected: boolean) => void> = new Set();\n private sessionManager: ReturnType<typeof getSessionManager>;\n\n constructor(config: ChatServiceConfig = {}) {\n this.baseUrl = config.baseUrl || DEFAULT_BASE_URL;\n this.wsUrl = config.wsUrl || DEFAULT_WS_URL;\n this.debug = config.debug || false;\n this.sessionManager = getSessionManager();\n }\n\n /**\n * Start a new support chat session\n * @param dialogflowSessionId - Optional: Dialogflow session ID\n * @param customerName - Optional: Customer name\n * @param customerEmail - Optional: Customer email\n * @param customerMobile - Optional: Customer mobile number\n */\n async startSupportChat(\n dialogflowSessionId?: string | null,\n customerName?: string | null,\n customerEmail?: string | null,\n customerMobile?: string | null\n ): Promise<SupportChatSession> {\n try {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...this.sessionManager.getSessionHeader(),\n };\n\n if (dialogflowSessionId) {\n headers[\"X-Session-ID\"] = dialogflowSessionId;\n }\n\n const body: Record<string, any> = {};\n if (customerName) body.name = customerName;\n if (customerEmail) body.email = customerEmail;\n if (customerMobile) body.mobile = customerMobile;\n\n const response = await fetch(`${this.baseUrl}/api/support/chat/start`, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(\n error.message || `HTTP error! status: ${response.status}`\n );\n }\n\n const data = await response.json();\n this.sessionManager.updateSessionFromResponse(data);\n\n if (data.status && data.data) {\n return {\n chat_id: data.data.chat_id,\n session_id: data.data.session_id,\n };\n }\n\n if (data.chat_id && data.session_id) {\n return {\n chat_id: data.chat_id,\n session_id: data.session_id,\n };\n }\n\n throw new Error(\"Invalid response format from chat start endpoint\");\n } catch (error: any) {\n console.error(\"Error starting support chat:\", error);\n throw new Error(\n error.message || \"Failed to start support chat session\"\n );\n }\n }\n\n /**\n * Request handoff to human agent\n * @param chatId - Chat ID (must be valid, not undefined)\n * @param sessionId - Session ID\n * @param reason - Optional: Reason for handoff\n * @param dialogflowSessionId - Optional: Dialogflow session ID\n * @param customerName - Optional: Customer name\n * @param customerEmail - Optional: Customer email\n * @param customerMobile - Optional: Customer mobile number\n */\n async requestHandoff(\n chatId: string,\n sessionId: string,\n reason?: string,\n dialogflowSessionId?: string | null,\n customerName?: string | null,\n customerEmail?: string | null,\n customerMobile?: string | null\n ): Promise<{ success: boolean; message?: string }> {\n if (!chatId || chatId === \"undefined\" || chatId === \"null\") {\n throw new Error(\"Invalid chat_id. Chat must be initialized first.\");\n }\n\n try {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...this.sessionManager.getSessionHeader(),\n };\n\n if (sessionId) {\n headers[\"X-Session-ID\"] = sessionId;\n } else if (dialogflowSessionId) {\n headers[\"X-Session-ID\"] = dialogflowSessionId;\n }\n\n const body: Record<string, any> = {};\n if (reason) body.reason = reason;\n if (customerName) body.name = customerName;\n if (customerEmail) body.email = customerEmail;\n if (customerMobile) body.mobile = customerMobile;\n\n const response = await fetch(\n `${this.baseUrl}/api/support/chat/${chatId}/handoff`,\n {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n }\n );\n\n if (response.status === 400 || response.status === 404) {\n const error = await response.json().catch(() => ({}));\n throw new Error(\n error.message || \"Invalid chat_id. Chat may have expired.\"\n );\n }\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(\n error.message || `HTTP error! status: ${response.status}`\n );\n }\n\n const data = await response.json();\n this.sessionManager.updateSessionFromResponse(data);\n\n if (data.status) {\n return {\n success: true,\n message: data.message || data.data?.message,\n };\n }\n\n return {\n success: true,\n message: data.message,\n };\n } catch (error: any) {\n console.error(\"Error requesting handoff:\", error);\n throw error;\n }\n }\n\n /**\n * Send message to agent via REST API\n */\n async sendMessageToAgent(\n chatId: string,\n sessionId: string,\n message: string\n ): Promise<SupportMessage> {\n try {\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...this.sessionManager.getSessionHeader(),\n };\n\n if (sessionId) {\n headers[\"X-Session-ID\"] = sessionId;\n }\n\n const response = await fetch(\n `${this.baseUrl}/api/support/chat/${chatId}/message`,\n {\n method: \"POST\",\n headers,\n body: JSON.stringify({\n content: message,\n }),\n }\n );\n\n if (response.status === 401 || response.status === 404) {\n const error = await response.json().catch(() => ({}));\n throw new Error(\n error.message || \"Chat not found or unauthorized\"\n );\n }\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(\n error.message || `HTTP error! status: ${response.status}`\n );\n }\n\n const data = await response.json();\n this.sessionManager.updateSessionFromResponse(data);\n\n // Surface this explicitly so the UI can enter the terminal \"resolved\" state and avoid retries.\n if ((data as any)?.ignored === true && (data as any)?.reason === \"chat_resolved\") {\n throw new ChatResolvedError();\n }\n\n return {\n id: data.id,\n sender_type: \"customer\",\n content: message,\n timestamp: data.timestamp || new Date().toISOString(),\n };\n } catch (error: any) {\n // chat_resolved is a normal terminal condition, not an error.\n if (error instanceof ChatResolvedError || error?.name === \"ChatResolvedError\") {\n throw error;\n }\n console.error(\"Error sending message to agent:\", error);\n throw new Error(error.message || \"Failed to send message to agent\");\n }\n }\n\n /**\n * Ensure chat is initialized - helper method\n * Returns existing chat_id if valid, otherwise initializes new chat\n * @param existingChatId - Existing chat ID to validate\n * @param existingSessionId - Existing session ID\n * @param dialogflowSessionId - Optional: Dialogflow session ID\n * @param customerName - Optional: Customer name\n * @param customerEmail - Optional: Customer email\n * @param customerMobile - Optional: Customer mobile number\n * @returns Promise with chat_id and session_id\n */\n async ensureChatInitialized(\n existingChatId?: string | null,\n existingSessionId?: string | null,\n dialogflowSessionId?: string | null,\n customerName?: string | null,\n customerEmail?: string | null,\n customerMobile?: string | null\n ): Promise<SupportChatSession> {\n // Validate existing chat_id\n if (existingChatId && \n existingChatId !== \"undefined\" && \n existingChatId !== \"null\" &&\n existingSessionId) {\n return {\n chat_id: existingChatId,\n session_id: existingSessionId,\n };\n }\n\n // Initialize new chat\n return await this.startSupportChat(\n dialogflowSessionId || null,\n customerName || null,\n customerEmail || null,\n customerMobile || null\n );\n }\n\n /**\n * Load message history\n */\n async loadMessageHistory(\n chatId: string,\n sessionId: string\n ): Promise<SupportMessage[]> {\n try {\n const headers: Record<string, string> = {\n ...this.sessionManager.getSessionHeader(),\n };\n\n if (sessionId) {\n headers[\"X-Session-ID\"] = sessionId;\n }\n\n const response = await fetch(\n `${this.baseUrl}/api/support/chat/${chatId}/messages`,\n {\n method: \"GET\",\n headers,\n }\n );\n\n if (response.status === 401 || response.status === 404) {\n const error = await response.json().catch(() => ({}));\n throw new Error(\n error.message || \"Chat not found or unauthorized\"\n );\n }\n\n if (!response.ok) {\n const error = await response.json().catch(() => ({}));\n throw new Error(\n error.message || `HTTP error! status: ${response.status}`\n );\n }\n\n const data = await response.json();\n this.sessionManager.updateSessionFromResponse(data);\n\n return data.messages || [];\n } catch (error: any) {\n console.error(\"Error loading message history:\", error);\n throw new Error(error.message || \"Failed to load message history\");\n }\n }\n\n /**\n * Connect to WebSocket\n */\n connectWebSocket(\n chatId: string,\n sessionId: string,\n onMessage?: (message: WebSocketMessage) => void,\n onConnectionChange?: (connected: boolean) => void,\n onClose?: (event: CloseEvent) => void\n ): void {\n // Clear any existing reconnection timeout\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\n\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n if (this.debug) {\n console.log(\"WebSocket already connected\");\n }\n return;\n }\n\n // Store chatId and sessionId for reconnection\n this.currentChatId = chatId;\n this.currentSessionId = sessionId;\n\n // Add handlers\n if (onMessage) {\n this.messageHandlers.add(onMessage);\n }\n if (onConnectionChange) {\n this.connectionHandlers.add(onConnectionChange);\n }\n\n // CRITICAL: Get session_id from session manager (persisted from previous requests)\n // Use provided sessionId, or fallback to stored session, or empty string\n const sessionIdToUse = sessionId || this.sessionManager.getSessionId() || '';\n const wsUrl = `${this.wsUrl}/ws/support/${chatId}?X-Session-ID=${encodeURIComponent(sessionIdToUse)}`;\n\n try {\n this.ws = new WebSocket(wsUrl);\n\n this.ws.onopen = () => {\n if (this.debug) {\n console.log(\"WebSocket connected to:\", wsUrl);\n }\n console.log(\"✅ Customer WebSocket connected:\", { chatId, sessionId, wsUrl });\n this.wsReconnectAttempts = 0;\n this.connectionHandlers.forEach((handler) => handler(true));\n this.startPingInterval();\n };\n\n this.ws.onmessage = (event) => {\n try {\n const message: WebSocketMessage = JSON.parse(event.data);\n \n if (this.debug) {\n console.log(\"WebSocket raw message received:\", event.data);\n console.log(\"WebSocket parsed message:\", message);\n }\n \n // Update session_id from WebSocket messages if provided\n if ((message as any).session_id) {\n this.sessionManager.updateSessionFromResponse({ session_id: (message as any).session_id });\n }\n \n // Always log agent_accepted and chat_resolved messages for debugging\n if (message.type === \"agent_accepted\" || message.type === \"chat_resolved\" || message.type === \"chat_ended\") {\n console.log(\"🔔 Received notification message:\", {\n type: message.type,\n chat_id: message.chat_id,\n timestamp: message.timestamp,\n to_agent: (message as any).to_agent,\n to_agent_id: (message as any).to_agent_id\n });\n }\n \n // Handle pong messages silently (keep-alive response)\n if (message.type === \"pong\") {\n return;\n }\n \n // Log message type and sender info\n if (this.debug && message.type === \"message\") {\n console.log(\"Processing message type:\", {\n type: message.type,\n sender_type: message.sender_type,\n content: message.content?.substring(0, 50) + \"...\",\n id: message.id\n });\n }\n \n this.messageHandlers.forEach((handler) => handler(message));\n } catch (error) {\n console.error(\"Error parsing WebSocket message:\", error);\n if (this.debug) {\n console.error(\"Raw message data:\", event.data);\n }\n }\n };\n\n this.ws.onerror = (error) => {\n console.error(\"❌ WebSocket error:\", error);\n this.connectionHandlers.forEach((handler) => handler(false));\n };\n\n this.ws.onclose = (event) => {\n if (this.debug) {\n console.log(\"WebSocket closed:\", event.code, event.reason);\n }\n console.log(\"🔌 Customer WebSocket closed:\", { code: event.code, reason: event.reason, chatId });\n this.stopPingInterval();\n this.connectionHandlers.forEach((handler) => handler(false));\n this.ws = null;\n onClose?.(event);\n\n // Terminal close: backend uses 4000 to indicate chat was resolved.\n // Do NOT attempt to reconnect in this case.\n if (event.code === 4000) {\n this.wsReconnectAttempts = this.maxReconnectAttempts;\n this.currentChatId = null;\n this.currentSessionId = null;\n return;\n }\n\n // Attempt to reconnect with exponential backoff\n if (this.wsReconnectAttempts < this.maxReconnectAttempts && this.currentChatId && this.currentSessionId) {\n this.wsReconnectAttempts++;\n const delay = Math.min(1000 * Math.pow(2, this.wsReconnectAttempts), 30000);\n if (this.debug) {\n console.log(`Reconnecting in ${delay}ms (attempt ${this.wsReconnectAttempts}/${this.maxReconnectAttempts})`);\n }\n \n this.reconnectTimeout = setTimeout(() => {\n if (this.currentChatId && this.currentSessionId) {\n this.connectWebSocket(this.currentChatId, this.currentSessionId, onMessage, onConnectionChange, onClose);\n }\n }, delay);\n } else if (this.debug) {\n console.error(\"Max reconnection attempts reached\");\n }\n };\n } catch (error) {\n console.error(\"Error creating WebSocket connection:\", error);\n this.connectionHandlers.forEach((handler) => handler(false));\n }\n }\n\n /**\n * Send message via WebSocket\n */\n sendMessageViaWebSocket(message: string): boolean {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n return false;\n }\n\n try {\n this.ws.send(\n JSON.stringify({\n type: \"message\",\n content: message,\n })\n );\n return true;\n } catch (error) {\n console.error(\"Error sending message via WebSocket:\", error);\n return false;\n }\n }\n\n /**\n * Send typing indicator via WebSocket\n */\n sendTypingIndicator(type: \"typing_start\" | \"typing_stop\"): boolean {\n if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {\n return false;\n }\n\n try {\n this.ws.send(\n JSON.stringify({\n type: type,\n })\n );\n return true;\n } catch (error) {\n console.error(`Error sending ${type} via WebSocket:`, error);\n return false;\n }\n }\n\n /**\n * Start ping interval to keep connection alive\n */\n private startPingInterval(): void {\n this.stopPingInterval(); // Clear any existing interval\n this.pingInterval = setInterval(() => {\n if (this.ws && this.ws.readyState === WebSocket.OPEN) {\n try {\n this.ws.send(JSON.stringify({ type: \"ping\" }));\n } catch (error) {\n console.error(\"Error sending ping:\", error);\n }\n }\n }, 30000); // Every 30 seconds\n }\n\n /**\n * Stop ping interval\n */\n private stopPingInterval(): void {\n if (this.pingInterval) {\n clearInterval(this.pingInterval);\n this.pingInterval = null;\n }\n }\n\n /**\n * Disconnect WebSocket\n */\n disconnectWebSocket(): void {\n // Clear reconnection timeout\n if (this.reconnectTimeout) {\n clearTimeout(this.reconnectTimeout);\n this.reconnectTimeout = null;\n }\n \n // Stop ping interval\n this.stopPingInterval();\n \n if (this.ws) {\n this.ws.close();\n this.ws = null;\n }\n this.messageHandlers.clear();\n this.connectionHandlers.clear();\n this.wsReconnectAttempts = 0;\n this.currentChatId = null;\n this.currentSessionId = null;\n }\n\n /**\n * Check if WebSocket is connected\n */\n isWebSocketConnected(): boolean {\n return this.ws !== null && this.ws.readyState === WebSocket.OPEN;\n }\n\n /**\n * Remove message handler\n */\n removeMessageHandler(handler: (message: WebSocketMessage) => void): void {\n this.messageHandlers.delete(handler);\n }\n\n /**\n * Remove connection handler\n */\n removeConnectionHandler(handler: (connected: boolean) => void): void {\n this.connectionHandlers.delete(handler);\n }\n}\n\n// Export singleton instance\nlet chatServiceInstance: ChatService | null = null;\n\nexport function getChatService(config?: ChatServiceConfig): ChatService {\n if (!chatServiceInstance) {\n chatServiceInstance = new ChatService(config);\n }\n return chatServiceInstance;\n}\n\nexport function createChatService(config: ChatServiceConfig): ChatService {\n return new ChatService(config);\n}\n\n"],"names":[],"mappings":";AAuCO,MAAM,0BAA0B,MAAM;AAAA,EAE3C,YAAY,UAAU,iBAAiB;AACrC,UAAM,OAAO;AAFf,SAAA,SAAS;AAGP,SAAK,OAAO;AAAA,EACd;AACF;AAiBA,MAAM,mBACH,OAAO,YAAY,eAAgB,QAAgB,KAAK;AAE3D,MAAM,iBACH,OAAO,YAAY,eAAgB,QAAgB,KAAK;AAE3D,MAAM,YAAY;AAAA,EAehB,YAAY,SAA4B,IAAI;AAX5C,SAAQ,KAAuB;AAC/B,SAAQ,sBAAsB;AAC9B,SAAQ,uBAAuB;AAC/B,SAAQ,mBAA0C;AAClD,SAAQ,eAAsC;AAC9C,SAAQ,gBAA+B;AACvC,SAAQ,mBAAkC;AAC1C,SAAQ,sCAAgE,IAAA;AACxE,SAAQ,yCAA4D,IAAA;AAIlE,SAAK,UAAU,OAAO,WAAW;AACjC,SAAK,QAAQ,OAAO,SAAS;AAC7B,SAAK,QAAQ,OAAO,SAAS;AAC7B,SAAK,iBAAiB,kBAAA;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,iBACJ,qBACA,cACA,eACA,gBAC6B;AAC7B,QAAI;AACF,YAAM,UAAkC;AAAA,QACtC,gBAAgB;AAAA,QAChB,GAAG,KAAK,eAAe,iBAAA;AAAA,MAAiB;AAG1C,UAAI,qBAAqB;AACvB,gBAAQ,cAAc,IAAI;AAAA,MAC5B;AAEA,YAAM,OAA4B,CAAA;AAClC,UAAI,mBAAmB,OAAO;AAC9B,UAAI,oBAAoB,QAAQ;AAChC,UAAI,qBAAqB,SAAS;AAElC,YAAM,WAAW,MAAM,MAAM,GAAG,KAAK,OAAO,2BAA2B;AAAA,QACrE,QAAQ;AAAA,QACR;AAAA,QACA,MAAM,KAAK,UAAU,IAAI;AAAA,MAAA,CAC1B;AAED,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,QAAQ,MAAM,SAAS,KAAA,EAAO,MAAM,OAAO,CAAA,EAAG;AACpD,cAAM,IAAI;AAAA,UACR,MAAM,WAAW,uBAAuB,SAAS,MAAM;AAAA,QAAA;AAAA,MAE3D;AAEA,YAAM,OAAO,MAAM,SAAS,KAAA;AAC5B,WAAK,eAAe,0BAA0B,IAAI;AAElD,UAAI,KAAK,UAAU,KAAK,MAAM;AAC5B,eAAO;AAAA,UACL,SAAS,KAAK,KAAK;AAAA,UACnB,YAAY,KAAK,KAAK;AAAA,QAAA;AAAA,MAE1B;AAEA,UAAI,KAAK,WAAW,KAAK,YAAY;AACnC,eAAO;AAAA,UACL,SAAS,KAAK;AAAA,UACd,YAAY,KAAK;AAAA,QAAA;AAAA,MAErB;AAEA,YAAM,IAAI,MAAM,kDAAkD;AAAA,IACpE,SAAS,OAAY;AACnB,cAAQ,MAAM,gCAAgC,KAAK;AACnD,YAAM,IAAI;AAAA,QACR,MAAM,WAAW;AAAA,MAAA;AAAA,IAErB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,eACJ,QACA,WACA,QACA,qBACA,cACA,eACA,gBACiD;AACjD,QAAI,CAAC,UAAU,WAAW,eAAe,WAAW,QAAQ;AAC1D,YAAM,IAAI,MAAM,kDAAkD;AAAA,IACpE;AAEA,QAAI;AACF,YAAM,UAAkC;AAAA,QACtC,gBAAgB;AAAA,QAChB,GAAG,KAAK,eAAe,iBAAA;AAAA,MAAiB;AAG1C,UAAI,WAAW;AACb,gBAAQ,cAAc,IAAI;AAAA,MAC5B,WAAW,qBAAqB;AAC9B,gBAAQ,cAAc,IAAI;AAAA,MAC5B;AAEA,YAAM,OAA4B,CAAA;AAClC,UAAI,aAAa,SAAS;AAC1B,UAAI,mBAAmB,OAAO;AAC9B,UAAI,oBAAoB,QAAQ;AAChC,UAAI,qBAAqB,SAAS;AAElC,YAAM,WAAW,MAAM;AAAA,QACrB,GAAG,KAAK,OAAO,qBAAqB,MAAM;AAAA,QAC1C;AAAA,UACE,QAAQ;AAAA,UACR;AAAA,UACA,MAAM,KAAK,UAAU,IAAI;AAAA,QAAA;AAAA,MAC3B;AAGF,UAAI,SAAS,WAAW,OAAO,SAAS,WAAW,KAAK;AACtD,cAAM,QAAQ,MAAM,SAAS,KAAA,EAAO,MAAM,OAAO,CAAA,EAAG;AACpD,cAAM,IAAI;AAAA,UACR,MAAM,WAAW;AAAA,QAAA;AAAA,MAErB;AAEA,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,QAAQ,MAAM,SAAS,KAAA,EAAO,MAAM,OAAO,CAAA,EAAG;AACpD,cAAM,IAAI;AAAA,UACR,MAAM,WAAW,uBAAuB,SAAS,MAAM;AAAA,QAAA;AAAA,MAE3D;AAEA,YAAM,OAAO,MAAM,SAAS,KAAA;AAC5B,WAAK,eAAe,0BAA0B,IAAI;AAElD,UAAI,KAAK,QAAQ;AACf,eAAO;AAAA,UACL,SAAS;AAAA,UACT,SAAS,KAAK,WAAW,KAAK,MAAM;AAAA,QAAA;AAAA,MAExC;AAEA,aAAO;AAAA,QACL,SAAS;AAAA,QACT,SAAS,KAAK;AAAA,MAAA;AAAA,IAElB,SAAS,OAAY;AACnB,cAAQ,MAAM,6BAA6B,KAAK;AAChD,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,mBACJ,QACA,WACA,SACyB;AACzB,QAAI;AACF,YAAM,UAAkC;AAAA,QACtC,gBAAgB;AAAA,QAChB,GAAG,KAAK,eAAe,iBAAA;AAAA,MAAiB;AAG1C,UAAI,WAAW;AACb,gBAAQ,cAAc,IAAI;AAAA,MAC5B;AAEA,YAAM,WAAW,MAAM;AAAA,QACrB,GAAG,KAAK,OAAO,qBAAqB,MAAM;AAAA,QAC1C;AAAA,UACE,QAAQ;AAAA,UACR;AAAA,UACA,MAAM,KAAK,UAAU;AAAA,YACnB,SAAS;AAAA,UAAA,CACV;AAAA,QAAA;AAAA,MACH;AAGF,UAAI,SAAS,WAAW,OAAO,SAAS,WAAW,KAAK;AACtD,cAAM,QAAQ,MAAM,SAAS,KAAA,EAAO,MAAM,OAAO,CAAA,EAAG;AACpD,cAAM,IAAI;AAAA,UACR,MAAM,WAAW;AAAA,QAAA;AAAA,MAErB;AAEA,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,QAAQ,MAAM,SAAS,KAAA,EAAO,MAAM,OAAO,CAAA,EAAG;AACpD,cAAM,IAAI;AAAA,UACR,MAAM,WAAW,uBAAuB,SAAS,MAAM;AAAA,QAAA;AAAA,MAE3D;AAEA,YAAM,OAAO,MAAM,SAAS,KAAA;AAC5B,WAAK,eAAe,0BAA0B,IAAI;AAGlD,UAAK,MAAc,YAAY,QAAS,MAAc,WAAW,iBAAiB;AAChF,cAAM,IAAI,kBAAA;AAAA,MACZ;AAEA,aAAO;AAAA,QACL,IAAI,KAAK;AAAA,QACT,aAAa;AAAA,QACb,SAAS;AAAA,QACT,WAAW,KAAK,cAAa,oBAAI,KAAA,GAAO,YAAA;AAAA,MAAY;AAAA,IAExD,SAAS,OAAY;AAEnB,UAAI,iBAAiB,qBAAqB,OAAO,SAAS,qBAAqB;AAC7E,cAAM;AAAA,MACR;AACA,cAAQ,MAAM,mCAAmC,KAAK;AACtD,YAAM,IAAI,MAAM,MAAM,WAAW,iCAAiC;AAAA,IACpE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,MAAM,sBACJ,gBACA,mBACA,qBACA,cACA,eACA,gBAC6B;AAE7B,QAAI,kBACA,mBAAmB,eACnB,mBAAmB,UACnB,mBAAmB;AACrB,aAAO;AAAA,QACL,SAAS;AAAA,QACT,YAAY;AAAA,MAAA;AAAA,IAEhB;AAGA,WAAO,MAAM,KAAK;AAAA,MAChB,uBAAuB;AAAA,MACvB,gBAAgB;AAAA,MAChB,iBAAiB;AAAA,MACjB,kBAAkB;AAAA,IAAA;AAAA,EAEtB;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,mBACJ,QACA,WAC2B;AAC3B,QAAI;AACF,YAAM,UAAkC;AAAA,QACtC,GAAG,KAAK,eAAe,iBAAA;AAAA,MAAiB;AAG1C,UAAI,WAAW;AACb,gBAAQ,cAAc,IAAI;AAAA,MAC5B;AAEA,YAAM,WAAW,MAAM;AAAA,QACrB,GAAG,KAAK,OAAO,qBAAqB,MAAM;AAAA,QAC1C;AAAA,UACE,QAAQ;AAAA,UACR;AAAA,QAAA;AAAA,MACF;AAGF,UAAI,SAAS,WAAW,OAAO,SAAS,WAAW,KAAK;AACtD,cAAM,QAAQ,MAAM,SAAS,KAAA,EAAO,MAAM,OAAO,CAAA,EAAG;AACpD,cAAM,IAAI;AAAA,UACR,MAAM,WAAW;AAAA,QAAA;AAAA,MAErB;AAEA,UAAI,CAAC,SAAS,IAAI;AAChB,cAAM,QAAQ,MAAM,SAAS,KAAA,EAAO,MAAM,OAAO,CAAA,EAAG;AACpD,cAAM,IAAI;AAAA,UACR,MAAM,WAAW,uBAAuB,SAAS,MAAM;AAAA,QAAA;AAAA,MAE3D;AAEA,YAAM,OAAO,MAAM,SAAS,KAAA;AAC5B,WAAK,eAAe,0BAA0B,IAAI;AAElD,aAAO,KAAK,YAAY,CAAA;AAAA,IAC1B,SAAS,OAAY;AACnB,cAAQ,MAAM,kCAAkC,KAAK;AACrD,YAAM,IAAI,MAAM,MAAM,WAAW,gCAAgC;AAAA,IACnE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,iBACE,QACA,WACA,WACA,oBACA,SACM;AAEN,QAAI,KAAK,kBAAkB;AACzB,mBAAa,KAAK,gBAAgB;AAClC,WAAK,mBAAmB;AAAA,IAC1B;AAEA,QAAI,KAAK,MAAM,KAAK,GAAG,eAAe,UAAU,MAAM;AACpD,UAAI,KAAK,OAAO;AACd,gBAAQ,IAAI,6BAA6B;AAAA,MAC3C;AACA;AAAA,IACF;AAGA,SAAK,gBAAgB;AACrB,SAAK,mBAAmB;AAGxB,QAAI,WAAW;AACb,WAAK,gBAAgB,IAAI,SAAS;AAAA,IACpC;AACA,QAAI,oBAAoB;AACtB,WAAK,mBAAmB,IAAI,kBAAkB;AAAA,IAChD;AAIA,UAAM,iBAAiB,aAAa,KAAK,eAAe,kBAAkB;AAC1E,UAAM,QAAQ,GAAG,KAAK,KAAK,eAAe,MAAM,iBAAiB,mBAAmB,cAAc,CAAC;AAEnG,QAAI;AACF,WAAK,KAAK,IAAI,UAAU,KAAK;AAE7B,WAAK,GAAG,SAAS,MAAM;AACrB,YAAI,KAAK,OAAO;AACd,kBAAQ,IAAI,2BAA2B,KAAK;AAAA,QAC9C;AACA,gBAAQ,IAAI,mCAAmC,EAAE,QAAQ,WAAW,OAAO;AAC3E,aAAK,sBAAsB;AAC3B,aAAK,mBAAmB,QAAQ,CAAC,YAAY,QAAQ,IAAI,CAAC;AAC1D,aAAK,kBAAA;AAAA,MACP;AAEA,WAAK,GAAG,YAAY,CAAC,UAAU;AAC7B,YAAI;AACF,gBAAM,UAA4B,KAAK,MAAM,MAAM,IAAI;AAEvD,cAAI,KAAK,OAAO;AACd,oBAAQ,IAAI,mCAAmC,MAAM,IAAI;AACzD,oBAAQ,IAAI,6BAA6B,OAAO;AAAA,UAClD;AAGA,cAAK,QAAgB,YAAY;AAC/B,iBAAK,eAAe,0BAA0B,EAAE,YAAa,QAAgB,YAAY;AAAA,UAC3F;AAGA,cAAI,QAAQ,SAAS,oBAAoB,QAAQ,SAAS,mBAAmB,QAAQ,SAAS,cAAc;AAC1G,oBAAQ,IAAI,qCAAqC;AAAA,cAC/C,MAAM,QAAQ;AAAA,cACd,SAAS,QAAQ;AAAA,cACjB,WAAW,QAAQ;AAAA,cACnB,UAAW,QAAgB;AAAA,cAC3B,aAAc,QAAgB;AAAA,YAAA,CAC/B;AAAA,UACH;AAGA,cAAI,QAAQ,SAAS,QAAQ;AAC3B;AAAA,UACF;AAGA,cAAI,KAAK,SAAS,QAAQ,SAAS,WAAW;AAC5C,oBAAQ,IAAI,4BAA4B;AAAA,cACtC,MAAM,QAAQ;AAAA,cACd,aAAa,QAAQ;AAAA,cACrB,SAAS,QAAQ,SAAS,UAAU,GAAG,EAAE,IAAI;AAAA,cAC7C,IAAI,QAAQ;AAAA,YAAA,CACb;AAAA,UACH;AAEA,eAAK,gBAAgB,QAAQ,CAAC,YAAY,QAAQ,OAAO,CAAC;AAAA,QAC5D,SAAS,OAAO;AACd,kBAAQ,MAAM,oCAAoC,KAAK;AACvD,cAAI,KAAK,OAAO;AACd,oBAAQ,MAAM,qBAAqB,MAAM,IAAI;AAAA,UAC/C;AAAA,QACF;AAAA,MACF;AAEA,WAAK,GAAG,UAAU,CAAC,UAAU;AAC3B,gBAAQ,MAAM,sBAAsB,KAAK;AACzC,aAAK,mBAAmB,QAAQ,CAAC,YAAY,QAAQ,KAAK,CAAC;AAAA,MAC7D;AAEA,WAAK,GAAG,UAAU,CAAC,UAAU;AAC3B,YAAI,KAAK,OAAO;AACd,kBAAQ,IAAI,qBAAqB,MAAM,MAAM,MAAM,MAAM;AAAA,QAC3D;AACA,gBAAQ,IAAI,iCAAiC,EAAE,MAAM,MAAM,MAAM,QAAQ,MAAM,QAAQ,OAAA,CAAQ;AAC/F,aAAK,iBAAA;AACL,aAAK,mBAAmB,QAAQ,CAAC,YAAY,QAAQ,KAAK,CAAC;AAC3D,aAAK,KAAK;AACV,kBAAU,KAAK;AAIf,YAAI,MAAM,SAAS,KAAM;AACvB,eAAK,sBAAsB,KAAK;AAChC,eAAK,gBAAgB;AACrB,eAAK,mBAAmB;AACxB;AAAA,QACF;AAGA,YAAI,KAAK,sBAAsB,KAAK,wBAAwB,KAAK,iBAAiB,KAAK,kBAAkB;AACvG,eAAK;AACL,gBAAM,QAAQ,KAAK,IAAI,MAAO,KAAK,IAAI,GAAG,KAAK,mBAAmB,GAAG,GAAK;AAC1E,cAAI,KAAK,OAAO;AACd,oBAAQ,IAAI,mBAAmB,KAAK,eAAe,KAAK,mBAAmB,IAAI,KAAK,oBAAoB,GAAG;AAAA,UAC7G;AAEA,eAAK,mBAAmB,WAAW,MAAM;AACvC,gBAAI,KAAK,iBAAiB,KAAK,kBAAkB;AAC/C,mBAAK,iBAAiB,KAAK,eAAe,KAAK,kBAAkB,WAAW,oBAAoB,OAAO;AAAA,YACzG;AAAA,UACF,GAAG,KAAK;AAAA,QACV,WAAW,KAAK,OAAO;AACrB,kBAAQ,MAAM,mCAAmC;AAAA,QACnD;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,cAAQ,MAAM,wCAAwC,KAAK;AAC3D,WAAK,mBAAmB,QAAQ,CAAC,YAAY,QAAQ,KAAK,CAAC;AAAA,IAC7D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,wBAAwB,SAA0B;AAChD,QAAI,CAAC,KAAK,MAAM,KAAK,GAAG,eAAe,UAAU,MAAM;AACrD,aAAO;AAAA,IACT;AAEA,QAAI;AACF,WAAK,GAAG;AAAA,QACN,KAAK,UAAU;AAAA,UACb,MAAM;AAAA,UACN,SAAS;AAAA,QAAA,CACV;AAAA,MAAA;AAEH,aAAO;AAAA,IACT,SAAS,OAAO;AACd,cAAQ,MAAM,wCAAwC,KAAK;AAC3D,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,oBAAoB,MAA+C;AACjE,QAAI,CAAC,KAAK,MAAM,KAAK,GAAG,eAAe,UAAU,MAAM;AACrD,aAAO;AAAA,IACT;AAEA,QAAI;AACF,WAAK,GAAG;AAAA,QACN,KAAK,UAAU;AAAA,UACb;AAAA,QAAA,CACD;AAAA,MAAA;AAEH,aAAO;AAAA,IACT,SAAS,OAAO;AACd,cAAQ,MAAM,iBAAiB,IAAI,mBAAmB,KAAK;AAC3D,aAAO;AAAA,IACT;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,oBAA0B;AAChC,SAAK,iBAAA;AACL,SAAK,eAAe,YAAY,MAAM;AACpC,UAAI,KAAK,MAAM,KAAK,GAAG,eAAe,UAAU,MAAM;AACpD,YAAI;AACF,eAAK,GAAG,KAAK,KAAK,UAAU,EAAE,MAAM,OAAA,CAAQ,CAAC;AAAA,QAC/C,SAAS,OAAO;AACd,kBAAQ,MAAM,uBAAuB,KAAK;AAAA,QAC5C;AAAA,MACF;AAAA,IACF,GAAG,GAAK;AAAA,EACV;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAyB;AAC/B,QAAI,KAAK,cAAc;AACrB,oBAAc,KAAK,YAAY;AAC/B,WAAK,eAAe;AAAA,IACtB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,sBAA4B;AAE1B,QAAI,KAAK,kBAAkB;AACzB,mBAAa,KAAK,gBAAgB;AAClC,WAAK,mBAAmB;AAAA,IAC1B;AAGA,SAAK,iBAAA;AAEL,QAAI,KAAK,IAAI;AACX,WAAK,GAAG,MAAA;AACR,WAAK,KAAK;AAAA,IACZ;AACA,SAAK,gBAAgB,MAAA;AACrB,SAAK,mBAAmB,MAAA;AACxB,SAAK,sBAAsB;AAC3B,SAAK,gBAAgB;AACrB,SAAK,mBAAmB;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA,EAKA,uBAAgC;AAC9B,WAAO,KAAK,OAAO,QAAQ,KAAK,GAAG,eAAe,UAAU;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA,EAKA,qBAAqB,SAAoD;AACvE,SAAK,gBAAgB,OAAO,OAAO;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA,EAKA,wBAAwB,SAA6C;AACnE,SAAK,mBAAmB,OAAO,OAAO;AAAA,EACxC;AACF;AAYO,SAAS,kBAAkB,QAAwC;AACxE,SAAO,IAAI,YAAY,MAAM;AAC/B;"}
@@ -1,2 +0,0 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("../node_modules/jose/dist/browser/key/import.cjs.js"),t=require("../node_modules/jose/dist/browser/jwt/sign.cjs.js");async function o(o){if(o.accessToken)return o.accessToken;if(!o.serviceAccountKey)throw new Error("Either serviceAccountKey or accessToken must be provided");return await async function(o){const s=Math.floor(Date.now()/1e3);let r=o.private_key;if(!r)throw new Error("Private key is missing from service account key");if(r=r.trim(),r.includes("-----BEGIN")){if(r=r.replace(/\r\n/g,"\n").replace(/\r/g,"\n"),r.includes("BEGIN RSA PRIVATE KEY"))throw new Error("Private key is in PKCS#1 format (RSA PRIVATE KEY). Please download a new service account key from Google Cloud Console. The key should be in PKCS#8 format (PRIVATE KEY).");const e=r.match(/-----BEGIN PRIVATE KEY-----\n?([\s\S]*?)\n?-----END PRIVATE KEY-----/);if(e){const t=e[1].replace(/\s/g,"");(!t.includes("\n")||t.length>64)&&(r=`-----BEGIN PRIVATE KEY-----\n${t.match(/.{1,64}/g)?.join("\n")||t}\n-----END PRIVATE KEY-----`)}}else{const e=r.replace(/\s/g,"");r=`-----BEGIN PRIVATE KEY-----\n${e.match(/.{1,64}/g)?.join("\n")||e}\n-----END PRIVATE KEY-----`}try{const n=await e.importPKCS8(r,"RS256"),a=await new t.SignJWT({scope:"https://www.googleapis.com/auth/cloud-platform"}).setProtectedHeader({alg:"RS256"}).setIssuedAt(s).setExpirationTime(s+3600).setIssuer(o.client_email).setSubject(o.client_email).setAudience("https://oauth2.googleapis.com/token").sign(n),i=await fetch("https://oauth2.googleapis.com/token",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({grant_type:"urn:ietf:params:oauth:grant-type:jwt-bearer",assertion:a})});if(!i.ok){const e=await i.json();throw new Error(e.error_description||"Failed to get access token")}return(await i.json()).access_token}catch(n){if(n.message&&n.message.includes("pkcs8"))throw new Error("Invalid private key format. The service account key must be in PKCS#8 format. Please ensure your service account key JSON file has a properly formatted private_key field. If you downloaded the key from Google Cloud Console, it should already be in the correct format.");throw n}}(o.serviceAccountKey)}function s(e,t,o,s){return`projects/${e}/locations/${t}/agents/${o}/sessions/${s}`}function r(e){let t=null;for(const o of e)if(o.payload){if(o.payload.richContent){t=o.payload.richContent;break}if(o.payload.fields&&o.payload.fields.richContent){const e=o.payload.fields.richContent;e.listValue&&e.listValue.values?t=e.listValue.values.map(e=>e.listValue&&e.listValue.values?e.listValue.values.map(e=>{if(e.structValue&&e.structValue.fields){const t=e.structValue.fields;if(t.type&&t.options)return{type:t.type.stringValue||t.type,options:t.options.listValue?t.options.listValue.values.map(e=>({text:e.structValue?.fields?.text?.stringValue||"",payload:e.structValue?.fields?.payload?.stringValue||""})):[]}}return e}):e):"object"!=typeof e||e.listValue||(t=e);break}}return t}exports.createDialogflowSession=async function(e){try{const t=await o(e),n=`session_${Date.now()}_${Math.random().toString(36).substr(2,9)}`,a=e.dfLocation.split(" ")[0].trim(),i=`https://${a}-dialogflow.googleapis.com/v3/${s(e.dfProjectId,a,e.dfAgentId,n)}:detectIntent`,l={queryInput:{text:{text:"hello"},languageCode:e.languageCode||"en"}},c=await fetch(i,{method:"POST",headers:{Authorization:`Bearer ${t}`,"Content-Type":"application/json"},body:JSON.stringify(l)});if(!c.ok){const e=await c.json().catch(()=>({}));throw new Error(e.error?.message||`HTTP error! status: ${c.status}`)}const u=await c.json();let d="Hello! I'm BlockSpark AI Assistant. How can I help you today?",f=null;if(u.queryResult?.responseMessages){const e=u.queryResult.responseMessages.filter(e=>e.text).map(e=>e.text.text.join(" "));e.length>0&&(d=e.join(" ")),f=r(u.queryResult.responseMessages)}else u.queryResult?.fulfillmentText&&(d=u.queryResult.fulfillmentText);return{session_id:n,message:d,...f&&{richContent:f}}}catch(t){const e=t.message||"Failed to create session";if(e.includes("401")||e.includes("Unauthorized"))throw new Error("Authentication failed. Please check your service account key or access token.");if(e.includes("403")||e.includes("Forbidden"))throw new Error("Access forbidden. Please check your Dialogflow API permissions.");if(e.includes("404")||e.includes("Not Found"))throw new Error("Dialogflow agent not found. Please check your project ID, location, and agent ID.");throw new Error(e)}},exports.sendDialogflowMessage=async function(e,t,n){try{const a=await o(n),i=n.dfLocation.split(" ")[0].trim(),l=`https://${i}-dialogflow.googleapis.com/v3/${s(n.dfProjectId,i,n.dfAgentId,t)}:detectIntent`,c={queryInput:{text:{text:e.trim()},languageCode:n.languageCode||"en"}},u=await fetch(l,{method:"POST",headers:{Authorization:`Bearer ${a}`,"Content-Type":"application/json"},body:JSON.stringify(c)});if(!u.ok){const e=await u.text();let t={};try{t=JSON.parse(e)}catch{t={message:e||`HTTP ${u.status}`}}const o=t.error?.message||t.message||`HTTP error! status: ${u.status}`;throw new Error(o)}const d=await u.json();let f="I'm sorry, I didn't understand that. Could you please rephrase?",p=null,y=!1;if(!0===d.queryResult?.parameters?.fields?.handoff?.boolValue)y=!0;else if(d.queryResult?.responseMessages)for(const e of d.queryResult.responseMessages)if(e.payload&&"object"==typeof e.payload){if(!0===e.payload.handoff){y=!0;break}if(!0===e.payload.fields?.handoff?.boolValue){y=!0;break}}if(!0===d.handoff&&(y=!0),d.queryResult?.responseMessages){const e=d.queryResult.responseMessages.filter(e=>e.text).map(e=>e.text.text.join(" "));e.length>0&&(f=e.join(" ")),p=r(d.queryResult.responseMessages)}else d.queryResult?.fulfillmentText&&(f=d.queryResult.fulfillmentText);return{response:f,session_id:t,source:"dialogflow",timestamp:(new Date).toISOString(),...p&&{richContent:p},...y&&{handoff:!0}}}catch(a){const e=a.message||"Failed to send message";if(e.includes("401")||e.includes("Unauthorized"))throw new Error("Authentication failed. Please check your service account key or access token.");if(e.includes("403")||e.includes("Forbidden"))throw new Error("Access forbidden. Please check your Dialogflow API permissions.");if(e.includes("404")||e.includes("Not Found"))throw new Error("Dialogflow agent not found. Please check your project ID, location, and agent ID.");if(e.includes("CORS"))throw new Error("CORS error. Dialogflow API may not allow browser requests. Consider using a backend proxy.");throw new Error(e)}};
2
- //# sourceMappingURL=dialogflowClient.cjs.js.map