@elqnt/chat 3.1.0 → 3.5.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.
- package/README.md +6 -0
- package/SKILL.md +531 -0
- package/TIER2_AUTH.md +258 -0
- package/dist/api/index.d.mts +11 -0
- package/dist/api/index.d.ts +11 -0
- package/dist/api/index.js +27 -9
- package/dist/api/index.js.map +1 -1
- package/dist/api/index.mjs +27 -10
- package/dist/api/index.mjs.map +1 -1
- package/dist/hooks/index.d.mts +75 -37
- package/dist/hooks/index.d.ts +75 -37
- package/dist/hooks/index.js +116 -16
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/index.mjs +115 -15
- package/dist/hooks/index.mjs.map +1 -1
- package/dist/index.d.mts +2 -7
- package/dist/index.d.ts +2 -7
- package/dist/index.js +14 -1420
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +9 -1413
- package/dist/index.mjs.map +1 -1
- package/dist/models/index.d.mts +89 -4
- package/dist/models/index.d.ts +89 -4
- package/dist/models/index.js +12 -1
- package/dist/models/index.js.map +1 -1
- package/dist/models/index.mjs +8 -2
- package/dist/models/index.mjs.map +1 -1
- package/dist/transport/index.d.mts +2 -2
- package/dist/transport/index.d.ts +2 -2
- package/dist/transport/index.js +100 -11
- package/dist/transport/index.js.map +1 -1
- package/dist/transport/index.mjs +100 -12
- package/dist/transport/index.mjs.map +1 -1
- package/dist/{types-CQHtUQ6p.d.mts → types-CLtQA6Qq.d.mts} +16 -0
- package/dist/{types-7UNI1iYv.d.ts → types-CxibhkqW.d.ts} +16 -0
- package/package.json +8 -6
package/dist/transport/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use client";
|
|
2
1
|
"use strict";
|
|
3
2
|
var __defProp = Object.defineProperty;
|
|
4
3
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
@@ -32,6 +31,18 @@ __export(transport_exports, {
|
|
|
32
31
|
module.exports = __toCommonJS(transport_exports);
|
|
33
32
|
|
|
34
33
|
// transport/types.ts
|
|
34
|
+
async function resolveTransportToken(cfg) {
|
|
35
|
+
if (!cfg) return void 0;
|
|
36
|
+
if (cfg.getToken) {
|
|
37
|
+
try {
|
|
38
|
+
const t = await cfg.getToken();
|
|
39
|
+
return t ?? void 0;
|
|
40
|
+
} catch {
|
|
41
|
+
return void 0;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return cfg.token ?? void 0;
|
|
45
|
+
}
|
|
35
46
|
function createLogger(debug = false) {
|
|
36
47
|
return {
|
|
37
48
|
debug: debug ? console.log.bind(console, "[chat]") : () => {
|
|
@@ -112,9 +123,12 @@ function createSSETransport(options = {}) {
|
|
|
112
123
|
}
|
|
113
124
|
const url = `${config.baseUrl}/${endpoint}`;
|
|
114
125
|
logger.debug(`POST ${endpoint}`, body);
|
|
126
|
+
const token = await resolveTransportToken(config);
|
|
127
|
+
const headers = { "Content-Type": "application/json" };
|
|
128
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
115
129
|
const response = await fetch(url, {
|
|
116
130
|
method: "POST",
|
|
117
|
-
headers
|
|
131
|
+
headers,
|
|
118
132
|
body: JSON.stringify(body)
|
|
119
133
|
});
|
|
120
134
|
if (!response.ok) {
|
|
@@ -140,6 +154,7 @@ function createSSETransport(options = {}) {
|
|
|
140
154
|
function setupEventListeners(es) {
|
|
141
155
|
es.addEventListener("message", handleMessage);
|
|
142
156
|
const eventTypes = [
|
|
157
|
+
"error",
|
|
143
158
|
"reconnected",
|
|
144
159
|
"typing",
|
|
145
160
|
"stopped_typing",
|
|
@@ -149,21 +164,53 @@ function createSSETransport(options = {}) {
|
|
|
149
164
|
"human_agent_left",
|
|
150
165
|
"chat_ended",
|
|
151
166
|
"chat_updated",
|
|
167
|
+
"chat_removed",
|
|
152
168
|
"load_chat_response",
|
|
153
169
|
"new_chat_created",
|
|
154
170
|
"show_csat_survey",
|
|
155
171
|
"csat_response",
|
|
156
172
|
"user_suggested_actions",
|
|
173
|
+
"user_suggested_action_selected",
|
|
157
174
|
"agent_execution_started",
|
|
158
175
|
"agent_execution_ended",
|
|
159
176
|
"agent_context_update",
|
|
177
|
+
"load_agent_context_response",
|
|
160
178
|
"plan_pending_approval",
|
|
179
|
+
"plan_approved",
|
|
180
|
+
"plan_rejected",
|
|
161
181
|
"step_started",
|
|
162
182
|
"step_completed",
|
|
163
183
|
"step_failed",
|
|
164
184
|
"plan_completed",
|
|
165
185
|
"skills_changed",
|
|
166
|
-
"summary_update"
|
|
186
|
+
"summary_update",
|
|
187
|
+
"message_status_update",
|
|
188
|
+
"delivered",
|
|
189
|
+
"read",
|
|
190
|
+
"sync_metadata_response",
|
|
191
|
+
"sync_user_session_response",
|
|
192
|
+
"client_action",
|
|
193
|
+
"client_action_callback",
|
|
194
|
+
"attachment_processing_started",
|
|
195
|
+
"attachment_processing_progress",
|
|
196
|
+
"attachment_processing_complete",
|
|
197
|
+
"attachment_processing_error",
|
|
198
|
+
"observer_joined",
|
|
199
|
+
"observer_left",
|
|
200
|
+
"block_user",
|
|
201
|
+
"message_edited",
|
|
202
|
+
"message_deleted",
|
|
203
|
+
"message_reaction",
|
|
204
|
+
"user_presence_changed",
|
|
205
|
+
"online_users",
|
|
206
|
+
"get_agents_response",
|
|
207
|
+
"room_created",
|
|
208
|
+
"room_updated",
|
|
209
|
+
"room_deleted",
|
|
210
|
+
"rooms_response",
|
|
211
|
+
"room_user_joined",
|
|
212
|
+
"room_user_left",
|
|
213
|
+
"user_invited"
|
|
167
214
|
];
|
|
168
215
|
eventTypes.forEach((type) => {
|
|
169
216
|
es.addEventListener(type, handleMessage);
|
|
@@ -208,20 +255,38 @@ function createSSETransport(options = {}) {
|
|
|
208
255
|
reconnectTimeout = void 0;
|
|
209
256
|
}
|
|
210
257
|
state = retryCount > 0 ? "reconnecting" : "connecting";
|
|
258
|
+
const connectToken = await resolveTransportToken(cfg);
|
|
211
259
|
return new Promise((resolve, reject) => {
|
|
212
260
|
const connectionStart = Date.now();
|
|
213
|
-
const
|
|
261
|
+
const streamParams = new URLSearchParams({
|
|
262
|
+
orgId: cfg.orgId,
|
|
263
|
+
userId: cfg.userId,
|
|
264
|
+
clientType: cfg.clientType
|
|
265
|
+
});
|
|
266
|
+
if (cfg.chatKey) streamParams.set("chatId", cfg.chatKey);
|
|
267
|
+
if (connectToken) streamParams.set("token", connectToken);
|
|
268
|
+
const url = `${cfg.baseUrl}/stream?${streamParams.toString()}`;
|
|
214
269
|
logger.debug("Connecting to:", url);
|
|
215
270
|
const es = new EventSource(url);
|
|
216
271
|
es.onopen = () => {
|
|
217
272
|
const connectionTime = Date.now() - connectionStart;
|
|
218
273
|
logger.info(`Connected in ${connectionTime}ms`);
|
|
274
|
+
const wasReconnect = retryCount > 0;
|
|
219
275
|
state = "connected";
|
|
220
276
|
error = void 0;
|
|
221
277
|
retryCount = 0;
|
|
222
278
|
metrics.connectedAt = Date.now();
|
|
223
279
|
metrics.latency = connectionTime;
|
|
224
280
|
setupEventListeners(es);
|
|
281
|
+
if (wasReconnect) {
|
|
282
|
+
emit({
|
|
283
|
+
type: "transport_reconnected",
|
|
284
|
+
orgId: cfg.orgId,
|
|
285
|
+
chatKey: cfg.chatKey || "",
|
|
286
|
+
userId: cfg.userId,
|
|
287
|
+
timestamp: Date.now()
|
|
288
|
+
});
|
|
289
|
+
}
|
|
225
290
|
resolve();
|
|
226
291
|
};
|
|
227
292
|
es.onerror = () => {
|
|
@@ -453,9 +518,12 @@ function createFetchSSETransport(options = {}) {
|
|
|
453
518
|
}
|
|
454
519
|
const url = `${config.baseUrl}/${endpoint}`;
|
|
455
520
|
logger.debug(`POST ${endpoint}`, body);
|
|
521
|
+
const token = await resolveTransportToken(config);
|
|
522
|
+
const headers = { "Content-Type": "application/json" };
|
|
523
|
+
if (token) headers["Authorization"] = `Bearer ${token}`;
|
|
456
524
|
const response = await customFetch(url, {
|
|
457
525
|
method: "POST",
|
|
458
|
-
headers
|
|
526
|
+
headers,
|
|
459
527
|
body: JSON.stringify(body)
|
|
460
528
|
});
|
|
461
529
|
if (!response.ok) {
|
|
@@ -492,15 +560,25 @@ function createFetchSSETransport(options = {}) {
|
|
|
492
560
|
return events;
|
|
493
561
|
}
|
|
494
562
|
async function startStream(cfg) {
|
|
495
|
-
const
|
|
563
|
+
const streamParams = new URLSearchParams({
|
|
564
|
+
orgId: cfg.orgId,
|
|
565
|
+
userId: cfg.userId,
|
|
566
|
+
clientType: cfg.clientType
|
|
567
|
+
});
|
|
568
|
+
if (cfg.chatKey) streamParams.set("chatId", cfg.chatKey);
|
|
569
|
+
const token = await resolveTransportToken(cfg);
|
|
570
|
+
if (token) streamParams.set("token", token);
|
|
571
|
+
const url = `${cfg.baseUrl}/stream?${streamParams.toString()}`;
|
|
496
572
|
logger.debug("Connecting to:", url);
|
|
497
573
|
abortController = new AbortController();
|
|
574
|
+
const streamHeaders = {
|
|
575
|
+
Accept: "text/event-stream",
|
|
576
|
+
"Cache-Control": "no-cache"
|
|
577
|
+
};
|
|
578
|
+
if (token) streamHeaders["Authorization"] = `Bearer ${token}`;
|
|
498
579
|
const response = await customFetch(url, {
|
|
499
580
|
method: "GET",
|
|
500
|
-
headers:
|
|
501
|
-
Accept: "text/event-stream",
|
|
502
|
-
"Cache-Control": "no-cache"
|
|
503
|
-
},
|
|
581
|
+
headers: streamHeaders,
|
|
504
582
|
signal: abortController.signal
|
|
505
583
|
});
|
|
506
584
|
if (!response.ok) {
|
|
@@ -598,6 +676,7 @@ function createFetchSSETransport(options = {}) {
|
|
|
598
676
|
state = retryCount > 0 ? "reconnecting" : "connecting";
|
|
599
677
|
const connectionStart = Date.now();
|
|
600
678
|
try {
|
|
679
|
+
const wasReconnect = retryCount > 0;
|
|
601
680
|
await startStream(cfg);
|
|
602
681
|
const connectionTime = Date.now() - connectionStart;
|
|
603
682
|
logger.info(`Connected in ${connectionTime}ms`);
|
|
@@ -606,6 +685,15 @@ function createFetchSSETransport(options = {}) {
|
|
|
606
685
|
retryCount = 0;
|
|
607
686
|
metrics.connectedAt = Date.now();
|
|
608
687
|
metrics.latency = connectionTime;
|
|
688
|
+
if (wasReconnect) {
|
|
689
|
+
emit({
|
|
690
|
+
type: "transport_reconnected",
|
|
691
|
+
orgId: cfg.orgId,
|
|
692
|
+
chatKey: cfg.chatKey || "",
|
|
693
|
+
userId: cfg.userId,
|
|
694
|
+
timestamp: Date.now()
|
|
695
|
+
});
|
|
696
|
+
}
|
|
609
697
|
} catch (err) {
|
|
610
698
|
const connectError = {
|
|
611
699
|
code: "CONNECTION_FAILED",
|
|
@@ -647,7 +735,8 @@ function createFetchSSETransport(options = {}) {
|
|
|
647
735
|
orgId: event.orgId,
|
|
648
736
|
chatKey: event.chatKey,
|
|
649
737
|
userId: event.userId,
|
|
650
|
-
message: event.message
|
|
738
|
+
message: event.message,
|
|
739
|
+
...event.data ? { data: event.data } : {}
|
|
651
740
|
});
|
|
652
741
|
break;
|
|
653
742
|
case "typing":
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../transport/index.ts","../../transport/types.ts","../../transport/sse.ts","../../transport/sse-fetch.ts","../../transport/whatsapp.ts"],"sourcesContent":["/**\n * Transport Layer\n *\n * Platform-agnostic transport abstractions for chat communication.\n *\n * @example Browser (SSE)\n * ```typescript\n * import { createSSETransport } from \"@elqnt/chat/transport\";\n *\n * const transport = createSSETransport({ debug: true });\n * await transport.connect({ baseUrl, orgId, userId, clientType: \"customer\" });\n *\n * transport.onMessage((event) => {\n * console.log(\"Received:\", event);\n * });\n * ```\n *\n * @example React Native (Fetch-based SSE)\n * ```typescript\n * import { createFetchSSETransport } from \"@elqnt/chat/transport\";\n *\n * const transport = createFetchSSETransport();\n * await transport.connect({ baseUrl, orgId, userId, clientType: \"customer\" });\n * ```\n *\n * @example Custom Transport\n * ```typescript\n * import type { ChatTransport } from \"@elqnt/chat/transport\";\n *\n * const customTransport: ChatTransport = {\n * connect: async (config) => { ... },\n * disconnect: () => { ... },\n * send: async (event) => { ... },\n * sendMessage: async (message) => { ... },\n * onMessage: (handler) => { ... },\n * on: (eventType, handler) => { ... },\n * getState: () => { ... },\n * getMetrics: () => { ... },\n * getError: () => { ... },\n * clearError: () => { ... },\n * };\n * ```\n */\n\n// Types\nexport type {\n ChatTransport,\n TransportConfig,\n TransportState,\n TransportError,\n TransportFactory,\n TransportLogger,\n ConnectionMetrics,\n EventHandler,\n Unsubscribe,\n RetryConfig,\n SendMessageOptions,\n CreateChatOptions,\n LoadChatOptions,\n EndChatOptions,\n} from \"./types\";\n\nexport {\n createLogger,\n calculateRetryInterval,\n DEFAULT_RETRY_CONFIG,\n} from \"./types\";\n\n// Browser SSE transport\nexport { createSSETransport } from \"./sse\";\nexport type { SSETransportOptions } from \"./sse\";\n\n// Fetch-based SSE transport (React Native)\nexport { createFetchSSETransport } from \"./sse-fetch\";\nexport type { FetchSSETransportOptions } from \"./sse-fetch\";\n\n// WhatsApp Business API transport (stub)\nexport {\n createWhatsAppTransport,\n mapWhatsAppToChatEvent,\n} from \"./whatsapp\";\nexport type {\n WhatsAppTransportConfig,\n WhatsAppTransportOptions,\n WhatsAppMessageType,\n WhatsAppWebhookPayload,\n} from \"./whatsapp\";\n","/**\n * Transport Abstraction Layer Types\n *\n * Platform-agnostic transport interfaces for chat communication.\n * Supports browser SSE, React Native fetch-based SSE, and extensible\n * transports like WhatsApp Business API.\n */\n\nimport type { Chat, ChatEvent, ChatMessage } from \"../models\";\n\n/**\n * Transport connection state\n */\nexport type TransportState =\n | \"disconnected\"\n | \"connecting\"\n | \"connected\"\n | \"reconnecting\";\n\n/**\n * Transport error with retry information\n */\nexport interface TransportError {\n code:\n | \"CONNECTION_FAILED\"\n | \"PARSE_ERROR\"\n | \"SEND_FAILED\"\n | \"TIMEOUT\"\n | \"NETWORK_ERROR\"\n | \"AUTH_FAILED\";\n message: string;\n retryable: boolean;\n timestamp: number;\n originalError?: unknown;\n}\n\n/**\n * Configuration for transport connection\n */\nexport interface TransportConfig {\n /** Base URL for the chat server */\n baseUrl: string;\n /** Organization ID */\n orgId: string;\n /** User ID for authentication */\n userId: string;\n /** Client type for routing */\n clientType: \"customer\" | \"humanAgent\" | \"observer\";\n /** Optional current chat key for reconnection */\n chatKey?: string;\n /** Enable debug logging */\n debug?: boolean;\n}\n\n/**\n * Retry configuration for connection attempts\n */\nexport interface RetryConfig {\n /** Maximum number of retry attempts */\n maxRetries?: number;\n /** Initial retry intervals in milliseconds */\n intervals?: number[];\n /** Multiplier for exponential backoff */\n backoffMultiplier?: number;\n /** Maximum backoff time in milliseconds */\n maxBackoffTime?: number;\n}\n\n/**\n * Connection metrics for monitoring\n */\nexport interface ConnectionMetrics {\n /** Round-trip latency in milliseconds */\n latency: number;\n /** Total messages sent */\n messagesSent: number;\n /** Total messages received */\n messagesReceived: number;\n /** Messages waiting to be sent */\n messagesQueued: number;\n /** Number of reconnection attempts */\n reconnectCount: number;\n /** Last error encountered */\n lastError?: TransportError;\n /** Timestamp when connection was established */\n connectedAt?: number;\n /** Timestamp of last message */\n lastMessageAt?: number;\n /** Current transport type */\n transportType?: string;\n}\n\n/**\n * Event handler function type\n */\nexport type EventHandler<T = ChatEvent> = (event: T) => void;\n\n/**\n * Unsubscribe function returned by event subscription\n */\nexport type Unsubscribe = () => void;\n\n/**\n * Send message options\n */\nexport interface SendMessageOptions {\n /** Message content */\n content: string;\n /** Optional attachments */\n attachments?: unknown[];\n /** Optional message metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Create chat options\n */\nexport interface CreateChatOptions {\n /** Organization ID */\n orgId: string;\n /** User ID */\n userId: string;\n /** Optional chat metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Load chat options\n */\nexport interface LoadChatOptions {\n /** Organization ID */\n orgId: string;\n /** Chat key to load */\n chatKey: string;\n /** User ID */\n userId: string;\n}\n\n/**\n * End chat options\n */\nexport interface EndChatOptions {\n /** Organization ID */\n orgId: string;\n /** Chat key to end */\n chatKey: string;\n /** User ID */\n userId: string;\n /** Optional end reason */\n reason?: string;\n}\n\n/**\n * Response from creating a new chat\n */\nexport interface CreateChatResponse {\n /** The key of the created chat */\n chatKey: string;\n}\n\n/**\n * Response from loading a chat\n */\nexport interface LoadChatResponse {\n /** The loaded chat object */\n chat: Chat;\n /** Agent ID if applicable */\n agentId?: string;\n}\n\n/**\n * Core transport interface\n *\n * Implementations must handle:\n * - Connection lifecycle (connect, disconnect, reconnect)\n * - Message sending (POST requests)\n * - Event receiving (SSE stream)\n * - Error handling and recovery\n */\nexport interface ChatTransport {\n /**\n * Connect to the chat server\n * @param config - Transport configuration\n * @returns Promise that resolves when connected\n */\n connect(config: TransportConfig): Promise<void>;\n\n /**\n * Disconnect from the server\n * @param intentional - Whether disconnect was user-initiated\n */\n disconnect(intentional?: boolean): void;\n\n /**\n * Send a chat event to the server\n * @param event - Chat event to send\n */\n send(event: ChatEvent): Promise<void>;\n\n /**\n * Send a message in the current chat\n * @param message - Message to send\n */\n sendMessage(message: ChatMessage): Promise<void>;\n\n /**\n * Create a new chat and return the chat key directly\n * @param options - Create chat options\n * @returns Promise with the created chat key\n */\n createChat(options: CreateChatOptions): Promise<CreateChatResponse>;\n\n /**\n * Load an existing chat and return the chat data directly\n * @param options - Load chat options\n * @returns Promise with the loaded chat data\n */\n loadChatData(options: LoadChatOptions): Promise<LoadChatResponse>;\n\n /**\n * Subscribe to incoming events\n * @param handler - Event handler function\n * @returns Unsubscribe function\n */\n onMessage(handler: EventHandler): Unsubscribe;\n\n /**\n * Subscribe to specific event type\n * @param eventType - Type of event to listen for\n * @param handler - Event handler function\n * @returns Unsubscribe function\n */\n on(eventType: string, handler: EventHandler): Unsubscribe;\n\n /**\n * Get current connection state\n */\n getState(): TransportState;\n\n /**\n * Get connection metrics\n */\n getMetrics(): ConnectionMetrics;\n\n /**\n * Get last error\n */\n getError(): TransportError | undefined;\n\n /**\n * Clear error state\n */\n clearError(): void;\n}\n\n/**\n * Transport factory function type\n */\nexport type TransportFactory = (config?: Partial<TransportConfig>) => ChatTransport;\n\n/**\n * Logger interface for transport debugging\n */\nexport interface TransportLogger {\n debug: (message: string, ...args: unknown[]) => void;\n info: (message: string, ...args: unknown[]) => void;\n warn: (message: string, ...args: unknown[]) => void;\n error: (message: string, ...args: unknown[]) => void;\n}\n\n/**\n * Create a default logger\n * @param debug - Enable debug logging\n */\nexport function createLogger(debug: boolean = false): TransportLogger {\n return {\n debug: debug ? console.log.bind(console, \"[chat]\") : () => {},\n info: console.info.bind(console, \"[chat]\"),\n warn: console.warn.bind(console, \"[chat]\"),\n error: console.error.bind(console, \"[chat]\"),\n };\n}\n\n/**\n * Default retry configuration\n */\nexport const DEFAULT_RETRY_CONFIG: Required<RetryConfig> = {\n maxRetries: 10,\n intervals: [1000, 2000, 5000],\n backoffMultiplier: 1.5,\n maxBackoffTime: 30000,\n};\n\n/**\n * Calculate retry interval with exponential backoff\n * @param retryCount - Current retry attempt number\n * @param config - Retry configuration\n */\nexport function calculateRetryInterval(\n retryCount: number,\n config: RetryConfig = DEFAULT_RETRY_CONFIG\n): number {\n const {\n intervals = DEFAULT_RETRY_CONFIG.intervals,\n backoffMultiplier = DEFAULT_RETRY_CONFIG.backoffMultiplier,\n maxBackoffTime = DEFAULT_RETRY_CONFIG.maxBackoffTime,\n } = config;\n\n if (retryCount < intervals.length) {\n return intervals[retryCount];\n }\n\n const baseInterval = intervals[intervals.length - 1] || 5000;\n const backoffTime =\n baseInterval * Math.pow(backoffMultiplier, retryCount - intervals.length + 1);\n return Math.min(backoffTime, maxBackoffTime);\n}\n","/**\n * SSE Transport (Browser)\n *\n * Uses native EventSource for receiving server events and fetch POST for sending.\n * This is the default transport for browser environments.\n *\n * @example\n * ```typescript\n * import { createSSETransport } from \"@elqnt/chat/transport\";\n *\n * const transport = createSSETransport();\n * await transport.connect({ baseUrl, orgId, userId, clientType: \"customer\" });\n * ```\n */\n\nimport type { Chat, ChatEvent, ChatMessage } from \"../models\";\nimport type {\n ChatTransport,\n TransportConfig,\n TransportState,\n TransportError,\n ConnectionMetrics,\n EventHandler,\n Unsubscribe,\n RetryConfig,\n TransportLogger,\n CreateChatOptions,\n CreateChatResponse,\n LoadChatOptions,\n LoadChatResponse,\n} from \"./types\";\nimport { createLogger, calculateRetryInterval, DEFAULT_RETRY_CONFIG } from \"./types\";\n\n/**\n * SSE Transport options\n */\nexport interface SSETransportOptions {\n /** Retry configuration */\n retryConfig?: RetryConfig;\n /** Enable debug logging */\n debug?: boolean;\n /** Custom logger */\n logger?: TransportLogger;\n}\n\n/**\n * Create an SSE transport for browser environments\n */\nexport function createSSETransport(options: SSETransportOptions = {}): ChatTransport {\n const {\n retryConfig = DEFAULT_RETRY_CONFIG,\n debug = false,\n logger = createLogger(debug),\n } = options;\n\n // Internal state\n let eventSource: EventSource | undefined;\n let config: TransportConfig | undefined;\n let state: TransportState = \"disconnected\";\n let error: TransportError | undefined;\n let retryCount = 0;\n let reconnectTimeout: ReturnType<typeof setTimeout> | undefined;\n let intentionalDisconnect = false;\n\n // Metrics\n const metrics: ConnectionMetrics = {\n latency: 0,\n messagesSent: 0,\n messagesReceived: 0,\n messagesQueued: 0,\n reconnectCount: 0,\n transportType: \"sse\",\n };\n\n // Event handlers\n const globalHandlers = new Set<EventHandler>();\n const typeHandlers = new Map<string, Set<EventHandler>>();\n\n // Helper to emit events\n function emit(event: ChatEvent): void {\n // Update metrics\n metrics.messagesReceived++;\n metrics.lastMessageAt = Date.now();\n\n // Call global handlers\n globalHandlers.forEach((handler) => {\n try {\n handler(event);\n } catch (err) {\n logger.error(\"Error in message handler:\", err);\n }\n });\n\n // Call type-specific handlers\n const handlers = typeHandlers.get(event.type);\n if (handlers) {\n handlers.forEach((handler) => {\n try {\n handler(event);\n } catch (err) {\n logger.error(`Error in ${event.type} handler:`, err);\n }\n });\n }\n }\n\n // REST API helper\n async function sendRest(endpoint: string, body: Record<string, unknown>): Promise<unknown> {\n if (!config) {\n throw new Error(\"Transport not connected\");\n }\n\n const url = `${config.baseUrl}/${endpoint}`;\n logger.debug(`POST ${endpoint}`, body);\n\n const response = await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`API error: ${response.status} - ${errorText}`);\n }\n\n const json = await response.json() as { success?: boolean; data?: unknown };\n\n // Backend wraps responses in { success: true, data: {...} }\n // Unwrap to return just the data\n if (json && typeof json === \"object\" && \"data\" in json) {\n return json.data;\n }\n\n return json;\n }\n\n // Handle SSE message\n function handleMessage(event: MessageEvent): void {\n if (!event.data || event.data === \"\") return;\n\n try {\n const data = JSON.parse(event.data) as ChatEvent;\n logger.debug(\"Received:\", data.type);\n emit(data);\n } catch (err) {\n logger.error(\"Failed to parse SSE message:\", err);\n }\n }\n\n // Setup SSE event listeners\n function setupEventListeners(es: EventSource): void {\n // Generic message handler\n es.addEventListener(\"message\", handleMessage);\n\n // Add handlers for known event types\n const eventTypes = [\n \"reconnected\", \"typing\", \"stopped_typing\", \"waiting\",\n \"waiting_for_agent\", \"human_agent_joined\", \"human_agent_left\",\n \"chat_ended\", \"chat_updated\", \"load_chat_response\",\n \"new_chat_created\", \"show_csat_survey\", \"csat_response\",\n \"user_suggested_actions\", \"agent_execution_started\",\n \"agent_execution_ended\", \"agent_context_update\",\n \"plan_pending_approval\", \"step_started\", \"step_completed\",\n \"step_failed\", \"plan_completed\", \"skills_changed\", \"summary_update\",\n ];\n\n eventTypes.forEach((type) => {\n es.addEventListener(type, handleMessage);\n });\n }\n\n // Schedule reconnect\n function scheduleReconnect(): void {\n if (intentionalDisconnect || !config) return;\n\n const maxRetries = retryConfig.maxRetries ?? DEFAULT_RETRY_CONFIG.maxRetries;\n if (retryCount >= maxRetries) {\n logger.error(`Max retries (${maxRetries}) exceeded`);\n error = {\n code: \"CONNECTION_FAILED\",\n message: `Max retries (${maxRetries}) exceeded`,\n retryable: false,\n timestamp: Date.now(),\n };\n return;\n }\n\n const interval = calculateRetryInterval(retryCount, retryConfig);\n retryCount++;\n metrics.reconnectCount++;\n\n logger.info(`Reconnecting in ${interval}ms (attempt ${retryCount})`);\n state = \"reconnecting\";\n\n reconnectTimeout = setTimeout(() => {\n if (config) {\n transport.connect(config).catch((err) => {\n logger.error(\"Reconnect failed:\", err);\n });\n }\n }, interval);\n }\n\n const transport: ChatTransport = {\n async connect(cfg: TransportConfig): Promise<void> {\n config = cfg;\n intentionalDisconnect = false;\n\n // Clean up existing connection\n if (eventSource) {\n eventSource.close();\n eventSource = undefined;\n }\n if (reconnectTimeout) {\n clearTimeout(reconnectTimeout);\n reconnectTimeout = undefined;\n }\n\n state = retryCount > 0 ? \"reconnecting\" : \"connecting\";\n\n return new Promise((resolve, reject) => {\n const connectionStart = Date.now();\n const url = `${cfg.baseUrl}/stream?orgId=${cfg.orgId}&userId=${cfg.userId}&clientType=${cfg.clientType}${cfg.chatKey ? `&chatId=${cfg.chatKey}` : \"\"}`;\n\n logger.debug(\"Connecting to:\", url);\n const es = new EventSource(url);\n\n es.onopen = () => {\n const connectionTime = Date.now() - connectionStart;\n logger.info(`Connected in ${connectionTime}ms`);\n\n state = \"connected\";\n error = undefined;\n retryCount = 0;\n metrics.connectedAt = Date.now();\n metrics.latency = connectionTime;\n\n setupEventListeners(es);\n resolve();\n };\n\n es.onerror = () => {\n if (es.readyState === EventSource.CLOSED) {\n const sseError: TransportError = {\n code: \"CONNECTION_FAILED\",\n message: \"SSE connection failed\",\n retryable: true,\n timestamp: Date.now(),\n };\n error = sseError;\n metrics.lastError = sseError;\n state = \"disconnected\";\n\n if (!intentionalDisconnect) {\n scheduleReconnect();\n }\n\n // Only reject if this is the initial connection attempt\n if (retryCount === 0) {\n reject(sseError);\n }\n }\n };\n\n eventSource = es;\n });\n },\n\n disconnect(intentional = true): void {\n logger.info(\"Disconnecting\", { intentional });\n intentionalDisconnect = intentional;\n\n if (reconnectTimeout) {\n clearTimeout(reconnectTimeout);\n reconnectTimeout = undefined;\n }\n\n if (eventSource) {\n eventSource.close();\n eventSource = undefined;\n }\n\n state = \"disconnected\";\n retryCount = 0;\n },\n\n async send(event: ChatEvent): Promise<void> {\n if (!config) {\n throw new Error(\"Transport not connected\");\n }\n\n // Map event types to REST endpoints\n switch (event.type) {\n case \"message\":\n await sendRest(\"send\", {\n orgId: event.orgId,\n chatKey: event.chatKey,\n userId: event.userId,\n message: event.message,\n ...(event.data ? { data: event.data } : {}),\n });\n break;\n\n case \"typing\":\n await sendRest(\"typing\", {\n orgId: event.orgId,\n chatKey: event.chatKey,\n userId: event.userId,\n typing: true,\n });\n break;\n\n case \"stopped_typing\":\n await sendRest(\"typing\", {\n orgId: event.orgId,\n chatKey: event.chatKey,\n userId: event.userId,\n typing: false,\n });\n break;\n\n case \"load_chat\":\n // Deprecated: Use loadChatData() instead for direct response\n await sendRest(\"load\", {\n orgId: event.orgId,\n chatKey: event.chatKey,\n userId: event.userId,\n });\n break;\n\n case \"new_chat\":\n // Deprecated: Use createChat() instead for direct response\n await sendRest(\"create\", {\n orgId: event.orgId,\n userId: event.userId,\n metadata: event.data,\n });\n break;\n\n case \"end_chat\":\n await sendRest(\"end\", {\n orgId: event.orgId,\n chatKey: event.chatKey,\n userId: event.userId,\n data: event.data,\n });\n break;\n\n default:\n // Generic event endpoint for other types\n await sendRest(\"event\", {\n type: event.type,\n orgId: event.orgId,\n chatKey: event.chatKey,\n userId: event.userId,\n data: event.data,\n });\n }\n\n metrics.messagesSent++;\n },\n\n async sendMessage(message: ChatMessage): Promise<void> {\n if (!config) {\n throw new Error(\"Transport not connected\");\n }\n\n await sendRest(\"send\", {\n orgId: config.orgId,\n chatKey: config.chatKey,\n userId: config.userId,\n message,\n });\n\n metrics.messagesSent++;\n },\n\n async createChat(options: CreateChatOptions): Promise<CreateChatResponse> {\n if (!config) {\n throw new Error(\"Transport not connected\");\n }\n\n const response = await sendRest(\"create\", {\n orgId: options.orgId,\n userId: options.userId,\n metadata: options.metadata,\n }) as { chatKey?: string };\n\n if (!response?.chatKey) {\n throw new Error(\"Failed to create chat: no chatKey returned\");\n }\n\n return { chatKey: response.chatKey };\n },\n\n async loadChatData(options: LoadChatOptions): Promise<LoadChatResponse> {\n if (!config) {\n throw new Error(\"Transport not connected\");\n }\n\n const response = await sendRest(\"load\", {\n orgId: options.orgId,\n chatKey: options.chatKey,\n userId: options.userId,\n }) as { chat?: unknown; agentId?: string };\n\n if (!response?.chat) {\n throw new Error(\"Failed to load chat: no chat data returned\");\n }\n\n return {\n chat: response.chat as Chat,\n agentId: response.agentId,\n };\n },\n\n onMessage(handler: EventHandler): Unsubscribe {\n globalHandlers.add(handler);\n return () => globalHandlers.delete(handler);\n },\n\n on(eventType: string, handler: EventHandler): Unsubscribe {\n if (!typeHandlers.has(eventType)) {\n typeHandlers.set(eventType, new Set());\n }\n typeHandlers.get(eventType)!.add(handler);\n return () => {\n const handlers = typeHandlers.get(eventType);\n if (handlers) {\n handlers.delete(handler);\n if (handlers.size === 0) {\n typeHandlers.delete(eventType);\n }\n }\n };\n },\n\n getState(): TransportState {\n return state;\n },\n\n getMetrics(): ConnectionMetrics {\n return { ...metrics };\n },\n\n getError(): TransportError | undefined {\n return error;\n },\n\n clearError(): void {\n error = undefined;\n },\n };\n\n return transport;\n}\n","/**\n * SSE Transport (Fetch-based)\n *\n * Uses fetch with ReadableStream for receiving server events and fetch POST for sending.\n * Designed for React Native and environments without native EventSource support.\n *\n * @example\n * ```typescript\n * import { createFetchSSETransport } from \"@elqnt/chat/transport\";\n *\n * const transport = createFetchSSETransport();\n * await transport.connect({ baseUrl, orgId, userId, clientType: \"customer\" });\n * ```\n */\n\nimport type { Chat, ChatEvent, ChatMessage } from \"../models\";\nimport type {\n ChatTransport,\n TransportConfig,\n TransportState,\n TransportError,\n ConnectionMetrics,\n EventHandler,\n Unsubscribe,\n RetryConfig,\n TransportLogger,\n CreateChatOptions,\n CreateChatResponse,\n LoadChatOptions,\n LoadChatResponse,\n} from \"./types\";\nimport { createLogger, calculateRetryInterval, DEFAULT_RETRY_CONFIG } from \"./types\";\n\n/**\n * Fetch SSE Transport options\n */\nexport interface FetchSSETransportOptions {\n /** Retry configuration */\n retryConfig?: RetryConfig;\n /** Enable debug logging */\n debug?: boolean;\n /** Custom logger */\n logger?: TransportLogger;\n /** Custom fetch implementation (useful for React Native) */\n customFetch?: typeof fetch;\n}\n\n/**\n * Create a fetch-based SSE transport for React Native and polyfill environments\n */\nexport function createFetchSSETransport(options: FetchSSETransportOptions = {}): ChatTransport {\n const {\n retryConfig = DEFAULT_RETRY_CONFIG,\n debug = false,\n logger = createLogger(debug),\n customFetch = fetch,\n } = options;\n\n // Internal state\n let abortController: AbortController | undefined;\n let config: TransportConfig | undefined;\n let state: TransportState = \"disconnected\";\n let error: TransportError | undefined;\n let retryCount = 0;\n let reconnectTimeout: ReturnType<typeof setTimeout> | undefined;\n let intentionalDisconnect = false;\n\n // Metrics\n const metrics: ConnectionMetrics = {\n latency: 0,\n messagesSent: 0,\n messagesReceived: 0,\n messagesQueued: 0,\n reconnectCount: 0,\n transportType: \"sse-fetch\",\n };\n\n // Event handlers\n const globalHandlers = new Set<EventHandler>();\n const typeHandlers = new Map<string, Set<EventHandler>>();\n\n // Helper to emit events\n function emit(event: ChatEvent): void {\n metrics.messagesReceived++;\n metrics.lastMessageAt = Date.now();\n\n globalHandlers.forEach((handler) => {\n try {\n handler(event);\n } catch (err) {\n logger.error(\"Error in message handler:\", err);\n }\n });\n\n const handlers = typeHandlers.get(event.type);\n if (handlers) {\n handlers.forEach((handler) => {\n try {\n handler(event);\n } catch (err) {\n logger.error(`Error in ${event.type} handler:`, err);\n }\n });\n }\n }\n\n // REST API helper\n async function sendRest(endpoint: string, body: Record<string, unknown>): Promise<unknown> {\n if (!config) {\n throw new Error(\"Transport not connected\");\n }\n\n const url = `${config.baseUrl}/${endpoint}`;\n logger.debug(`POST ${endpoint}`, body);\n\n const response = await customFetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`API error: ${response.status} - ${errorText}`);\n }\n\n const json = await response.json() as { success?: boolean; data?: unknown };\n\n // Backend wraps responses in { success: true, data: {...} }\n // Unwrap to return just the data\n if (json && typeof json === \"object\" && \"data\" in json) {\n return json.data;\n }\n\n return json;\n }\n\n // Parse SSE data from a text chunk\n function parseSSEChunk(chunk: string): ChatEvent[] {\n const events: ChatEvent[] = [];\n const lines = chunk.split(\"\\n\");\n\n let eventType = \"message\";\n let data = \"\";\n\n for (const line of lines) {\n if (line.startsWith(\"event:\")) {\n eventType = line.slice(6).trim();\n } else if (line.startsWith(\"data:\")) {\n data = line.slice(5).trim();\n } else if (line === \"\" && data) {\n // End of event\n try {\n const parsed = JSON.parse(data) as ChatEvent;\n events.push(parsed);\n } catch {\n logger.warn(\"Failed to parse SSE data:\", data);\n }\n eventType = \"message\";\n data = \"\";\n }\n }\n\n return events;\n }\n\n // Start SSE stream using fetch\n async function startStream(cfg: TransportConfig): Promise<void> {\n const url = `${cfg.baseUrl}/stream?orgId=${cfg.orgId}&userId=${cfg.userId}&clientType=${cfg.clientType}${cfg.chatKey ? `&chatId=${cfg.chatKey}` : \"\"}`;\n\n logger.debug(\"Connecting to:\", url);\n\n abortController = new AbortController();\n\n const response = await customFetch(url, {\n method: \"GET\",\n headers: {\n Accept: \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n },\n signal: abortController.signal,\n });\n\n if (!response.ok) {\n throw new Error(`Stream connection failed: ${response.status}`);\n }\n\n if (!response.body) {\n throw new Error(\"Response body is null - ReadableStream not supported\");\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n // Read stream in background\n const readStream = async () => {\n try {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n logger.info(\"Stream ended\");\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n\n // Process complete events in buffer\n const lastNewline = buffer.lastIndexOf(\"\\n\\n\");\n if (lastNewline !== -1) {\n const complete = buffer.slice(0, lastNewline + 2);\n buffer = buffer.slice(lastNewline + 2);\n\n const events = parseSSEChunk(complete);\n events.forEach(emit);\n }\n }\n } catch (err) {\n if ((err as Error).name === \"AbortError\") {\n logger.debug(\"Stream aborted\");\n return;\n }\n logger.error(\"Stream error:\", err);\n throw err;\n }\n\n // Stream closed - handle reconnection\n if (!intentionalDisconnect) {\n state = \"disconnected\";\n scheduleReconnect();\n }\n };\n\n // Start reading in background\n readStream().catch((err) => {\n if (!intentionalDisconnect) {\n error = {\n code: \"CONNECTION_FAILED\",\n message: err.message,\n retryable: true,\n timestamp: Date.now(),\n originalError: err,\n };\n metrics.lastError = error;\n state = \"disconnected\";\n scheduleReconnect();\n }\n });\n }\n\n // Schedule reconnect\n function scheduleReconnect(): void {\n if (intentionalDisconnect || !config) return;\n\n const maxRetries = retryConfig.maxRetries ?? DEFAULT_RETRY_CONFIG.maxRetries;\n if (retryCount >= maxRetries) {\n logger.error(`Max retries (${maxRetries}) exceeded`);\n error = {\n code: \"CONNECTION_FAILED\",\n message: `Max retries (${maxRetries}) exceeded`,\n retryable: false,\n timestamp: Date.now(),\n };\n return;\n }\n\n const interval = calculateRetryInterval(retryCount, retryConfig);\n retryCount++;\n metrics.reconnectCount++;\n\n logger.info(`Reconnecting in ${interval}ms (attempt ${retryCount})`);\n state = \"reconnecting\";\n\n reconnectTimeout = setTimeout(() => {\n if (config) {\n transport.connect(config).catch((err) => {\n logger.error(\"Reconnect failed:\", err);\n });\n }\n }, interval);\n }\n\n const transport: ChatTransport = {\n async connect(cfg: TransportConfig): Promise<void> {\n config = cfg;\n intentionalDisconnect = false;\n\n // Clean up existing connection\n if (abortController) {\n abortController.abort();\n abortController = undefined;\n }\n if (reconnectTimeout) {\n clearTimeout(reconnectTimeout);\n reconnectTimeout = undefined;\n }\n\n state = retryCount > 0 ? \"reconnecting\" : \"connecting\";\n const connectionStart = Date.now();\n\n try {\n await startStream(cfg);\n\n const connectionTime = Date.now() - connectionStart;\n logger.info(`Connected in ${connectionTime}ms`);\n\n state = \"connected\";\n error = undefined;\n retryCount = 0;\n metrics.connectedAt = Date.now();\n metrics.latency = connectionTime;\n } catch (err) {\n const connectError: TransportError = {\n code: \"CONNECTION_FAILED\",\n message: (err as Error).message,\n retryable: true,\n timestamp: Date.now(),\n originalError: err,\n };\n error = connectError;\n metrics.lastError = connectError;\n state = \"disconnected\";\n\n if (!intentionalDisconnect) {\n scheduleReconnect();\n }\n\n throw connectError;\n }\n },\n\n disconnect(intentional = true): void {\n logger.info(\"Disconnecting\", { intentional });\n intentionalDisconnect = intentional;\n\n if (reconnectTimeout) {\n clearTimeout(reconnectTimeout);\n reconnectTimeout = undefined;\n }\n\n if (abortController) {\n abortController.abort();\n abortController = undefined;\n }\n\n state = \"disconnected\";\n retryCount = 0;\n },\n\n async send(event: ChatEvent): Promise<void> {\n if (!config) {\n throw new Error(\"Transport not connected\");\n }\n\n switch (event.type) {\n case \"message\":\n await sendRest(\"send\", {\n orgId: event.orgId,\n chatKey: event.chatKey,\n userId: event.userId,\n message: event.message,\n });\n break;\n\n case \"typing\":\n await sendRest(\"typing\", {\n orgId: event.orgId,\n chatKey: event.chatKey,\n userId: event.userId,\n typing: true,\n });\n break;\n\n case \"stopped_typing\":\n await sendRest(\"typing\", {\n orgId: event.orgId,\n chatKey: event.chatKey,\n userId: event.userId,\n typing: false,\n });\n break;\n\n case \"load_chat\":\n await sendRest(\"load\", {\n orgId: event.orgId,\n chatKey: event.chatKey,\n userId: event.userId,\n });\n break;\n\n case \"new_chat\":\n await sendRest(\"create\", {\n orgId: event.orgId,\n userId: event.userId,\n metadata: event.data,\n });\n break;\n\n case \"end_chat\":\n await sendRest(\"end\", {\n orgId: event.orgId,\n chatKey: event.chatKey,\n userId: event.userId,\n data: event.data,\n });\n break;\n\n default:\n await sendRest(\"event\", {\n type: event.type,\n orgId: event.orgId,\n chatKey: event.chatKey,\n userId: event.userId,\n data: event.data,\n });\n }\n\n metrics.messagesSent++;\n },\n\n async sendMessage(message: ChatMessage): Promise<void> {\n if (!config) {\n throw new Error(\"Transport not connected\");\n }\n\n await sendRest(\"send\", {\n orgId: config.orgId,\n chatKey: config.chatKey,\n userId: config.userId,\n message,\n });\n\n metrics.messagesSent++;\n },\n\n async createChat(options: CreateChatOptions): Promise<CreateChatResponse> {\n if (!config) {\n throw new Error(\"Transport not connected\");\n }\n\n const response = await sendRest(\"create\", {\n orgId: options.orgId,\n userId: options.userId,\n metadata: options.metadata,\n }) as { chatKey?: string };\n\n if (!response?.chatKey) {\n throw new Error(\"Failed to create chat: no chatKey returned\");\n }\n\n return { chatKey: response.chatKey };\n },\n\n async loadChatData(options: LoadChatOptions): Promise<LoadChatResponse> {\n if (!config) {\n throw new Error(\"Transport not connected\");\n }\n\n const response = await sendRest(\"load\", {\n orgId: options.orgId,\n chatKey: options.chatKey,\n userId: options.userId,\n }) as { chat?: unknown; agentId?: string };\n\n if (!response?.chat) {\n throw new Error(\"Failed to load chat: no chat data returned\");\n }\n\n return {\n chat: response.chat as Chat,\n agentId: response.agentId,\n };\n },\n\n onMessage(handler: EventHandler): Unsubscribe {\n globalHandlers.add(handler);\n return () => globalHandlers.delete(handler);\n },\n\n on(eventType: string, handler: EventHandler): Unsubscribe {\n if (!typeHandlers.has(eventType)) {\n typeHandlers.set(eventType, new Set());\n }\n typeHandlers.get(eventType)!.add(handler);\n return () => {\n const handlers = typeHandlers.get(eventType);\n if (handlers) {\n handlers.delete(handler);\n if (handlers.size === 0) {\n typeHandlers.delete(eventType);\n }\n }\n };\n },\n\n getState(): TransportState {\n return state;\n },\n\n getMetrics(): ConnectionMetrics {\n return { ...metrics };\n },\n\n getError(): TransportError | undefined {\n return error;\n },\n\n clearError(): void {\n error = undefined;\n },\n };\n\n return transport;\n}\n","/**\n * WhatsApp Business API Transport (Stub)\n *\n * This module provides the interface for integrating with WhatsApp Business API.\n * The implementation connects to a webhook receiver that bridges WhatsApp messages\n * to the chat system.\n *\n * ## Integration Architecture\n *\n * ```\n * WhatsApp User → WhatsApp Cloud API → Your Webhook → Chat Service → SSE to Admin\n * ↓\n * NATS Events\n * ```\n *\n * ## Setup Requirements\n *\n * 1. Register a WhatsApp Business Account\n * 2. Create a Meta Business App with WhatsApp integration\n * 3. Configure webhook URL pointing to your chat service\n * 4. Set up message templates for outbound messages\n *\n * ## Backend Integration\n *\n * Your backend should expose webhook endpoints that:\n * - Verify webhook (GET /webhook with hub.verify_token)\n * - Receive messages (POST /webhook with message payload)\n * - Map WhatsApp user IDs to chat users\n * - Create/load chats for new conversations\n *\n * @example Backend Webhook Handler (Go)\n * ```go\n * func HandleWhatsAppWebhook(w http.ResponseWriter, r *http.Request) {\n * if r.Method == \"GET\" {\n * // Webhook verification\n * verifyToken := r.URL.Query().Get(\"hub.verify_token\")\n * if verifyToken == os.Getenv(\"WHATSAPP_VERIFY_TOKEN\") {\n * w.Write([]byte(r.URL.Query().Get(\"hub.challenge\")))\n * return\n * }\n * http.Error(w, \"Invalid verify token\", 403)\n * return\n * }\n *\n * // Handle incoming message\n * var payload WhatsAppPayload\n * json.NewDecoder(r.Body).Decode(&payload)\n *\n * // Map to chat event and publish to NATS\n * chatEvent := mapWhatsAppToChatEvent(payload)\n * natsClient.Publish(\"chat.events\", chatEvent)\n * }\n * ```\n *\n * @see https://developers.facebook.com/docs/whatsapp/cloud-api\n */\n\nimport type {\n ChatTransport,\n TransportConfig,\n TransportState,\n TransportError,\n ConnectionMetrics,\n EventHandler,\n Unsubscribe,\n TransportLogger,\n CreateChatOptions,\n CreateChatResponse,\n LoadChatOptions,\n LoadChatResponse,\n} from \"./types\";\nimport type { Chat, ChatEvent, ChatMessage, Attachment, AttachmentTypeTS } from \"../models\";\nimport { createLogger } from \"./types\";\n\n/**\n * WhatsApp transport configuration\n */\nexport interface WhatsAppTransportConfig extends TransportConfig {\n /** WhatsApp Business API access token */\n accessToken: string;\n /** Phone number ID from Meta Business */\n phoneNumberId: string;\n /** Webhook verify token for setup */\n verifyToken?: string;\n}\n\n/**\n * WhatsApp transport options\n */\nexport interface WhatsAppTransportOptions {\n /** Enable debug logging */\n debug?: boolean;\n /** Custom logger */\n logger?: TransportLogger;\n}\n\n/**\n * WhatsApp message types\n */\nexport type WhatsAppMessageType =\n | \"text\"\n | \"image\"\n | \"document\"\n | \"audio\"\n | \"video\"\n | \"sticker\"\n | \"location\"\n | \"contacts\"\n | \"interactive\"\n | \"template\";\n\n/**\n * Create a WhatsApp Business API transport\n *\n * NOTE: This is a stub implementation. The actual WhatsApp integration\n * happens on the backend via webhooks. This transport is for:\n * - Type definitions\n * - Documentation\n * - Future client-side WhatsApp Web integration\n */\nexport function createWhatsAppTransport(\n options: WhatsAppTransportOptions = {}\n): ChatTransport {\n const { debug = false, logger = createLogger(debug) } = options;\n\n let state: TransportState = \"disconnected\";\n let error: TransportError | undefined;\n let config: WhatsAppTransportConfig | undefined;\n\n const metrics: ConnectionMetrics = {\n latency: 0,\n messagesSent: 0,\n messagesReceived: 0,\n messagesQueued: 0,\n reconnectCount: 0,\n transportType: \"whatsapp\",\n };\n\n const globalHandlers = new Set<EventHandler>();\n const typeHandlers = new Map<string, Set<EventHandler>>();\n\n const transport: ChatTransport = {\n async connect(cfg: TransportConfig): Promise<void> {\n logger.warn(\n \"WhatsApp transport is a stub. WhatsApp integration happens via backend webhooks.\"\n );\n logger.info(\n \"See transport/whatsapp.ts for integration documentation.\"\n );\n\n config = cfg as WhatsAppTransportConfig;\n\n // In a real implementation, this would:\n // 1. Validate WhatsApp Business API credentials\n // 2. Register webhook endpoints\n // 3. Set up SSE connection to receive webhook-relayed events\n\n state = \"connected\";\n metrics.connectedAt = Date.now();\n },\n\n disconnect(): void {\n state = \"disconnected\";\n },\n\n async send(event: ChatEvent): Promise<void> {\n if (!config) {\n throw new Error(\"Transport not connected\");\n }\n\n logger.warn(\"WhatsApp send is a stub. Implement backend webhook relay.\");\n\n // In a real implementation, this would:\n // 1. Convert ChatEvent to WhatsApp message format\n // 2. Call WhatsApp Cloud API to send message\n // 3. Handle delivery receipts\n\n metrics.messagesSent++;\n },\n\n async sendMessage(message: ChatMessage): Promise<void> {\n if (!config) {\n throw new Error(\"Transport not connected\");\n }\n\n logger.warn(\n \"WhatsApp sendMessage is a stub. Implement backend webhook relay.\"\n );\n\n // In a real implementation:\n // POST https://graph.facebook.com/v17.0/{phone_number_id}/messages\n // {\n // \"messaging_product\": \"whatsapp\",\n // \"to\": \"{recipient_phone}\",\n // \"type\": \"text\",\n // \"text\": { \"body\": message.content }\n // }\n\n metrics.messagesSent++;\n },\n\n async createChat(options: CreateChatOptions): Promise<CreateChatResponse> {\n logger.warn(\n \"WhatsApp createChat is a stub. WhatsApp chats are created via incoming webhooks.\"\n );\n\n // In WhatsApp, chats are typically initiated by users messaging first.\n // This stub returns a placeholder - real implementation would create\n // a chat record on the backend for outbound messaging.\n return { chatKey: `whatsapp_${options.userId}_${Date.now()}` };\n },\n\n async loadChatData(options: LoadChatOptions): Promise<LoadChatResponse> {\n logger.warn(\n \"WhatsApp loadChatData is a stub. Implement backend API call.\"\n );\n\n // In a real implementation, this would fetch chat data from the backend\n return {\n chat: {\n orgId: options.orgId,\n key: options.chatKey,\n title: \"WhatsApp Chat\",\n messages: [],\n lastUpdated: Date.now(),\n startTime: Date.now(),\n users: [],\n status: \"active\",\n aiEngaged: false,\n humanAgentEngaged: false,\n isWaiting: false,\n isWaitingForAgent: false,\n } as Chat,\n };\n },\n\n onMessage(handler: EventHandler): Unsubscribe {\n globalHandlers.add(handler);\n return () => globalHandlers.delete(handler);\n },\n\n on(eventType: string, handler: EventHandler): Unsubscribe {\n if (!typeHandlers.has(eventType)) {\n typeHandlers.set(eventType, new Set());\n }\n typeHandlers.get(eventType)!.add(handler);\n return () => {\n const handlers = typeHandlers.get(eventType);\n if (handlers) {\n handlers.delete(handler);\n if (handlers.size === 0) {\n typeHandlers.delete(eventType);\n }\n }\n };\n },\n\n getState(): TransportState {\n return state;\n },\n\n getMetrics(): ConnectionMetrics {\n return { ...metrics };\n },\n\n getError(): TransportError | undefined {\n return error;\n },\n\n clearError(): void {\n error = undefined;\n },\n };\n\n return transport;\n}\n\n/**\n * Helper: Convert WhatsApp webhook payload to ChatEvent\n *\n * Use this in your backend webhook handler to map incoming\n * WhatsApp messages to the chat system format.\n */\nexport interface WhatsAppWebhookPayload {\n object: \"whatsapp_business_account\";\n entry: Array<{\n id: string;\n changes: Array<{\n value: {\n messaging_product: \"whatsapp\";\n metadata: {\n display_phone_number: string;\n phone_number_id: string;\n };\n contacts?: Array<{\n profile: { name: string };\n wa_id: string;\n }>;\n messages?: Array<{\n from: string;\n id: string;\n timestamp: string;\n type: WhatsAppMessageType;\n text?: { body: string };\n image?: { id: string; mime_type: string; sha256: string };\n document?: { id: string; mime_type: string; filename: string };\n audio?: { id: string; mime_type: string };\n video?: { id: string; mime_type: string };\n location?: { latitude: number; longitude: number; name?: string };\n }>;\n statuses?: Array<{\n id: string;\n status: \"sent\" | \"delivered\" | \"read\" | \"failed\";\n timestamp: string;\n recipient_id: string;\n }>;\n };\n }>;\n }>;\n}\n\n/**\n * Example: Map WhatsApp payload to ChatEvent (for backend use)\n *\n * @example\n * ```typescript\n * // In your backend webhook handler\n * import { mapWhatsAppToChatEvent } from \"@elqnt/chat/transport\";\n *\n * app.post(\"/webhook/whatsapp\", (req, res) => {\n * const events = mapWhatsAppToChatEvent(req.body, orgId);\n * events.forEach(event => publishToChatService(event));\n * res.sendStatus(200);\n * });\n * ```\n */\nexport function mapWhatsAppToChatEvent(\n payload: WhatsAppWebhookPayload,\n orgId: string\n): ChatEvent[] {\n const events: ChatEvent[] = [];\n\n for (const entry of payload.entry) {\n for (const change of entry.changes) {\n const value = change.value;\n\n // Handle incoming messages\n if (value.messages) {\n for (const msg of value.messages) {\n const contact = value.contacts?.find((c) => c.wa_id === msg.from);\n\n events.push({\n type: \"message\",\n orgId,\n chatKey: `wa_${msg.from}`, // Use phone number as chat key\n userId: msg.from,\n timestamp: parseInt(msg.timestamp) * 1000,\n message: {\n id: msg.id,\n role: \"user\",\n content: msg.text?.body || \"\",\n time: parseInt(msg.timestamp) * 1000,\n status: \"delivered\",\n senderId: msg.from,\n senderName: contact?.profile.name,\n createdAt: parseInt(msg.timestamp) * 1000,\n // Map attachments if present\n attachments: mapWhatsAppAttachments(msg),\n },\n });\n }\n }\n\n // Handle message status updates\n if (value.statuses) {\n for (const status of value.statuses) {\n events.push({\n type: \"message_status_update\",\n orgId,\n chatKey: `wa_${status.recipient_id}`,\n userId: status.recipient_id,\n timestamp: parseInt(status.timestamp) * 1000,\n data: {\n messageId: status.id,\n status: status.status,\n },\n });\n }\n }\n }\n }\n\n return events;\n}\n\n// Helper to map WhatsApp attachments\nfunction mapWhatsAppAttachments(msg: {\n type: WhatsAppMessageType;\n image?: { id: string };\n document?: { id: string; filename: string };\n audio?: { id: string };\n video?: { id: string };\n location?: { latitude: number; longitude: number; name?: string };\n}): Attachment[] | undefined {\n switch (msg.type) {\n case \"image\":\n return msg.image\n ? [{ type: \"image\" as AttachmentTypeTS, url: `whatsapp://media/${msg.image.id}` }]\n : undefined;\n case \"document\":\n return msg.document\n ? [\n {\n type: \"document\" as AttachmentTypeTS,\n url: `whatsapp://media/${msg.document.id}`,\n },\n ]\n : undefined;\n case \"audio\":\n return msg.audio\n ? [{ type: \"audio\" as AttachmentTypeTS, url: `whatsapp://media/${msg.audio.id}` }]\n : undefined;\n case \"video\":\n return msg.video\n ? [{ type: \"video\" as AttachmentTypeTS, url: `whatsapp://media/${msg.video.id}` }]\n : undefined;\n case \"location\":\n return msg.location\n ? [\n {\n type: \"location\" as AttachmentTypeTS,\n url: `geo:${msg.location.latitude},${msg.location.longitude}`,\n },\n ]\n : undefined;\n default:\n return undefined;\n }\n}\n"],"mappings":"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","names":["options","options","options"]}
|
|
1
|
+
{"version":3,"sources":["../../transport/index.ts","../../transport/types.ts","../../transport/sse.ts","../../transport/sse-fetch.ts","../../transport/whatsapp.ts"],"sourcesContent":["/**\n * Transport Layer\n *\n * Platform-agnostic transport abstractions for chat communication.\n *\n * @example Browser (SSE)\n * ```typescript\n * import { createSSETransport } from \"@elqnt/chat/transport\";\n *\n * const transport = createSSETransport({ debug: true });\n * await transport.connect({ baseUrl, orgId, userId, clientType: \"customer\" });\n *\n * transport.onMessage((event) => {\n * console.log(\"Received:\", event);\n * });\n * ```\n *\n * @example React Native (Fetch-based SSE)\n * ```typescript\n * import { createFetchSSETransport } from \"@elqnt/chat/transport\";\n *\n * const transport = createFetchSSETransport();\n * await transport.connect({ baseUrl, orgId, userId, clientType: \"customer\" });\n * ```\n *\n * @example Custom Transport\n * ```typescript\n * import type { ChatTransport } from \"@elqnt/chat/transport\";\n *\n * const customTransport: ChatTransport = {\n * connect: async (config) => { ... },\n * disconnect: () => { ... },\n * send: async (event) => { ... },\n * sendMessage: async (message) => { ... },\n * onMessage: (handler) => { ... },\n * on: (eventType, handler) => { ... },\n * getState: () => { ... },\n * getMetrics: () => { ... },\n * getError: () => { ... },\n * clearError: () => { ... },\n * };\n * ```\n */\n\n// Types\nexport type {\n ChatTransport,\n TransportConfig,\n TransportState,\n TransportError,\n TransportFactory,\n TransportLogger,\n ConnectionMetrics,\n EventHandler,\n Unsubscribe,\n RetryConfig,\n SendMessageOptions,\n CreateChatOptions,\n LoadChatOptions,\n EndChatOptions,\n} from \"./types\";\n\nexport {\n createLogger,\n calculateRetryInterval,\n DEFAULT_RETRY_CONFIG,\n} from \"./types\";\n\n// Browser SSE transport\nexport { createSSETransport } from \"./sse\";\nexport type { SSETransportOptions } from \"./sse\";\n\n// Fetch-based SSE transport (React Native)\nexport { createFetchSSETransport } from \"./sse-fetch\";\nexport type { FetchSSETransportOptions } from \"./sse-fetch\";\n\n// WhatsApp Business API transport (stub)\nexport {\n createWhatsAppTransport,\n mapWhatsAppToChatEvent,\n} from \"./whatsapp\";\nexport type {\n WhatsAppTransportConfig,\n WhatsAppTransportOptions,\n WhatsAppMessageType,\n WhatsAppWebhookPayload,\n} from \"./whatsapp\";\n","/**\n * Transport Abstraction Layer Types\n *\n * Platform-agnostic transport interfaces for chat communication.\n * Supports browser SSE, React Native fetch-based SSE, and extensible\n * transports like WhatsApp Business API.\n */\n\nimport type { Chat, ChatEvent, ChatMessage } from \"../models\";\n\n/**\n * Transport connection state\n */\nexport type TransportState =\n | \"disconnected\"\n | \"connecting\"\n | \"connected\"\n | \"reconnecting\";\n\n/**\n * Transport error with retry information\n */\nexport interface TransportError {\n code:\n | \"CONNECTION_FAILED\"\n | \"PARSE_ERROR\"\n | \"SEND_FAILED\"\n | \"TIMEOUT\"\n | \"NETWORK_ERROR\"\n | \"AUTH_FAILED\";\n message: string;\n retryable: boolean;\n timestamp: number;\n originalError?: unknown;\n}\n\n/**\n * Configuration for transport connection\n */\nexport interface TransportConfig {\n /** Base URL for the chat server */\n baseUrl: string;\n /** Organization ID */\n orgId: string;\n /** User ID for authentication */\n userId: string;\n /** Client type for routing */\n clientType: \"customer\" | \"humanAgent\" | \"observer\";\n /** Optional current chat key for reconnection */\n chatKey?: string;\n /** Enable debug logging */\n debug?: boolean;\n /**\n * Tier-2 authorization (REQUIRED in production). Chat realtime talks to the\n * chat service DIRECTLY, bypassing the API gateway — so the service has no\n * gateway JWT to trust and authorizes each connection from this signed\n * token instead. The service derives orgId/userId/product from the token's\n * claims, so the token MUST be scoped to the same org/user as this config.\n *\n * Prefer `getToken`: it is invoked before every connect and every REST send,\n * so a long-lived stream keeps working as short-lived tokens roll over. Use\n * the static `token` only for one-shot/non-refreshing callers. Obtain the\n * token from a server route that mints a gateway-shaped JWT (e.g. the app's\n * /api/gateway-token or a dedicated chat-token route). See SKILL.md.\n */\n getToken?: () => Promise<string | null | undefined> | string | null | undefined;\n /** Static signed token. Ignored when `getToken` is provided. */\n token?: string;\n}\n\n/**\n * Resolve the connect/send token from a transport config. Prefers the\n * `getToken` provider (so tokens refresh) and falls back to the static\n * `token`. Never throws — a failing provider resolves to undefined and the\n * request goes out tokenless (the service rejects it when enforcing).\n */\nexport async function resolveTransportToken(\n cfg: { getToken?: TransportConfig[\"getToken\"]; token?: string } | undefined\n): Promise<string | undefined> {\n if (!cfg) return undefined;\n if (cfg.getToken) {\n try {\n const t = await cfg.getToken();\n return t ?? undefined;\n } catch {\n return undefined;\n }\n }\n return cfg.token ?? undefined;\n}\n\n/**\n * Retry configuration for connection attempts\n */\nexport interface RetryConfig {\n /** Maximum number of retry attempts */\n maxRetries?: number;\n /** Initial retry intervals in milliseconds */\n intervals?: number[];\n /** Multiplier for exponential backoff */\n backoffMultiplier?: number;\n /** Maximum backoff time in milliseconds */\n maxBackoffTime?: number;\n}\n\n/**\n * Connection metrics for monitoring\n */\nexport interface ConnectionMetrics {\n /** Round-trip latency in milliseconds */\n latency: number;\n /** Total messages sent */\n messagesSent: number;\n /** Total messages received */\n messagesReceived: number;\n /** Messages waiting to be sent */\n messagesQueued: number;\n /** Number of reconnection attempts */\n reconnectCount: number;\n /** Last error encountered */\n lastError?: TransportError;\n /** Timestamp when connection was established */\n connectedAt?: number;\n /** Timestamp of last message */\n lastMessageAt?: number;\n /** Current transport type */\n transportType?: string;\n}\n\n/**\n * Event handler function type\n */\nexport type EventHandler<T = ChatEvent> = (event: T) => void;\n\n/**\n * Unsubscribe function returned by event subscription\n */\nexport type Unsubscribe = () => void;\n\n/**\n * Send message options\n */\nexport interface SendMessageOptions {\n /** Message content */\n content: string;\n /** Optional attachments */\n attachments?: unknown[];\n /** Optional message metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Create chat options\n */\nexport interface CreateChatOptions {\n /** Organization ID */\n orgId: string;\n /** User ID */\n userId: string;\n /** Optional chat metadata */\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Load chat options\n */\nexport interface LoadChatOptions {\n /** Organization ID */\n orgId: string;\n /** Chat key to load */\n chatKey: string;\n /** User ID */\n userId: string;\n}\n\n/**\n * End chat options\n */\nexport interface EndChatOptions {\n /** Organization ID */\n orgId: string;\n /** Chat key to end */\n chatKey: string;\n /** User ID */\n userId: string;\n /** Optional end reason */\n reason?: string;\n}\n\n/**\n * Response from creating a new chat\n */\nexport interface CreateChatResponse {\n /** The key of the created chat */\n chatKey: string;\n}\n\n/**\n * Response from loading a chat\n */\nexport interface LoadChatResponse {\n /** The loaded chat object */\n chat: Chat;\n /** Agent ID if applicable */\n agentId?: string;\n}\n\n/**\n * Core transport interface\n *\n * Implementations must handle:\n * - Connection lifecycle (connect, disconnect, reconnect)\n * - Message sending (POST requests)\n * - Event receiving (SSE stream)\n * - Error handling and recovery\n */\nexport interface ChatTransport {\n /**\n * Connect to the chat server\n * @param config - Transport configuration\n * @returns Promise that resolves when connected\n */\n connect(config: TransportConfig): Promise<void>;\n\n /**\n * Disconnect from the server\n * @param intentional - Whether disconnect was user-initiated\n */\n disconnect(intentional?: boolean): void;\n\n /**\n * Send a chat event to the server\n * @param event - Chat event to send\n */\n send(event: ChatEvent): Promise<void>;\n\n /**\n * Send a message in the current chat\n * @param message - Message to send\n */\n sendMessage(message: ChatMessage): Promise<void>;\n\n /**\n * Create a new chat and return the chat key directly\n * @param options - Create chat options\n * @returns Promise with the created chat key\n */\n createChat(options: CreateChatOptions): Promise<CreateChatResponse>;\n\n /**\n * Load an existing chat and return the chat data directly\n * @param options - Load chat options\n * @returns Promise with the loaded chat data\n */\n loadChatData(options: LoadChatOptions): Promise<LoadChatResponse>;\n\n /**\n * Subscribe to incoming events\n * @param handler - Event handler function\n * @returns Unsubscribe function\n */\n onMessage(handler: EventHandler): Unsubscribe;\n\n /**\n * Subscribe to specific event type\n * @param eventType - Type of event to listen for\n * @param handler - Event handler function\n * @returns Unsubscribe function\n */\n on(eventType: string, handler: EventHandler): Unsubscribe;\n\n /**\n * Get current connection state\n */\n getState(): TransportState;\n\n /**\n * Get connection metrics\n */\n getMetrics(): ConnectionMetrics;\n\n /**\n * Get last error\n */\n getError(): TransportError | undefined;\n\n /**\n * Clear error state\n */\n clearError(): void;\n}\n\n/**\n * Transport factory function type\n */\nexport type TransportFactory = (config?: Partial<TransportConfig>) => ChatTransport;\n\n/**\n * Logger interface for transport debugging\n */\nexport interface TransportLogger {\n debug: (message: string, ...args: unknown[]) => void;\n info: (message: string, ...args: unknown[]) => void;\n warn: (message: string, ...args: unknown[]) => void;\n error: (message: string, ...args: unknown[]) => void;\n}\n\n/**\n * Create a default logger\n * @param debug - Enable debug logging\n */\nexport function createLogger(debug: boolean = false): TransportLogger {\n return {\n debug: debug ? console.log.bind(console, \"[chat]\") : () => {},\n info: console.info.bind(console, \"[chat]\"),\n warn: console.warn.bind(console, \"[chat]\"),\n error: console.error.bind(console, \"[chat]\"),\n };\n}\n\n/**\n * Default retry configuration\n */\nexport const DEFAULT_RETRY_CONFIG: Required<RetryConfig> = {\n maxRetries: 10,\n intervals: [1000, 2000, 5000],\n backoffMultiplier: 1.5,\n maxBackoffTime: 30000,\n};\n\n/**\n * Calculate retry interval with exponential backoff\n * @param retryCount - Current retry attempt number\n * @param config - Retry configuration\n */\nexport function calculateRetryInterval(\n retryCount: number,\n config: RetryConfig = DEFAULT_RETRY_CONFIG\n): number {\n const {\n intervals = DEFAULT_RETRY_CONFIG.intervals,\n backoffMultiplier = DEFAULT_RETRY_CONFIG.backoffMultiplier,\n maxBackoffTime = DEFAULT_RETRY_CONFIG.maxBackoffTime,\n } = config;\n\n if (retryCount < intervals.length) {\n return intervals[retryCount];\n }\n\n const baseInterval = intervals[intervals.length - 1] || 5000;\n const backoffTime =\n baseInterval * Math.pow(backoffMultiplier, retryCount - intervals.length + 1);\n return Math.min(backoffTime, maxBackoffTime);\n}\n","/**\n * SSE Transport (Browser)\n *\n * Uses native EventSource for receiving server events and fetch POST for sending.\n * This is the default transport for browser environments.\n *\n * @example\n * ```typescript\n * import { createSSETransport } from \"@elqnt/chat/transport\";\n *\n * const transport = createSSETransport();\n * await transport.connect({ baseUrl, orgId, userId, clientType: \"customer\" });\n * ```\n */\n\nimport type { Chat, ChatEvent, ChatMessage } from \"../models\";\nimport type {\n ChatTransport,\n TransportConfig,\n TransportState,\n TransportError,\n ConnectionMetrics,\n EventHandler,\n Unsubscribe,\n RetryConfig,\n TransportLogger,\n CreateChatOptions,\n CreateChatResponse,\n LoadChatOptions,\n LoadChatResponse,\n} from \"./types\";\nimport { createLogger, calculateRetryInterval, DEFAULT_RETRY_CONFIG, resolveTransportToken } from \"./types\";\n\n/**\n * SSE Transport options\n */\nexport interface SSETransportOptions {\n /** Retry configuration */\n retryConfig?: RetryConfig;\n /** Enable debug logging */\n debug?: boolean;\n /** Custom logger */\n logger?: TransportLogger;\n}\n\n/**\n * Create an SSE transport for browser environments\n */\nexport function createSSETransport(options: SSETransportOptions = {}): ChatTransport {\n const {\n retryConfig = DEFAULT_RETRY_CONFIG,\n debug = false,\n logger = createLogger(debug),\n } = options;\n\n // Internal state\n let eventSource: EventSource | undefined;\n let config: TransportConfig | undefined;\n let state: TransportState = \"disconnected\";\n let error: TransportError | undefined;\n let retryCount = 0;\n let reconnectTimeout: ReturnType<typeof setTimeout> | undefined;\n let intentionalDisconnect = false;\n\n // Metrics\n const metrics: ConnectionMetrics = {\n latency: 0,\n messagesSent: 0,\n messagesReceived: 0,\n messagesQueued: 0,\n reconnectCount: 0,\n transportType: \"sse\",\n };\n\n // Event handlers\n const globalHandlers = new Set<EventHandler>();\n const typeHandlers = new Map<string, Set<EventHandler>>();\n\n // Helper to emit events\n function emit(event: ChatEvent): void {\n // Update metrics\n metrics.messagesReceived++;\n metrics.lastMessageAt = Date.now();\n\n // Call global handlers\n globalHandlers.forEach((handler) => {\n try {\n handler(event);\n } catch (err) {\n logger.error(\"Error in message handler:\", err);\n }\n });\n\n // Call type-specific handlers\n const handlers = typeHandlers.get(event.type);\n if (handlers) {\n handlers.forEach((handler) => {\n try {\n handler(event);\n } catch (err) {\n logger.error(`Error in ${event.type} handler:`, err);\n }\n });\n }\n }\n\n // REST API helper\n async function sendRest(endpoint: string, body: Record<string, unknown>): Promise<unknown> {\n if (!config) {\n throw new Error(\"Transport not connected\");\n }\n\n const url = `${config.baseUrl}/${endpoint}`;\n logger.debug(`POST ${endpoint}`, body);\n\n // Tier-2 authorization: attach the signed token as a bearer header.\n const token = await resolveTransportToken(config);\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (token) headers[\"Authorization\"] = `Bearer ${token}`;\n\n const response = await fetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`API error: ${response.status} - ${errorText}`);\n }\n\n const json = await response.json() as { success?: boolean; data?: unknown };\n\n // Backend wraps responses in { success: true, data: {...} }\n // Unwrap to return just the data\n if (json && typeof json === \"object\" && \"data\" in json) {\n return json.data;\n }\n\n return json;\n }\n\n // Handle SSE message\n function handleMessage(event: MessageEvent): void {\n if (!event.data || event.data === \"\") return;\n\n try {\n const data = JSON.parse(event.data) as ChatEvent;\n logger.debug(\"Received:\", data.type);\n emit(data);\n } catch (err) {\n logger.error(\"Failed to parse SSE message:\", err);\n }\n }\n\n // Setup SSE event listeners\n function setupEventListeners(es: EventSource): void {\n // Generic message handler\n es.addEventListener(\"message\", handleMessage);\n\n // Add handlers for known event types.\n // Note \"error\": the server emits failed-turn notices as named `event:\n // error` SSE events. EventSource also fires a native data-less \"error\"\n // event on connection problems — handleMessage's empty-data guard\n // filters those out, so registering here is safe.\n // The server names EVERY SSE event (event: <type>), and EventSource\n // only fires listeners that were registered by name — any type missing\n // here is silently dropped. Keep this in sync with the server→client\n // types in ChatEventTypeTS; registering an unused name is harmless.\n const eventTypes = [\n \"error\",\n \"reconnected\", \"typing\", \"stopped_typing\", \"waiting\",\n \"waiting_for_agent\", \"human_agent_joined\", \"human_agent_left\",\n \"chat_ended\", \"chat_updated\", \"chat_removed\", \"load_chat_response\",\n \"new_chat_created\", \"show_csat_survey\", \"csat_response\",\n \"user_suggested_actions\", \"user_suggested_action_selected\",\n \"agent_execution_started\", \"agent_execution_ended\",\n \"agent_context_update\", \"load_agent_context_response\",\n \"plan_pending_approval\", \"plan_approved\", \"plan_rejected\",\n \"step_started\", \"step_completed\", \"step_failed\", \"plan_completed\",\n \"skills_changed\", \"summary_update\",\n \"message_status_update\", \"delivered\", \"read\",\n \"sync_metadata_response\", \"sync_user_session_response\",\n \"client_action\", \"client_action_callback\",\n \"attachment_processing_started\", \"attachment_processing_progress\",\n \"attachment_processing_complete\", \"attachment_processing_error\",\n \"observer_joined\", \"observer_left\", \"block_user\",\n \"message_edited\", \"message_deleted\", \"message_reaction\",\n \"user_presence_changed\", \"online_users\", \"get_agents_response\",\n \"room_created\", \"room_updated\", \"room_deleted\", \"rooms_response\",\n \"room_user_joined\", \"room_user_left\", \"user_invited\",\n ];\n\n eventTypes.forEach((type) => {\n es.addEventListener(type, handleMessage);\n });\n }\n\n // Schedule reconnect\n function scheduleReconnect(): void {\n if (intentionalDisconnect || !config) return;\n\n const maxRetries = retryConfig.maxRetries ?? DEFAULT_RETRY_CONFIG.maxRetries;\n if (retryCount >= maxRetries) {\n logger.error(`Max retries (${maxRetries}) exceeded`);\n error = {\n code: \"CONNECTION_FAILED\",\n message: `Max retries (${maxRetries}) exceeded`,\n retryable: false,\n timestamp: Date.now(),\n };\n return;\n }\n\n const interval = calculateRetryInterval(retryCount, retryConfig);\n retryCount++;\n metrics.reconnectCount++;\n\n logger.info(`Reconnecting in ${interval}ms (attempt ${retryCount})`);\n state = \"reconnecting\";\n\n reconnectTimeout = setTimeout(() => {\n if (config) {\n transport.connect(config).catch((err) => {\n logger.error(\"Reconnect failed:\", err);\n });\n }\n }, interval);\n }\n\n const transport: ChatTransport = {\n async connect(cfg: TransportConfig): Promise<void> {\n config = cfg;\n intentionalDisconnect = false;\n\n // Clean up existing connection\n if (eventSource) {\n eventSource.close();\n eventSource = undefined;\n }\n if (reconnectTimeout) {\n clearTimeout(reconnectTimeout);\n reconnectTimeout = undefined;\n }\n\n state = retryCount > 0 ? \"reconnecting\" : \"connecting\";\n\n // Resolve a fresh token BEFORE opening the stream. EventSource cannot set\n // headers, so the token must ride as a ?token= query param — the chat\n // service reads it there (the gateway uses the same pattern for SSE).\n const connectToken = await resolveTransportToken(cfg);\n\n return new Promise((resolve, reject) => {\n const connectionStart = Date.now();\n // URLSearchParams encodes reserved characters — a raw \"+\" in a\n // userId email decodes server-side to a space, silently breaking\n // targeted delivery (connection registered under the wrong user).\n const streamParams = new URLSearchParams({\n orgId: cfg.orgId,\n userId: cfg.userId,\n clientType: cfg.clientType,\n });\n if (cfg.chatKey) streamParams.set(\"chatId\", cfg.chatKey);\n if (connectToken) streamParams.set(\"token\", connectToken);\n const url = `${cfg.baseUrl}/stream?${streamParams.toString()}`;\n\n logger.debug(\"Connecting to:\", url);\n const es = new EventSource(url);\n\n es.onopen = () => {\n const connectionTime = Date.now() - connectionStart;\n logger.info(`Connected in ${connectionTime}ms`);\n\n const wasReconnect = retryCount > 0;\n state = \"connected\";\n error = undefined;\n retryCount = 0;\n metrics.connectedAt = Date.now();\n metrics.latency = connectionTime;\n\n setupEventListeners(es);\n\n // Synthetic event so the app layer can re-sync chat state —\n // broadcasts emitted while the stream was down are gone for good.\n if (wasReconnect) {\n emit({\n type: \"transport_reconnected\",\n orgId: cfg.orgId,\n chatKey: cfg.chatKey || \"\",\n userId: cfg.userId,\n timestamp: Date.now(),\n } as ChatEvent);\n }\n\n resolve();\n };\n\n es.onerror = () => {\n if (es.readyState === EventSource.CLOSED) {\n const sseError: TransportError = {\n code: \"CONNECTION_FAILED\",\n message: \"SSE connection failed\",\n retryable: true,\n timestamp: Date.now(),\n };\n error = sseError;\n metrics.lastError = sseError;\n state = \"disconnected\";\n\n if (!intentionalDisconnect) {\n scheduleReconnect();\n }\n\n // Only reject if this is the initial connection attempt\n if (retryCount === 0) {\n reject(sseError);\n }\n }\n };\n\n eventSource = es;\n });\n },\n\n disconnect(intentional = true): void {\n logger.info(\"Disconnecting\", { intentional });\n intentionalDisconnect = intentional;\n\n if (reconnectTimeout) {\n clearTimeout(reconnectTimeout);\n reconnectTimeout = undefined;\n }\n\n if (eventSource) {\n eventSource.close();\n eventSource = undefined;\n }\n\n state = \"disconnected\";\n retryCount = 0;\n },\n\n async send(event: ChatEvent): Promise<void> {\n if (!config) {\n throw new Error(\"Transport not connected\");\n }\n\n // Map event types to REST endpoints\n switch (event.type) {\n case \"message\":\n await sendRest(\"send\", {\n orgId: event.orgId,\n chatKey: event.chatKey,\n userId: event.userId,\n message: event.message,\n ...(event.data ? { data: event.data } : {}),\n });\n break;\n\n case \"typing\":\n await sendRest(\"typing\", {\n orgId: event.orgId,\n chatKey: event.chatKey,\n userId: event.userId,\n typing: true,\n });\n break;\n\n case \"stopped_typing\":\n await sendRest(\"typing\", {\n orgId: event.orgId,\n chatKey: event.chatKey,\n userId: event.userId,\n typing: false,\n });\n break;\n\n case \"load_chat\":\n // Deprecated: Use loadChatData() instead for direct response\n await sendRest(\"load\", {\n orgId: event.orgId,\n chatKey: event.chatKey,\n userId: event.userId,\n });\n break;\n\n case \"new_chat\":\n // Deprecated: Use createChat() instead for direct response\n await sendRest(\"create\", {\n orgId: event.orgId,\n userId: event.userId,\n metadata: event.data,\n });\n break;\n\n case \"end_chat\":\n await sendRest(\"end\", {\n orgId: event.orgId,\n chatKey: event.chatKey,\n userId: event.userId,\n data: event.data,\n });\n break;\n\n default:\n // Generic event endpoint for other types\n await sendRest(\"event\", {\n type: event.type,\n orgId: event.orgId,\n chatKey: event.chatKey,\n userId: event.userId,\n data: event.data,\n });\n }\n\n metrics.messagesSent++;\n },\n\n async sendMessage(message: ChatMessage): Promise<void> {\n if (!config) {\n throw new Error(\"Transport not connected\");\n }\n\n await sendRest(\"send\", {\n orgId: config.orgId,\n chatKey: config.chatKey,\n userId: config.userId,\n message,\n });\n\n metrics.messagesSent++;\n },\n\n async createChat(options: CreateChatOptions): Promise<CreateChatResponse> {\n if (!config) {\n throw new Error(\"Transport not connected\");\n }\n\n const response = await sendRest(\"create\", {\n orgId: options.orgId,\n userId: options.userId,\n metadata: options.metadata,\n }) as { chatKey?: string };\n\n if (!response?.chatKey) {\n throw new Error(\"Failed to create chat: no chatKey returned\");\n }\n\n return { chatKey: response.chatKey };\n },\n\n async loadChatData(options: LoadChatOptions): Promise<LoadChatResponse> {\n if (!config) {\n throw new Error(\"Transport not connected\");\n }\n\n const response = await sendRest(\"load\", {\n orgId: options.orgId,\n chatKey: options.chatKey,\n userId: options.userId,\n }) as { chat?: unknown; agentId?: string };\n\n if (!response?.chat) {\n throw new Error(\"Failed to load chat: no chat data returned\");\n }\n\n return {\n chat: response.chat as Chat,\n agentId: response.agentId,\n };\n },\n\n onMessage(handler: EventHandler): Unsubscribe {\n globalHandlers.add(handler);\n return () => globalHandlers.delete(handler);\n },\n\n on(eventType: string, handler: EventHandler): Unsubscribe {\n if (!typeHandlers.has(eventType)) {\n typeHandlers.set(eventType, new Set());\n }\n typeHandlers.get(eventType)!.add(handler);\n return () => {\n const handlers = typeHandlers.get(eventType);\n if (handlers) {\n handlers.delete(handler);\n if (handlers.size === 0) {\n typeHandlers.delete(eventType);\n }\n }\n };\n },\n\n getState(): TransportState {\n return state;\n },\n\n getMetrics(): ConnectionMetrics {\n return { ...metrics };\n },\n\n getError(): TransportError | undefined {\n return error;\n },\n\n clearError(): void {\n error = undefined;\n },\n };\n\n return transport;\n}\n","/**\n * SSE Transport (Fetch-based)\n *\n * Uses fetch with ReadableStream for receiving server events and fetch POST for sending.\n * Designed for React Native and environments without native EventSource support.\n *\n * @example\n * ```typescript\n * import { createFetchSSETransport } from \"@elqnt/chat/transport\";\n *\n * const transport = createFetchSSETransport();\n * await transport.connect({ baseUrl, orgId, userId, clientType: \"customer\" });\n * ```\n */\n\nimport type { Chat, ChatEvent, ChatMessage } from \"../models\";\nimport type {\n ChatTransport,\n TransportConfig,\n TransportState,\n TransportError,\n ConnectionMetrics,\n EventHandler,\n Unsubscribe,\n RetryConfig,\n TransportLogger,\n CreateChatOptions,\n CreateChatResponse,\n LoadChatOptions,\n LoadChatResponse,\n} from \"./types\";\nimport { createLogger, calculateRetryInterval, DEFAULT_RETRY_CONFIG, resolveTransportToken } from \"./types\";\n\n/**\n * Fetch SSE Transport options\n */\nexport interface FetchSSETransportOptions {\n /** Retry configuration */\n retryConfig?: RetryConfig;\n /** Enable debug logging */\n debug?: boolean;\n /** Custom logger */\n logger?: TransportLogger;\n /** Custom fetch implementation (useful for React Native) */\n customFetch?: typeof fetch;\n}\n\n/**\n * Create a fetch-based SSE transport for React Native and polyfill environments\n */\nexport function createFetchSSETransport(options: FetchSSETransportOptions = {}): ChatTransport {\n const {\n retryConfig = DEFAULT_RETRY_CONFIG,\n debug = false,\n logger = createLogger(debug),\n customFetch = fetch,\n } = options;\n\n // Internal state\n let abortController: AbortController | undefined;\n let config: TransportConfig | undefined;\n let state: TransportState = \"disconnected\";\n let error: TransportError | undefined;\n let retryCount = 0;\n let reconnectTimeout: ReturnType<typeof setTimeout> | undefined;\n let intentionalDisconnect = false;\n\n // Metrics\n const metrics: ConnectionMetrics = {\n latency: 0,\n messagesSent: 0,\n messagesReceived: 0,\n messagesQueued: 0,\n reconnectCount: 0,\n transportType: \"sse-fetch\",\n };\n\n // Event handlers\n const globalHandlers = new Set<EventHandler>();\n const typeHandlers = new Map<string, Set<EventHandler>>();\n\n // Helper to emit events\n function emit(event: ChatEvent): void {\n metrics.messagesReceived++;\n metrics.lastMessageAt = Date.now();\n\n globalHandlers.forEach((handler) => {\n try {\n handler(event);\n } catch (err) {\n logger.error(\"Error in message handler:\", err);\n }\n });\n\n const handlers = typeHandlers.get(event.type);\n if (handlers) {\n handlers.forEach((handler) => {\n try {\n handler(event);\n } catch (err) {\n logger.error(`Error in ${event.type} handler:`, err);\n }\n });\n }\n }\n\n // REST API helper\n async function sendRest(endpoint: string, body: Record<string, unknown>): Promise<unknown> {\n if (!config) {\n throw new Error(\"Transport not connected\");\n }\n\n const url = `${config.baseUrl}/${endpoint}`;\n logger.debug(`POST ${endpoint}`, body);\n\n // Tier-2 authorization: attach the signed token as a bearer header.\n const token = await resolveTransportToken(config);\n const headers: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (token) headers[\"Authorization\"] = `Bearer ${token}`;\n\n const response = await customFetch(url, {\n method: \"POST\",\n headers,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`API error: ${response.status} - ${errorText}`);\n }\n\n const json = await response.json() as { success?: boolean; data?: unknown };\n\n // Backend wraps responses in { success: true, data: {...} }\n // Unwrap to return just the data\n if (json && typeof json === \"object\" && \"data\" in json) {\n return json.data;\n }\n\n return json;\n }\n\n // Parse SSE data from a text chunk\n function parseSSEChunk(chunk: string): ChatEvent[] {\n const events: ChatEvent[] = [];\n const lines = chunk.split(\"\\n\");\n\n let eventType = \"message\";\n let data = \"\";\n\n for (const line of lines) {\n if (line.startsWith(\"event:\")) {\n eventType = line.slice(6).trim();\n } else if (line.startsWith(\"data:\")) {\n data = line.slice(5).trim();\n } else if (line === \"\" && data) {\n // End of event\n try {\n const parsed = JSON.parse(data) as ChatEvent;\n events.push(parsed);\n } catch {\n logger.warn(\"Failed to parse SSE data:\", data);\n }\n eventType = \"message\";\n data = \"\";\n }\n }\n\n return events;\n }\n\n // Start SSE stream using fetch\n async function startStream(cfg: TransportConfig): Promise<void> {\n // URLSearchParams encodes reserved characters — a raw \"+\" in a userId\n // email decodes server-side to a space, silently breaking targeted\n // delivery (connection registered under the wrong user).\n const streamParams = new URLSearchParams({\n orgId: cfg.orgId,\n userId: cfg.userId,\n clientType: cfg.clientType,\n });\n if (cfg.chatKey) streamParams.set(\"chatId\", cfg.chatKey);\n // Tier-2 authorization. fetch CAN set headers, but we also pass ?token=\n // for parity with EventSource and so the same server query path works.\n const token = await resolveTransportToken(cfg);\n if (token) streamParams.set(\"token\", token);\n const url = `${cfg.baseUrl}/stream?${streamParams.toString()}`;\n\n logger.debug(\"Connecting to:\", url);\n\n abortController = new AbortController();\n\n const streamHeaders: Record<string, string> = {\n Accept: \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n };\n if (token) streamHeaders[\"Authorization\"] = `Bearer ${token}`;\n\n const response = await customFetch(url, {\n method: \"GET\",\n headers: streamHeaders,\n signal: abortController.signal,\n });\n\n if (!response.ok) {\n throw new Error(`Stream connection failed: ${response.status}`);\n }\n\n if (!response.body) {\n throw new Error(\"Response body is null - ReadableStream not supported\");\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = \"\";\n\n // Read stream in background\n const readStream = async () => {\n try {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n logger.info(\"Stream ended\");\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n\n // Process complete events in buffer\n const lastNewline = buffer.lastIndexOf(\"\\n\\n\");\n if (lastNewline !== -1) {\n const complete = buffer.slice(0, lastNewline + 2);\n buffer = buffer.slice(lastNewline + 2);\n\n const events = parseSSEChunk(complete);\n events.forEach(emit);\n }\n }\n } catch (err) {\n if ((err as Error).name === \"AbortError\") {\n logger.debug(\"Stream aborted\");\n return;\n }\n logger.error(\"Stream error:\", err);\n throw err;\n }\n\n // Stream closed - handle reconnection\n if (!intentionalDisconnect) {\n state = \"disconnected\";\n scheduleReconnect();\n }\n };\n\n // Start reading in background\n readStream().catch((err) => {\n if (!intentionalDisconnect) {\n error = {\n code: \"CONNECTION_FAILED\",\n message: err.message,\n retryable: true,\n timestamp: Date.now(),\n originalError: err,\n };\n metrics.lastError = error;\n state = \"disconnected\";\n scheduleReconnect();\n }\n });\n }\n\n // Schedule reconnect\n function scheduleReconnect(): void {\n if (intentionalDisconnect || !config) return;\n\n const maxRetries = retryConfig.maxRetries ?? DEFAULT_RETRY_CONFIG.maxRetries;\n if (retryCount >= maxRetries) {\n logger.error(`Max retries (${maxRetries}) exceeded`);\n error = {\n code: \"CONNECTION_FAILED\",\n message: `Max retries (${maxRetries}) exceeded`,\n retryable: false,\n timestamp: Date.now(),\n };\n return;\n }\n\n const interval = calculateRetryInterval(retryCount, retryConfig);\n retryCount++;\n metrics.reconnectCount++;\n\n logger.info(`Reconnecting in ${interval}ms (attempt ${retryCount})`);\n state = \"reconnecting\";\n\n reconnectTimeout = setTimeout(() => {\n if (config) {\n transport.connect(config).catch((err) => {\n logger.error(\"Reconnect failed:\", err);\n });\n }\n }, interval);\n }\n\n const transport: ChatTransport = {\n async connect(cfg: TransportConfig): Promise<void> {\n config = cfg;\n intentionalDisconnect = false;\n\n // Clean up existing connection\n if (abortController) {\n abortController.abort();\n abortController = undefined;\n }\n if (reconnectTimeout) {\n clearTimeout(reconnectTimeout);\n reconnectTimeout = undefined;\n }\n\n state = retryCount > 0 ? \"reconnecting\" : \"connecting\";\n const connectionStart = Date.now();\n\n try {\n const wasReconnect = retryCount > 0;\n await startStream(cfg);\n\n const connectionTime = Date.now() - connectionStart;\n logger.info(`Connected in ${connectionTime}ms`);\n\n state = \"connected\";\n error = undefined;\n retryCount = 0;\n metrics.connectedAt = Date.now();\n metrics.latency = connectionTime;\n\n // Synthetic event so the app layer can re-sync chat state —\n // broadcasts emitted while the stream was down are gone for good.\n if (wasReconnect) {\n emit({\n type: \"transport_reconnected\",\n orgId: cfg.orgId,\n chatKey: cfg.chatKey || \"\",\n userId: cfg.userId,\n timestamp: Date.now(),\n } as ChatEvent);\n }\n } catch (err) {\n const connectError: TransportError = {\n code: \"CONNECTION_FAILED\",\n message: (err as Error).message,\n retryable: true,\n timestamp: Date.now(),\n originalError: err,\n };\n error = connectError;\n metrics.lastError = connectError;\n state = \"disconnected\";\n\n if (!intentionalDisconnect) {\n scheduleReconnect();\n }\n\n throw connectError;\n }\n },\n\n disconnect(intentional = true): void {\n logger.info(\"Disconnecting\", { intentional });\n intentionalDisconnect = intentional;\n\n if (reconnectTimeout) {\n clearTimeout(reconnectTimeout);\n reconnectTimeout = undefined;\n }\n\n if (abortController) {\n abortController.abort();\n abortController = undefined;\n }\n\n state = \"disconnected\";\n retryCount = 0;\n },\n\n async send(event: ChatEvent): Promise<void> {\n if (!config) {\n throw new Error(\"Transport not connected\");\n }\n\n switch (event.type) {\n case \"message\":\n await sendRest(\"send\", {\n orgId: event.orgId,\n chatKey: event.chatKey,\n userId: event.userId,\n message: event.message,\n ...(event.data ? { data: event.data } : {}),\n });\n break;\n\n case \"typing\":\n await sendRest(\"typing\", {\n orgId: event.orgId,\n chatKey: event.chatKey,\n userId: event.userId,\n typing: true,\n });\n break;\n\n case \"stopped_typing\":\n await sendRest(\"typing\", {\n orgId: event.orgId,\n chatKey: event.chatKey,\n userId: event.userId,\n typing: false,\n });\n break;\n\n case \"load_chat\":\n await sendRest(\"load\", {\n orgId: event.orgId,\n chatKey: event.chatKey,\n userId: event.userId,\n });\n break;\n\n case \"new_chat\":\n await sendRest(\"create\", {\n orgId: event.orgId,\n userId: event.userId,\n metadata: event.data,\n });\n break;\n\n case \"end_chat\":\n await sendRest(\"end\", {\n orgId: event.orgId,\n chatKey: event.chatKey,\n userId: event.userId,\n data: event.data,\n });\n break;\n\n default:\n await sendRest(\"event\", {\n type: event.type,\n orgId: event.orgId,\n chatKey: event.chatKey,\n userId: event.userId,\n data: event.data,\n });\n }\n\n metrics.messagesSent++;\n },\n\n async sendMessage(message: ChatMessage): Promise<void> {\n if (!config) {\n throw new Error(\"Transport not connected\");\n }\n\n await sendRest(\"send\", {\n orgId: config.orgId,\n chatKey: config.chatKey,\n userId: config.userId,\n message,\n });\n\n metrics.messagesSent++;\n },\n\n async createChat(options: CreateChatOptions): Promise<CreateChatResponse> {\n if (!config) {\n throw new Error(\"Transport not connected\");\n }\n\n const response = await sendRest(\"create\", {\n orgId: options.orgId,\n userId: options.userId,\n metadata: options.metadata,\n }) as { chatKey?: string };\n\n if (!response?.chatKey) {\n throw new Error(\"Failed to create chat: no chatKey returned\");\n }\n\n return { chatKey: response.chatKey };\n },\n\n async loadChatData(options: LoadChatOptions): Promise<LoadChatResponse> {\n if (!config) {\n throw new Error(\"Transport not connected\");\n }\n\n const response = await sendRest(\"load\", {\n orgId: options.orgId,\n chatKey: options.chatKey,\n userId: options.userId,\n }) as { chat?: unknown; agentId?: string };\n\n if (!response?.chat) {\n throw new Error(\"Failed to load chat: no chat data returned\");\n }\n\n return {\n chat: response.chat as Chat,\n agentId: response.agentId,\n };\n },\n\n onMessage(handler: EventHandler): Unsubscribe {\n globalHandlers.add(handler);\n return () => globalHandlers.delete(handler);\n },\n\n on(eventType: string, handler: EventHandler): Unsubscribe {\n if (!typeHandlers.has(eventType)) {\n typeHandlers.set(eventType, new Set());\n }\n typeHandlers.get(eventType)!.add(handler);\n return () => {\n const handlers = typeHandlers.get(eventType);\n if (handlers) {\n handlers.delete(handler);\n if (handlers.size === 0) {\n typeHandlers.delete(eventType);\n }\n }\n };\n },\n\n getState(): TransportState {\n return state;\n },\n\n getMetrics(): ConnectionMetrics {\n return { ...metrics };\n },\n\n getError(): TransportError | undefined {\n return error;\n },\n\n clearError(): void {\n error = undefined;\n },\n };\n\n return transport;\n}\n","/**\n * WhatsApp Business API Transport (Stub)\n *\n * This module provides the interface for integrating with WhatsApp Business API.\n * The implementation connects to a webhook receiver that bridges WhatsApp messages\n * to the chat system.\n *\n * ## Integration Architecture\n *\n * ```\n * WhatsApp User → WhatsApp Cloud API → Your Webhook → Chat Service → SSE to Admin\n * ↓\n * NATS Events\n * ```\n *\n * ## Setup Requirements\n *\n * 1. Register a WhatsApp Business Account\n * 2. Create a Meta Business App with WhatsApp integration\n * 3. Configure webhook URL pointing to your chat service\n * 4. Set up message templates for outbound messages\n *\n * ## Backend Integration\n *\n * Your backend should expose webhook endpoints that:\n * - Verify webhook (GET /webhook with hub.verify_token)\n * - Receive messages (POST /webhook with message payload)\n * - Map WhatsApp user IDs to chat users\n * - Create/load chats for new conversations\n *\n * @example Backend Webhook Handler (Go)\n * ```go\n * func HandleWhatsAppWebhook(w http.ResponseWriter, r *http.Request) {\n * if r.Method == \"GET\" {\n * // Webhook verification\n * verifyToken := r.URL.Query().Get(\"hub.verify_token\")\n * if verifyToken == os.Getenv(\"WHATSAPP_VERIFY_TOKEN\") {\n * w.Write([]byte(r.URL.Query().Get(\"hub.challenge\")))\n * return\n * }\n * http.Error(w, \"Invalid verify token\", 403)\n * return\n * }\n *\n * // Handle incoming message\n * var payload WhatsAppPayload\n * json.NewDecoder(r.Body).Decode(&payload)\n *\n * // Map to chat event and publish to NATS\n * chatEvent := mapWhatsAppToChatEvent(payload)\n * natsClient.Publish(\"chat.events\", chatEvent)\n * }\n * ```\n *\n * @see https://developers.facebook.com/docs/whatsapp/cloud-api\n */\n\nimport type {\n ChatTransport,\n TransportConfig,\n TransportState,\n TransportError,\n ConnectionMetrics,\n EventHandler,\n Unsubscribe,\n TransportLogger,\n CreateChatOptions,\n CreateChatResponse,\n LoadChatOptions,\n LoadChatResponse,\n} from \"./types\";\nimport type { Chat, ChatEvent, ChatMessage, Attachment, AttachmentTypeTS } from \"../models\";\nimport { createLogger } from \"./types\";\n\n/**\n * WhatsApp transport configuration\n */\nexport interface WhatsAppTransportConfig extends TransportConfig {\n /** WhatsApp Business API access token */\n accessToken: string;\n /** Phone number ID from Meta Business */\n phoneNumberId: string;\n /** Webhook verify token for setup */\n verifyToken?: string;\n}\n\n/**\n * WhatsApp transport options\n */\nexport interface WhatsAppTransportOptions {\n /** Enable debug logging */\n debug?: boolean;\n /** Custom logger */\n logger?: TransportLogger;\n}\n\n/**\n * WhatsApp message types\n */\nexport type WhatsAppMessageType =\n | \"text\"\n | \"image\"\n | \"document\"\n | \"audio\"\n | \"video\"\n | \"sticker\"\n | \"location\"\n | \"contacts\"\n | \"interactive\"\n | \"template\";\n\n/**\n * Create a WhatsApp Business API transport\n *\n * NOTE: This is a stub implementation. The actual WhatsApp integration\n * happens on the backend via webhooks. This transport is for:\n * - Type definitions\n * - Documentation\n * - Future client-side WhatsApp Web integration\n */\nexport function createWhatsAppTransport(\n options: WhatsAppTransportOptions = {}\n): ChatTransport {\n const { debug = false, logger = createLogger(debug) } = options;\n\n let state: TransportState = \"disconnected\";\n let error: TransportError | undefined;\n let config: WhatsAppTransportConfig | undefined;\n\n const metrics: ConnectionMetrics = {\n latency: 0,\n messagesSent: 0,\n messagesReceived: 0,\n messagesQueued: 0,\n reconnectCount: 0,\n transportType: \"whatsapp\",\n };\n\n const globalHandlers = new Set<EventHandler>();\n const typeHandlers = new Map<string, Set<EventHandler>>();\n\n const transport: ChatTransport = {\n async connect(cfg: TransportConfig): Promise<void> {\n logger.warn(\n \"WhatsApp transport is a stub. WhatsApp integration happens via backend webhooks.\"\n );\n logger.info(\n \"See transport/whatsapp.ts for integration documentation.\"\n );\n\n config = cfg as WhatsAppTransportConfig;\n\n // In a real implementation, this would:\n // 1. Validate WhatsApp Business API credentials\n // 2. Register webhook endpoints\n // 3. Set up SSE connection to receive webhook-relayed events\n\n state = \"connected\";\n metrics.connectedAt = Date.now();\n },\n\n disconnect(): void {\n state = \"disconnected\";\n },\n\n async send(event: ChatEvent): Promise<void> {\n if (!config) {\n throw new Error(\"Transport not connected\");\n }\n\n logger.warn(\"WhatsApp send is a stub. Implement backend webhook relay.\");\n\n // In a real implementation, this would:\n // 1. Convert ChatEvent to WhatsApp message format\n // 2. Call WhatsApp Cloud API to send message\n // 3. Handle delivery receipts\n\n metrics.messagesSent++;\n },\n\n async sendMessage(message: ChatMessage): Promise<void> {\n if (!config) {\n throw new Error(\"Transport not connected\");\n }\n\n logger.warn(\n \"WhatsApp sendMessage is a stub. Implement backend webhook relay.\"\n );\n\n // In a real implementation:\n // POST https://graph.facebook.com/v17.0/{phone_number_id}/messages\n // {\n // \"messaging_product\": \"whatsapp\",\n // \"to\": \"{recipient_phone}\",\n // \"type\": \"text\",\n // \"text\": { \"body\": message.content }\n // }\n\n metrics.messagesSent++;\n },\n\n async createChat(options: CreateChatOptions): Promise<CreateChatResponse> {\n logger.warn(\n \"WhatsApp createChat is a stub. WhatsApp chats are created via incoming webhooks.\"\n );\n\n // In WhatsApp, chats are typically initiated by users messaging first.\n // This stub returns a placeholder - real implementation would create\n // a chat record on the backend for outbound messaging.\n return { chatKey: `whatsapp_${options.userId}_${Date.now()}` };\n },\n\n async loadChatData(options: LoadChatOptions): Promise<LoadChatResponse> {\n logger.warn(\n \"WhatsApp loadChatData is a stub. Implement backend API call.\"\n );\n\n // In a real implementation, this would fetch chat data from the backend\n return {\n chat: {\n orgId: options.orgId,\n key: options.chatKey,\n title: \"WhatsApp Chat\",\n messages: [],\n lastUpdated: Date.now(),\n startTime: Date.now(),\n users: [],\n status: \"active\",\n aiEngaged: false,\n humanAgentEngaged: false,\n isWaiting: false,\n isWaitingForAgent: false,\n } as Chat,\n };\n },\n\n onMessage(handler: EventHandler): Unsubscribe {\n globalHandlers.add(handler);\n return () => globalHandlers.delete(handler);\n },\n\n on(eventType: string, handler: EventHandler): Unsubscribe {\n if (!typeHandlers.has(eventType)) {\n typeHandlers.set(eventType, new Set());\n }\n typeHandlers.get(eventType)!.add(handler);\n return () => {\n const handlers = typeHandlers.get(eventType);\n if (handlers) {\n handlers.delete(handler);\n if (handlers.size === 0) {\n typeHandlers.delete(eventType);\n }\n }\n };\n },\n\n getState(): TransportState {\n return state;\n },\n\n getMetrics(): ConnectionMetrics {\n return { ...metrics };\n },\n\n getError(): TransportError | undefined {\n return error;\n },\n\n clearError(): void {\n error = undefined;\n },\n };\n\n return transport;\n}\n\n/**\n * Helper: Convert WhatsApp webhook payload to ChatEvent\n *\n * Use this in your backend webhook handler to map incoming\n * WhatsApp messages to the chat system format.\n */\nexport interface WhatsAppWebhookPayload {\n object: \"whatsapp_business_account\";\n entry: Array<{\n id: string;\n changes: Array<{\n value: {\n messaging_product: \"whatsapp\";\n metadata: {\n display_phone_number: string;\n phone_number_id: string;\n };\n contacts?: Array<{\n profile: { name: string };\n wa_id: string;\n }>;\n messages?: Array<{\n from: string;\n id: string;\n timestamp: string;\n type: WhatsAppMessageType;\n text?: { body: string };\n image?: { id: string; mime_type: string; sha256: string };\n document?: { id: string; mime_type: string; filename: string };\n audio?: { id: string; mime_type: string };\n video?: { id: string; mime_type: string };\n location?: { latitude: number; longitude: number; name?: string };\n }>;\n statuses?: Array<{\n id: string;\n status: \"sent\" | \"delivered\" | \"read\" | \"failed\";\n timestamp: string;\n recipient_id: string;\n }>;\n };\n }>;\n }>;\n}\n\n/**\n * Example: Map WhatsApp payload to ChatEvent (for backend use)\n *\n * @example\n * ```typescript\n * // In your backend webhook handler\n * import { mapWhatsAppToChatEvent } from \"@elqnt/chat/transport\";\n *\n * app.post(\"/webhook/whatsapp\", (req, res) => {\n * const events = mapWhatsAppToChatEvent(req.body, orgId);\n * events.forEach(event => publishToChatService(event));\n * res.sendStatus(200);\n * });\n * ```\n */\nexport function mapWhatsAppToChatEvent(\n payload: WhatsAppWebhookPayload,\n orgId: string\n): ChatEvent[] {\n const events: ChatEvent[] = [];\n\n for (const entry of payload.entry) {\n for (const change of entry.changes) {\n const value = change.value;\n\n // Handle incoming messages\n if (value.messages) {\n for (const msg of value.messages) {\n const contact = value.contacts?.find((c) => c.wa_id === msg.from);\n\n events.push({\n type: \"message\",\n orgId,\n chatKey: `wa_${msg.from}`, // Use phone number as chat key\n userId: msg.from,\n timestamp: parseInt(msg.timestamp) * 1000,\n message: {\n id: msg.id,\n role: \"user\",\n content: msg.text?.body || \"\",\n time: parseInt(msg.timestamp) * 1000,\n status: \"delivered\",\n senderId: msg.from,\n senderName: contact?.profile.name,\n createdAt: parseInt(msg.timestamp) * 1000,\n // Map attachments if present\n attachments: mapWhatsAppAttachments(msg),\n },\n });\n }\n }\n\n // Handle message status updates\n if (value.statuses) {\n for (const status of value.statuses) {\n events.push({\n type: \"message_status_update\",\n orgId,\n chatKey: `wa_${status.recipient_id}`,\n userId: status.recipient_id,\n timestamp: parseInt(status.timestamp) * 1000,\n data: {\n messageId: status.id,\n status: status.status,\n },\n });\n }\n }\n }\n }\n\n return events;\n}\n\n// Helper to map WhatsApp attachments\nfunction mapWhatsAppAttachments(msg: {\n type: WhatsAppMessageType;\n image?: { id: string };\n document?: { id: string; filename: string };\n audio?: { id: string };\n video?: { id: string };\n location?: { latitude: number; longitude: number; name?: string };\n}): Attachment[] | undefined {\n switch (msg.type) {\n case \"image\":\n return msg.image\n ? [{ type: \"image\" as AttachmentTypeTS, url: `whatsapp://media/${msg.image.id}` }]\n : undefined;\n case \"document\":\n return msg.document\n ? [\n {\n type: \"document\" as AttachmentTypeTS,\n url: `whatsapp://media/${msg.document.id}`,\n },\n ]\n : undefined;\n case \"audio\":\n return msg.audio\n ? [{ type: \"audio\" as AttachmentTypeTS, url: `whatsapp://media/${msg.audio.id}` }]\n : undefined;\n case \"video\":\n return msg.video\n ? [{ type: \"video\" as AttachmentTypeTS, url: `whatsapp://media/${msg.video.id}` }]\n : undefined;\n case \"location\":\n return msg.location\n ? [\n {\n type: \"location\" as AttachmentTypeTS,\n url: `geo:${msg.location.latitude},${msg.location.longitude}`,\n },\n ]\n : undefined;\n default:\n return undefined;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;AC4EA,eAAsB,sBACpB,KAC6B;AAC7B,MAAI,CAAC,IAAK,QAAO;AACjB,MAAI,IAAI,UAAU;AAChB,QAAI;AACF,YAAM,IAAI,MAAM,IAAI,SAAS;AAC7B,aAAO,KAAK;AAAA,IACd,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO,IAAI,SAAS;AACtB;AA8NO,SAAS,aAAa,QAAiB,OAAwB;AACpE,SAAO;AAAA,IACL,OAAO,QAAQ,QAAQ,IAAI,KAAK,SAAS,QAAQ,IAAI,MAAM;AAAA,IAAC;AAAA,IAC5D,MAAM,QAAQ,KAAK,KAAK,SAAS,QAAQ;AAAA,IACzC,MAAM,QAAQ,KAAK,KAAK,SAAS,QAAQ;AAAA,IACzC,OAAO,QAAQ,MAAM,KAAK,SAAS,QAAQ;AAAA,EAC7C;AACF;AAKO,IAAM,uBAA8C;AAAA,EACzD,YAAY;AAAA,EACZ,WAAW,CAAC,KAAM,KAAM,GAAI;AAAA,EAC5B,mBAAmB;AAAA,EACnB,gBAAgB;AAClB;AAOO,SAAS,uBACd,YACA,SAAsB,sBACd;AACR,QAAM;AAAA,IACJ,YAAY,qBAAqB;AAAA,IACjC,oBAAoB,qBAAqB;AAAA,IACzC,iBAAiB,qBAAqB;AAAA,EACxC,IAAI;AAEJ,MAAI,aAAa,UAAU,QAAQ;AACjC,WAAO,UAAU,UAAU;AAAA,EAC7B;AAEA,QAAM,eAAe,UAAU,UAAU,SAAS,CAAC,KAAK;AACxD,QAAM,cACJ,eAAe,KAAK,IAAI,mBAAmB,aAAa,UAAU,SAAS,CAAC;AAC9E,SAAO,KAAK,IAAI,aAAa,cAAc;AAC7C;;;ACjTO,SAAS,mBAAmB,UAA+B,CAAC,GAAkB;AACnF,QAAM;AAAA,IACJ,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,SAAS,aAAa,KAAK;AAAA,EAC7B,IAAI;AAGJ,MAAI;AACJ,MAAI;AACJ,MAAI,QAAwB;AAC5B,MAAI;AACJ,MAAI,aAAa;AACjB,MAAI;AACJ,MAAI,wBAAwB;AAG5B,QAAM,UAA6B;AAAA,IACjC,SAAS;AAAA,IACT,cAAc;AAAA,IACd,kBAAkB;AAAA,IAClB,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,eAAe;AAAA,EACjB;AAGA,QAAM,iBAAiB,oBAAI,IAAkB;AAC7C,QAAM,eAAe,oBAAI,IAA+B;AAGxD,WAAS,KAAK,OAAwB;AAEpC,YAAQ;AACR,YAAQ,gBAAgB,KAAK,IAAI;AAGjC,mBAAe,QAAQ,CAAC,YAAY;AAClC,UAAI;AACF,gBAAQ,KAAK;AAAA,MACf,SAAS,KAAK;AACZ,eAAO,MAAM,6BAA6B,GAAG;AAAA,MAC/C;AAAA,IACF,CAAC;AAGD,UAAM,WAAW,aAAa,IAAI,MAAM,IAAI;AAC5C,QAAI,UAAU;AACZ,eAAS,QAAQ,CAAC,YAAY;AAC5B,YAAI;AACF,kBAAQ,KAAK;AAAA,QACf,SAAS,KAAK;AACZ,iBAAO,MAAM,YAAY,MAAM,IAAI,aAAa,GAAG;AAAA,QACrD;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAGA,iBAAe,SAAS,UAAkB,MAAiD;AACzF,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AAEA,UAAM,MAAM,GAAG,OAAO,OAAO,IAAI,QAAQ;AACzC,WAAO,MAAM,QAAQ,QAAQ,IAAI,IAAI;AAGrC,UAAM,QAAQ,MAAM,sBAAsB,MAAM;AAChD,UAAM,UAAkC,EAAE,gBAAgB,mBAAmB;AAC7E,QAAI,MAAO,SAAQ,eAAe,IAAI,UAAU,KAAK;AAErD,UAAM,WAAW,MAAM,MAAM,KAAK;AAAA,MAChC,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,IAC3B,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,YAAY,MAAM,SAAS,KAAK;AACtC,YAAM,IAAI,MAAM,cAAc,SAAS,MAAM,MAAM,SAAS,EAAE;AAAA,IAChE;AAEA,UAAM,OAAO,MAAM,SAAS,KAAK;AAIjC,QAAI,QAAQ,OAAO,SAAS,YAAY,UAAU,MAAM;AACtD,aAAO,KAAK;AAAA,IACd;AAEA,WAAO;AAAA,EACT;AAGA,WAAS,cAAc,OAA2B;AAChD,QAAI,CAAC,MAAM,QAAQ,MAAM,SAAS,GAAI;AAEtC,QAAI;AACF,YAAM,OAAO,KAAK,MAAM,MAAM,IAAI;AAClC,aAAO,MAAM,aAAa,KAAK,IAAI;AACnC,WAAK,IAAI;AAAA,IACX,SAAS,KAAK;AACZ,aAAO,MAAM,gCAAgC,GAAG;AAAA,IAClD;AAAA,EACF;AAGA,WAAS,oBAAoB,IAAuB;AAElD,OAAG,iBAAiB,WAAW,aAAa;AAW5C,UAAM,aAAa;AAAA,MACjB;AAAA,MACA;AAAA,MAAe;AAAA,MAAU;AAAA,MAAkB;AAAA,MAC3C;AAAA,MAAqB;AAAA,MAAsB;AAAA,MAC3C;AAAA,MAAc;AAAA,MAAgB;AAAA,MAAgB;AAAA,MAC9C;AAAA,MAAoB;AAAA,MAAoB;AAAA,MACxC;AAAA,MAA0B;AAAA,MAC1B;AAAA,MAA2B;AAAA,MAC3B;AAAA,MAAwB;AAAA,MACxB;AAAA,MAAyB;AAAA,MAAiB;AAAA,MAC1C;AAAA,MAAgB;AAAA,MAAkB;AAAA,MAAe;AAAA,MACjD;AAAA,MAAkB;AAAA,MAClB;AAAA,MAAyB;AAAA,MAAa;AAAA,MACtC;AAAA,MAA0B;AAAA,MAC1B;AAAA,MAAiB;AAAA,MACjB;AAAA,MAAiC;AAAA,MACjC;AAAA,MAAkC;AAAA,MAClC;AAAA,MAAmB;AAAA,MAAiB;AAAA,MACpC;AAAA,MAAkB;AAAA,MAAmB;AAAA,MACrC;AAAA,MAAyB;AAAA,MAAgB;AAAA,MACzC;AAAA,MAAgB;AAAA,MAAgB;AAAA,MAAgB;AAAA,MAChD;AAAA,MAAoB;AAAA,MAAkB;AAAA,IACxC;AAEA,eAAW,QAAQ,CAAC,SAAS;AAC3B,SAAG,iBAAiB,MAAM,aAAa;AAAA,IACzC,CAAC;AAAA,EACH;AAGA,WAAS,oBAA0B;AACjC,QAAI,yBAAyB,CAAC,OAAQ;AAEtC,UAAM,aAAa,YAAY,cAAc,qBAAqB;AAClE,QAAI,cAAc,YAAY;AAC5B,aAAO,MAAM,gBAAgB,UAAU,YAAY;AACnD,cAAQ;AAAA,QACN,MAAM;AAAA,QACN,SAAS,gBAAgB,UAAU;AAAA,QACnC,WAAW;AAAA,QACX,WAAW,KAAK,IAAI;AAAA,MACtB;AACA;AAAA,IACF;AAEA,UAAM,WAAW,uBAAuB,YAAY,WAAW;AAC/D;AACA,YAAQ;AAER,WAAO,KAAK,mBAAmB,QAAQ,eAAe,UAAU,GAAG;AACnE,YAAQ;AAER,uBAAmB,WAAW,MAAM;AAClC,UAAI,QAAQ;AACV,kBAAU,QAAQ,MAAM,EAAE,MAAM,CAAC,QAAQ;AACvC,iBAAO,MAAM,qBAAqB,GAAG;AAAA,QACvC,CAAC;AAAA,MACH;AAAA,IACF,GAAG,QAAQ;AAAA,EACb;AAEA,QAAM,YAA2B;AAAA,IAC/B,MAAM,QAAQ,KAAqC;AACjD,eAAS;AACT,8BAAwB;AAGxB,UAAI,aAAa;AACf,oBAAY,MAAM;AAClB,sBAAc;AAAA,MAChB;AACA,UAAI,kBAAkB;AACpB,qBAAa,gBAAgB;AAC7B,2BAAmB;AAAA,MACrB;AAEA,cAAQ,aAAa,IAAI,iBAAiB;AAK1C,YAAM,eAAe,MAAM,sBAAsB,GAAG;AAEpD,aAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,cAAM,kBAAkB,KAAK,IAAI;AAIjC,cAAM,eAAe,IAAI,gBAAgB;AAAA,UACvC,OAAO,IAAI;AAAA,UACX,QAAQ,IAAI;AAAA,UACZ,YAAY,IAAI;AAAA,QAClB,CAAC;AACD,YAAI,IAAI,QAAS,cAAa,IAAI,UAAU,IAAI,OAAO;AACvD,YAAI,aAAc,cAAa,IAAI,SAAS,YAAY;AACxD,cAAM,MAAM,GAAG,IAAI,OAAO,WAAW,aAAa,SAAS,CAAC;AAE5D,eAAO,MAAM,kBAAkB,GAAG;AAClC,cAAM,KAAK,IAAI,YAAY,GAAG;AAE9B,WAAG,SAAS,MAAM;AAChB,gBAAM,iBAAiB,KAAK,IAAI,IAAI;AACpC,iBAAO,KAAK,gBAAgB,cAAc,IAAI;AAE9C,gBAAM,eAAe,aAAa;AAClC,kBAAQ;AACR,kBAAQ;AACR,uBAAa;AACb,kBAAQ,cAAc,KAAK,IAAI;AAC/B,kBAAQ,UAAU;AAElB,8BAAoB,EAAE;AAItB,cAAI,cAAc;AAChB,iBAAK;AAAA,cACH,MAAM;AAAA,cACN,OAAO,IAAI;AAAA,cACX,SAAS,IAAI,WAAW;AAAA,cACxB,QAAQ,IAAI;AAAA,cACZ,WAAW,KAAK,IAAI;AAAA,YACtB,CAAc;AAAA,UAChB;AAEA,kBAAQ;AAAA,QACV;AAEA,WAAG,UAAU,MAAM;AACjB,cAAI,GAAG,eAAe,YAAY,QAAQ;AACxC,kBAAM,WAA2B;AAAA,cAC/B,MAAM;AAAA,cACN,SAAS;AAAA,cACT,WAAW;AAAA,cACX,WAAW,KAAK,IAAI;AAAA,YACtB;AACA,oBAAQ;AACR,oBAAQ,YAAY;AACpB,oBAAQ;AAER,gBAAI,CAAC,uBAAuB;AAC1B,gCAAkB;AAAA,YACpB;AAGA,gBAAI,eAAe,GAAG;AACpB,qBAAO,QAAQ;AAAA,YACjB;AAAA,UACF;AAAA,QACF;AAEA,sBAAc;AAAA,MAChB,CAAC;AAAA,IACH;AAAA,IAEA,WAAW,cAAc,MAAY;AACnC,aAAO,KAAK,iBAAiB,EAAE,YAAY,CAAC;AAC5C,8BAAwB;AAExB,UAAI,kBAAkB;AACpB,qBAAa,gBAAgB;AAC7B,2BAAmB;AAAA,MACrB;AAEA,UAAI,aAAa;AACf,oBAAY,MAAM;AAClB,sBAAc;AAAA,MAChB;AAEA,cAAQ;AACR,mBAAa;AAAA,IACf;AAAA,IAEA,MAAM,KAAK,OAAiC;AAC1C,UAAI,CAAC,QAAQ;AACX,cAAM,IAAI,MAAM,yBAAyB;AAAA,MAC3C;AAGA,cAAQ,MAAM,MAAM;AAAA,QAClB,KAAK;AACH,gBAAM,SAAS,QAAQ;AAAA,YACrB,OAAO,MAAM;AAAA,YACb,SAAS,MAAM;AAAA,YACf,QAAQ,MAAM;AAAA,YACd,SAAS,MAAM;AAAA,YACf,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,UAC3C,CAAC;AACD;AAAA,QAEF,KAAK;AACH,gBAAM,SAAS,UAAU;AAAA,YACvB,OAAO,MAAM;AAAA,YACb,SAAS,MAAM;AAAA,YACf,QAAQ,MAAM;AAAA,YACd,QAAQ;AAAA,UACV,CAAC;AACD;AAAA,QAEF,KAAK;AACH,gBAAM,SAAS,UAAU;AAAA,YACvB,OAAO,MAAM;AAAA,YACb,SAAS,MAAM;AAAA,YACf,QAAQ,MAAM;AAAA,YACd,QAAQ;AAAA,UACV,CAAC;AACD;AAAA,QAEF,KAAK;AAEH,gBAAM,SAAS,QAAQ;AAAA,YACrB,OAAO,MAAM;AAAA,YACb,SAAS,MAAM;AAAA,YACf,QAAQ,MAAM;AAAA,UAChB,CAAC;AACD;AAAA,QAEF,KAAK;AAEH,gBAAM,SAAS,UAAU;AAAA,YACvB,OAAO,MAAM;AAAA,YACb,QAAQ,MAAM;AAAA,YACd,UAAU,MAAM;AAAA,UAClB,CAAC;AACD;AAAA,QAEF,KAAK;AACH,gBAAM,SAAS,OAAO;AAAA,YACpB,OAAO,MAAM;AAAA,YACb,SAAS,MAAM;AAAA,YACf,QAAQ,MAAM;AAAA,YACd,MAAM,MAAM;AAAA,UACd,CAAC;AACD;AAAA,QAEF;AAEE,gBAAM,SAAS,SAAS;AAAA,YACtB,MAAM,MAAM;AAAA,YACZ,OAAO,MAAM;AAAA,YACb,SAAS,MAAM;AAAA,YACf,QAAQ,MAAM;AAAA,YACd,MAAM,MAAM;AAAA,UACd,CAAC;AAAA,MACL;AAEA,cAAQ;AAAA,IACV;AAAA,IAEA,MAAM,YAAY,SAAqC;AACrD,UAAI,CAAC,QAAQ;AACX,cAAM,IAAI,MAAM,yBAAyB;AAAA,MAC3C;AAEA,YAAM,SAAS,QAAQ;AAAA,QACrB,OAAO,OAAO;AAAA,QACd,SAAS,OAAO;AAAA,QAChB,QAAQ,OAAO;AAAA,QACf;AAAA,MACF,CAAC;AAED,cAAQ;AAAA,IACV;AAAA,IAEA,MAAM,WAAWA,UAAyD;AACxE,UAAI,CAAC,QAAQ;AACX,cAAM,IAAI,MAAM,yBAAyB;AAAA,MAC3C;AAEA,YAAM,WAAW,MAAM,SAAS,UAAU;AAAA,QACxC,OAAOA,SAAQ;AAAA,QACf,QAAQA,SAAQ;AAAA,QAChB,UAAUA,SAAQ;AAAA,MACpB,CAAC;AAED,UAAI,CAAC,UAAU,SAAS;AACtB,cAAM,IAAI,MAAM,4CAA4C;AAAA,MAC9D;AAEA,aAAO,EAAE,SAAS,SAAS,QAAQ;AAAA,IACrC;AAAA,IAEA,MAAM,aAAaA,UAAqD;AACtE,UAAI,CAAC,QAAQ;AACX,cAAM,IAAI,MAAM,yBAAyB;AAAA,MAC3C;AAEA,YAAM,WAAW,MAAM,SAAS,QAAQ;AAAA,QACtC,OAAOA,SAAQ;AAAA,QACf,SAASA,SAAQ;AAAA,QACjB,QAAQA,SAAQ;AAAA,MAClB,CAAC;AAED,UAAI,CAAC,UAAU,MAAM;AACnB,cAAM,IAAI,MAAM,4CAA4C;AAAA,MAC9D;AAEA,aAAO;AAAA,QACL,MAAM,SAAS;AAAA,QACf,SAAS,SAAS;AAAA,MACpB;AAAA,IACF;AAAA,IAEA,UAAU,SAAoC;AAC5C,qBAAe,IAAI,OAAO;AAC1B,aAAO,MAAM,eAAe,OAAO,OAAO;AAAA,IAC5C;AAAA,IAEA,GAAG,WAAmB,SAAoC;AACxD,UAAI,CAAC,aAAa,IAAI,SAAS,GAAG;AAChC,qBAAa,IAAI,WAAW,oBAAI,IAAI,CAAC;AAAA,MACvC;AACA,mBAAa,IAAI,SAAS,EAAG,IAAI,OAAO;AACxC,aAAO,MAAM;AACX,cAAM,WAAW,aAAa,IAAI,SAAS;AAC3C,YAAI,UAAU;AACZ,mBAAS,OAAO,OAAO;AACvB,cAAI,SAAS,SAAS,GAAG;AACvB,yBAAa,OAAO,SAAS;AAAA,UAC/B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IAEA,WAA2B;AACzB,aAAO;AAAA,IACT;AAAA,IAEA,aAAgC;AAC9B,aAAO,EAAE,GAAG,QAAQ;AAAA,IACtB;AAAA,IAEA,WAAuC;AACrC,aAAO;AAAA,IACT;AAAA,IAEA,aAAmB;AACjB,cAAQ;AAAA,IACV;AAAA,EACF;AAEA,SAAO;AACT;;;AC7cO,SAAS,wBAAwB,UAAoC,CAAC,GAAkB;AAC7F,QAAM;AAAA,IACJ,cAAc;AAAA,IACd,QAAQ;AAAA,IACR,SAAS,aAAa,KAAK;AAAA,IAC3B,cAAc;AAAA,EAChB,IAAI;AAGJ,MAAI;AACJ,MAAI;AACJ,MAAI,QAAwB;AAC5B,MAAI;AACJ,MAAI,aAAa;AACjB,MAAI;AACJ,MAAI,wBAAwB;AAG5B,QAAM,UAA6B;AAAA,IACjC,SAAS;AAAA,IACT,cAAc;AAAA,IACd,kBAAkB;AAAA,IAClB,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,eAAe;AAAA,EACjB;AAGA,QAAM,iBAAiB,oBAAI,IAAkB;AAC7C,QAAM,eAAe,oBAAI,IAA+B;AAGxD,WAAS,KAAK,OAAwB;AACpC,YAAQ;AACR,YAAQ,gBAAgB,KAAK,IAAI;AAEjC,mBAAe,QAAQ,CAAC,YAAY;AAClC,UAAI;AACF,gBAAQ,KAAK;AAAA,MACf,SAAS,KAAK;AACZ,eAAO,MAAM,6BAA6B,GAAG;AAAA,MAC/C;AAAA,IACF,CAAC;AAED,UAAM,WAAW,aAAa,IAAI,MAAM,IAAI;AAC5C,QAAI,UAAU;AACZ,eAAS,QAAQ,CAAC,YAAY;AAC5B,YAAI;AACF,kBAAQ,KAAK;AAAA,QACf,SAAS,KAAK;AACZ,iBAAO,MAAM,YAAY,MAAM,IAAI,aAAa,GAAG;AAAA,QACrD;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AAGA,iBAAe,SAAS,UAAkB,MAAiD;AACzF,QAAI,CAAC,QAAQ;AACX,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AAEA,UAAM,MAAM,GAAG,OAAO,OAAO,IAAI,QAAQ;AACzC,WAAO,MAAM,QAAQ,QAAQ,IAAI,IAAI;AAGrC,UAAM,QAAQ,MAAM,sBAAsB,MAAM;AAChD,UAAM,UAAkC,EAAE,gBAAgB,mBAAmB;AAC7E,QAAI,MAAO,SAAQ,eAAe,IAAI,UAAU,KAAK;AAErD,UAAM,WAAW,MAAM,YAAY,KAAK;AAAA,MACtC,QAAQ;AAAA,MACR;AAAA,MACA,MAAM,KAAK,UAAU,IAAI;AAAA,IAC3B,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,YAAY,MAAM,SAAS,KAAK;AACtC,YAAM,IAAI,MAAM,cAAc,SAAS,MAAM,MAAM,SAAS,EAAE;AAAA,IAChE;AAEA,UAAM,OAAO,MAAM,SAAS,KAAK;AAIjC,QAAI,QAAQ,OAAO,SAAS,YAAY,UAAU,MAAM;AACtD,aAAO,KAAK;AAAA,IACd;AAEA,WAAO;AAAA,EACT;AAGA,WAAS,cAAc,OAA4B;AACjD,UAAM,SAAsB,CAAC;AAC7B,UAAM,QAAQ,MAAM,MAAM,IAAI;AAE9B,QAAI,YAAY;AAChB,QAAI,OAAO;AAEX,eAAW,QAAQ,OAAO;AACxB,UAAI,KAAK,WAAW,QAAQ,GAAG;AAC7B,oBAAY,KAAK,MAAM,CAAC,EAAE,KAAK;AAAA,MACjC,WAAW,KAAK,WAAW,OAAO,GAAG;AACnC,eAAO,KAAK,MAAM,CAAC,EAAE,KAAK;AAAA,MAC5B,WAAW,SAAS,MAAM,MAAM;AAE9B,YAAI;AACF,gBAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,iBAAO,KAAK,MAAM;AAAA,QACpB,QAAQ;AACN,iBAAO,KAAK,6BAA6B,IAAI;AAAA,QAC/C;AACA,oBAAY;AACZ,eAAO;AAAA,MACT;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAGA,iBAAe,YAAY,KAAqC;AAI9D,UAAM,eAAe,IAAI,gBAAgB;AAAA,MACvC,OAAO,IAAI;AAAA,MACX,QAAQ,IAAI;AAAA,MACZ,YAAY,IAAI;AAAA,IAClB,CAAC;AACD,QAAI,IAAI,QAAS,cAAa,IAAI,UAAU,IAAI,OAAO;AAGvD,UAAM,QAAQ,MAAM,sBAAsB,GAAG;AAC7C,QAAI,MAAO,cAAa,IAAI,SAAS,KAAK;AAC1C,UAAM,MAAM,GAAG,IAAI,OAAO,WAAW,aAAa,SAAS,CAAC;AAE5D,WAAO,MAAM,kBAAkB,GAAG;AAElC,sBAAkB,IAAI,gBAAgB;AAEtC,UAAM,gBAAwC;AAAA,MAC5C,QAAQ;AAAA,MACR,iBAAiB;AAAA,IACnB;AACA,QAAI,MAAO,eAAc,eAAe,IAAI,UAAU,KAAK;AAE3D,UAAM,WAAW,MAAM,YAAY,KAAK;AAAA,MACtC,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,QAAQ,gBAAgB;AAAA,IAC1B,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,IAAI,MAAM,6BAA6B,SAAS,MAAM,EAAE;AAAA,IAChE;AAEA,QAAI,CAAC,SAAS,MAAM;AAClB,YAAM,IAAI,MAAM,sDAAsD;AAAA,IACxE;AAEA,UAAM,SAAS,SAAS,KAAK,UAAU;AACvC,UAAM,UAAU,IAAI,YAAY;AAChC,QAAI,SAAS;AAGb,UAAM,aAAa,YAAY;AAC7B,UAAI;AACF,eAAO,MAAM;AACX,gBAAM,EAAE,MAAM,MAAM,IAAI,MAAM,OAAO,KAAK;AAE1C,cAAI,MAAM;AACR,mBAAO,KAAK,cAAc;AAC1B;AAAA,UACF;AAEA,oBAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAGhD,gBAAM,cAAc,OAAO,YAAY,MAAM;AAC7C,cAAI,gBAAgB,IAAI;AACtB,kBAAM,WAAW,OAAO,MAAM,GAAG,cAAc,CAAC;AAChD,qBAAS,OAAO,MAAM,cAAc,CAAC;AAErC,kBAAM,SAAS,cAAc,QAAQ;AACrC,mBAAO,QAAQ,IAAI;AAAA,UACrB;AAAA,QACF;AAAA,MACF,SAAS,KAAK;AACZ,YAAK,IAAc,SAAS,cAAc;AACxC,iBAAO,MAAM,gBAAgB;AAC7B;AAAA,QACF;AACA,eAAO,MAAM,iBAAiB,GAAG;AACjC,cAAM;AAAA,MACR;AAGA,UAAI,CAAC,uBAAuB;AAC1B,gBAAQ;AACR,0BAAkB;AAAA,MACpB;AAAA,IACF;AAGA,eAAW,EAAE,MAAM,CAAC,QAAQ;AAC1B,UAAI,CAAC,uBAAuB;AAC1B,gBAAQ;AAAA,UACN,MAAM;AAAA,UACN,SAAS,IAAI;AAAA,UACb,WAAW;AAAA,UACX,WAAW,KAAK,IAAI;AAAA,UACpB,eAAe;AAAA,QACjB;AACA,gBAAQ,YAAY;AACpB,gBAAQ;AACR,0BAAkB;AAAA,MACpB;AAAA,IACF,CAAC;AAAA,EACH;AAGA,WAAS,oBAA0B;AACjC,QAAI,yBAAyB,CAAC,OAAQ;AAEtC,UAAM,aAAa,YAAY,cAAc,qBAAqB;AAClE,QAAI,cAAc,YAAY;AAC5B,aAAO,MAAM,gBAAgB,UAAU,YAAY;AACnD,cAAQ;AAAA,QACN,MAAM;AAAA,QACN,SAAS,gBAAgB,UAAU;AAAA,QACnC,WAAW;AAAA,QACX,WAAW,KAAK,IAAI;AAAA,MACtB;AACA;AAAA,IACF;AAEA,UAAM,WAAW,uBAAuB,YAAY,WAAW;AAC/D;AACA,YAAQ;AAER,WAAO,KAAK,mBAAmB,QAAQ,eAAe,UAAU,GAAG;AACnE,YAAQ;AAER,uBAAmB,WAAW,MAAM;AAClC,UAAI,QAAQ;AACV,kBAAU,QAAQ,MAAM,EAAE,MAAM,CAAC,QAAQ;AACvC,iBAAO,MAAM,qBAAqB,GAAG;AAAA,QACvC,CAAC;AAAA,MACH;AAAA,IACF,GAAG,QAAQ;AAAA,EACb;AAEA,QAAM,YAA2B;AAAA,IAC/B,MAAM,QAAQ,KAAqC;AACjD,eAAS;AACT,8BAAwB;AAGxB,UAAI,iBAAiB;AACnB,wBAAgB,MAAM;AACtB,0BAAkB;AAAA,MACpB;AACA,UAAI,kBAAkB;AACpB,qBAAa,gBAAgB;AAC7B,2BAAmB;AAAA,MACrB;AAEA,cAAQ,aAAa,IAAI,iBAAiB;AAC1C,YAAM,kBAAkB,KAAK,IAAI;AAEjC,UAAI;AACF,cAAM,eAAe,aAAa;AAClC,cAAM,YAAY,GAAG;AAErB,cAAM,iBAAiB,KAAK,IAAI,IAAI;AACpC,eAAO,KAAK,gBAAgB,cAAc,IAAI;AAE9C,gBAAQ;AACR,gBAAQ;AACR,qBAAa;AACb,gBAAQ,cAAc,KAAK,IAAI;AAC/B,gBAAQ,UAAU;AAIlB,YAAI,cAAc;AAChB,eAAK;AAAA,YACH,MAAM;AAAA,YACN,OAAO,IAAI;AAAA,YACX,SAAS,IAAI,WAAW;AAAA,YACxB,QAAQ,IAAI;AAAA,YACZ,WAAW,KAAK,IAAI;AAAA,UACtB,CAAc;AAAA,QAChB;AAAA,MACF,SAAS,KAAK;AACZ,cAAM,eAA+B;AAAA,UACnC,MAAM;AAAA,UACN,SAAU,IAAc;AAAA,UACxB,WAAW;AAAA,UACX,WAAW,KAAK,IAAI;AAAA,UACpB,eAAe;AAAA,QACjB;AACA,gBAAQ;AACR,gBAAQ,YAAY;AACpB,gBAAQ;AAER,YAAI,CAAC,uBAAuB;AAC1B,4BAAkB;AAAA,QACpB;AAEA,cAAM;AAAA,MACR;AAAA,IACF;AAAA,IAEA,WAAW,cAAc,MAAY;AACnC,aAAO,KAAK,iBAAiB,EAAE,YAAY,CAAC;AAC5C,8BAAwB;AAExB,UAAI,kBAAkB;AACpB,qBAAa,gBAAgB;AAC7B,2BAAmB;AAAA,MACrB;AAEA,UAAI,iBAAiB;AACnB,wBAAgB,MAAM;AACtB,0BAAkB;AAAA,MACpB;AAEA,cAAQ;AACR,mBAAa;AAAA,IACf;AAAA,IAEA,MAAM,KAAK,OAAiC;AAC1C,UAAI,CAAC,QAAQ;AACX,cAAM,IAAI,MAAM,yBAAyB;AAAA,MAC3C;AAEA,cAAQ,MAAM,MAAM;AAAA,QAClB,KAAK;AACH,gBAAM,SAAS,QAAQ;AAAA,YACrB,OAAO,MAAM;AAAA,YACb,SAAS,MAAM;AAAA,YACf,QAAQ,MAAM;AAAA,YACd,SAAS,MAAM;AAAA,YACf,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,KAAK,IAAI,CAAC;AAAA,UAC3C,CAAC;AACD;AAAA,QAEF,KAAK;AACH,gBAAM,SAAS,UAAU;AAAA,YACvB,OAAO,MAAM;AAAA,YACb,SAAS,MAAM;AAAA,YACf,QAAQ,MAAM;AAAA,YACd,QAAQ;AAAA,UACV,CAAC;AACD;AAAA,QAEF,KAAK;AACH,gBAAM,SAAS,UAAU;AAAA,YACvB,OAAO,MAAM;AAAA,YACb,SAAS,MAAM;AAAA,YACf,QAAQ,MAAM;AAAA,YACd,QAAQ;AAAA,UACV,CAAC;AACD;AAAA,QAEF,KAAK;AACH,gBAAM,SAAS,QAAQ;AAAA,YACrB,OAAO,MAAM;AAAA,YACb,SAAS,MAAM;AAAA,YACf,QAAQ,MAAM;AAAA,UAChB,CAAC;AACD;AAAA,QAEF,KAAK;AACH,gBAAM,SAAS,UAAU;AAAA,YACvB,OAAO,MAAM;AAAA,YACb,QAAQ,MAAM;AAAA,YACd,UAAU,MAAM;AAAA,UAClB,CAAC;AACD;AAAA,QAEF,KAAK;AACH,gBAAM,SAAS,OAAO;AAAA,YACpB,OAAO,MAAM;AAAA,YACb,SAAS,MAAM;AAAA,YACf,QAAQ,MAAM;AAAA,YACd,MAAM,MAAM;AAAA,UACd,CAAC;AACD;AAAA,QAEF;AACE,gBAAM,SAAS,SAAS;AAAA,YACtB,MAAM,MAAM;AAAA,YACZ,OAAO,MAAM;AAAA,YACb,SAAS,MAAM;AAAA,YACf,QAAQ,MAAM;AAAA,YACd,MAAM,MAAM;AAAA,UACd,CAAC;AAAA,MACL;AAEA,cAAQ;AAAA,IACV;AAAA,IAEA,MAAM,YAAY,SAAqC;AACrD,UAAI,CAAC,QAAQ;AACX,cAAM,IAAI,MAAM,yBAAyB;AAAA,MAC3C;AAEA,YAAM,SAAS,QAAQ;AAAA,QACrB,OAAO,OAAO;AAAA,QACd,SAAS,OAAO;AAAA,QAChB,QAAQ,OAAO;AAAA,QACf;AAAA,MACF,CAAC;AAED,cAAQ;AAAA,IACV;AAAA,IAEA,MAAM,WAAWC,UAAyD;AACxE,UAAI,CAAC,QAAQ;AACX,cAAM,IAAI,MAAM,yBAAyB;AAAA,MAC3C;AAEA,YAAM,WAAW,MAAM,SAAS,UAAU;AAAA,QACxC,OAAOA,SAAQ;AAAA,QACf,QAAQA,SAAQ;AAAA,QAChB,UAAUA,SAAQ;AAAA,MACpB,CAAC;AAED,UAAI,CAAC,UAAU,SAAS;AACtB,cAAM,IAAI,MAAM,4CAA4C;AAAA,MAC9D;AAEA,aAAO,EAAE,SAAS,SAAS,QAAQ;AAAA,IACrC;AAAA,IAEA,MAAM,aAAaA,UAAqD;AACtE,UAAI,CAAC,QAAQ;AACX,cAAM,IAAI,MAAM,yBAAyB;AAAA,MAC3C;AAEA,YAAM,WAAW,MAAM,SAAS,QAAQ;AAAA,QACtC,OAAOA,SAAQ;AAAA,QACf,SAASA,SAAQ;AAAA,QACjB,QAAQA,SAAQ;AAAA,MAClB,CAAC;AAED,UAAI,CAAC,UAAU,MAAM;AACnB,cAAM,IAAI,MAAM,4CAA4C;AAAA,MAC9D;AAEA,aAAO;AAAA,QACL,MAAM,SAAS;AAAA,QACf,SAAS,SAAS;AAAA,MACpB;AAAA,IACF;AAAA,IAEA,UAAU,SAAoC;AAC5C,qBAAe,IAAI,OAAO;AAC1B,aAAO,MAAM,eAAe,OAAO,OAAO;AAAA,IAC5C;AAAA,IAEA,GAAG,WAAmB,SAAoC;AACxD,UAAI,CAAC,aAAa,IAAI,SAAS,GAAG;AAChC,qBAAa,IAAI,WAAW,oBAAI,IAAI,CAAC;AAAA,MACvC;AACA,mBAAa,IAAI,SAAS,EAAG,IAAI,OAAO;AACxC,aAAO,MAAM;AACX,cAAM,WAAW,aAAa,IAAI,SAAS;AAC3C,YAAI,UAAU;AACZ,mBAAS,OAAO,OAAO;AACvB,cAAI,SAAS,SAAS,GAAG;AACvB,yBAAa,OAAO,SAAS;AAAA,UAC/B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IAEA,WAA2B;AACzB,aAAO;AAAA,IACT;AAAA,IAEA,aAAgC;AAC9B,aAAO,EAAE,GAAG,QAAQ;AAAA,IACtB;AAAA,IAEA,WAAuC;AACrC,aAAO;AAAA,IACT;AAAA,IAEA,aAAmB;AACjB,cAAQ;AAAA,IACV;AAAA,EACF;AAEA,SAAO;AACT;;;AC7aO,SAAS,wBACd,UAAoC,CAAC,GACtB;AACf,QAAM,EAAE,QAAQ,OAAO,SAAS,aAAa,KAAK,EAAE,IAAI;AAExD,MAAI,QAAwB;AAC5B,MAAI;AACJ,MAAI;AAEJ,QAAM,UAA6B;AAAA,IACjC,SAAS;AAAA,IACT,cAAc;AAAA,IACd,kBAAkB;AAAA,IAClB,gBAAgB;AAAA,IAChB,gBAAgB;AAAA,IAChB,eAAe;AAAA,EACjB;AAEA,QAAM,iBAAiB,oBAAI,IAAkB;AAC7C,QAAM,eAAe,oBAAI,IAA+B;AAExD,QAAM,YAA2B;AAAA,IAC/B,MAAM,QAAQ,KAAqC;AACjD,aAAO;AAAA,QACL;AAAA,MACF;AACA,aAAO;AAAA,QACL;AAAA,MACF;AAEA,eAAS;AAOT,cAAQ;AACR,cAAQ,cAAc,KAAK,IAAI;AAAA,IACjC;AAAA,IAEA,aAAmB;AACjB,cAAQ;AAAA,IACV;AAAA,IAEA,MAAM,KAAK,OAAiC;AAC1C,UAAI,CAAC,QAAQ;AACX,cAAM,IAAI,MAAM,yBAAyB;AAAA,MAC3C;AAEA,aAAO,KAAK,2DAA2D;AAOvE,cAAQ;AAAA,IACV;AAAA,IAEA,MAAM,YAAY,SAAqC;AACrD,UAAI,CAAC,QAAQ;AACX,cAAM,IAAI,MAAM,yBAAyB;AAAA,MAC3C;AAEA,aAAO;AAAA,QACL;AAAA,MACF;AAWA,cAAQ;AAAA,IACV;AAAA,IAEA,MAAM,WAAWC,UAAyD;AACxE,aAAO;AAAA,QACL;AAAA,MACF;AAKA,aAAO,EAAE,SAAS,YAAYA,SAAQ,MAAM,IAAI,KAAK,IAAI,CAAC,GAAG;AAAA,IAC/D;AAAA,IAEA,MAAM,aAAaA,UAAqD;AACtE,aAAO;AAAA,QACL;AAAA,MACF;AAGA,aAAO;AAAA,QACL,MAAM;AAAA,UACJ,OAAOA,SAAQ;AAAA,UACf,KAAKA,SAAQ;AAAA,UACb,OAAO;AAAA,UACP,UAAU,CAAC;AAAA,UACX,aAAa,KAAK,IAAI;AAAA,UACtB,WAAW,KAAK,IAAI;AAAA,UACpB,OAAO,CAAC;AAAA,UACR,QAAQ;AAAA,UACR,WAAW;AAAA,UACX,mBAAmB;AAAA,UACnB,WAAW;AAAA,UACX,mBAAmB;AAAA,QACrB;AAAA,MACF;AAAA,IACF;AAAA,IAEA,UAAU,SAAoC;AAC5C,qBAAe,IAAI,OAAO;AAC1B,aAAO,MAAM,eAAe,OAAO,OAAO;AAAA,IAC5C;AAAA,IAEA,GAAG,WAAmB,SAAoC;AACxD,UAAI,CAAC,aAAa,IAAI,SAAS,GAAG;AAChC,qBAAa,IAAI,WAAW,oBAAI,IAAI,CAAC;AAAA,MACvC;AACA,mBAAa,IAAI,SAAS,EAAG,IAAI,OAAO;AACxC,aAAO,MAAM;AACX,cAAM,WAAW,aAAa,IAAI,SAAS;AAC3C,YAAI,UAAU;AACZ,mBAAS,OAAO,OAAO;AACvB,cAAI,SAAS,SAAS,GAAG;AACvB,yBAAa,OAAO,SAAS;AAAA,UAC/B;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,IAEA,WAA2B;AACzB,aAAO;AAAA,IACT;AAAA,IAEA,aAAgC;AAC9B,aAAO,EAAE,GAAG,QAAQ;AAAA,IACtB;AAAA,IAEA,WAAuC;AACrC,aAAO;AAAA,IACT;AAAA,IAEA,aAAmB;AACjB,cAAQ;AAAA,IACV;AAAA,EACF;AAEA,SAAO;AACT;AA6DO,SAAS,uBACd,SACA,OACa;AACb,QAAM,SAAsB,CAAC;AAE7B,aAAW,SAAS,QAAQ,OAAO;AACjC,eAAW,UAAU,MAAM,SAAS;AAClC,YAAM,QAAQ,OAAO;AAGrB,UAAI,MAAM,UAAU;AAClB,mBAAW,OAAO,MAAM,UAAU;AAChC,gBAAM,UAAU,MAAM,UAAU,KAAK,CAAC,MAAM,EAAE,UAAU,IAAI,IAAI;AAEhE,iBAAO,KAAK;AAAA,YACV,MAAM;AAAA,YACN;AAAA,YACA,SAAS,MAAM,IAAI,IAAI;AAAA;AAAA,YACvB,QAAQ,IAAI;AAAA,YACZ,WAAW,SAAS,IAAI,SAAS,IAAI;AAAA,YACrC,SAAS;AAAA,cACP,IAAI,IAAI;AAAA,cACR,MAAM;AAAA,cACN,SAAS,IAAI,MAAM,QAAQ;AAAA,cAC3B,MAAM,SAAS,IAAI,SAAS,IAAI;AAAA,cAChC,QAAQ;AAAA,cACR,UAAU,IAAI;AAAA,cACd,YAAY,SAAS,QAAQ;AAAA,cAC7B,WAAW,SAAS,IAAI,SAAS,IAAI;AAAA;AAAA,cAErC,aAAa,uBAAuB,GAAG;AAAA,YACzC;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAGA,UAAI,MAAM,UAAU;AAClB,mBAAW,UAAU,MAAM,UAAU;AACnC,iBAAO,KAAK;AAAA,YACV,MAAM;AAAA,YACN;AAAA,YACA,SAAS,MAAM,OAAO,YAAY;AAAA,YAClC,QAAQ,OAAO;AAAA,YACf,WAAW,SAAS,OAAO,SAAS,IAAI;AAAA,YACxC,MAAM;AAAA,cACJ,WAAW,OAAO;AAAA,cAClB,QAAQ,OAAO;AAAA,YACjB;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAGA,SAAS,uBAAuB,KAOH;AAC3B,UAAQ,IAAI,MAAM;AAAA,IAChB,KAAK;AACH,aAAO,IAAI,QACP,CAAC,EAAE,MAAM,SAA6B,KAAK,oBAAoB,IAAI,MAAM,EAAE,GAAG,CAAC,IAC/E;AAAA,IACN,KAAK;AACH,aAAO,IAAI,WACP;AAAA,QACE;AAAA,UACE,MAAM;AAAA,UACN,KAAK,oBAAoB,IAAI,SAAS,EAAE;AAAA,QAC1C;AAAA,MACF,IACA;AAAA,IACN,KAAK;AACH,aAAO,IAAI,QACP,CAAC,EAAE,MAAM,SAA6B,KAAK,oBAAoB,IAAI,MAAM,EAAE,GAAG,CAAC,IAC/E;AAAA,IACN,KAAK;AACH,aAAO,IAAI,QACP,CAAC,EAAE,MAAM,SAA6B,KAAK,oBAAoB,IAAI,MAAM,EAAE,GAAG,CAAC,IAC/E;AAAA,IACN,KAAK;AACH,aAAO,IAAI,WACP;AAAA,QACE;AAAA,UACE,MAAM;AAAA,UACN,KAAK,OAAO,IAAI,SAAS,QAAQ,IAAI,IAAI,SAAS,SAAS;AAAA,QAC7D;AAAA,MACF,IACA;AAAA,IACN;AACE,aAAO;AAAA,EACX;AACF;","names":["options","options","options"]}
|