@pedi/chika-sdk 1.0.7 → 1.0.8

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/dist/index.js CHANGED
@@ -445,8 +445,7 @@ async function createChatSession(config, channelId, profile, callbacks, networkM
445
445
  body: JSON.stringify({
446
446
  participant_id: profile.id,
447
447
  message_id: messageId
448
- }),
449
- signal: sessionAbort.signal
448
+ })
450
449
  });
451
450
  if (!res.ok) {
452
451
  await throwHttpError(res);
@@ -454,7 +453,7 @@ async function createChatSession(config, channelId, profile, callbacks, networkM
454
453
  };
455
454
  if (retryConfig) {
456
455
  try {
457
- await withRetry(readFn, MARK_READ_RETRY_CONFIG, sessionAbort.signal);
456
+ await withRetry(readFn, MARK_READ_RETRY_CONFIG);
458
457
  } catch (err) {
459
458
  if (err instanceof HttpError) {
460
459
  callbacks.onError(err);
@@ -770,6 +769,7 @@ var MessageQueue = class {
770
769
  entry.status = "failed";
771
770
  entry.error = err instanceof Error ? err : new Error(String(err));
772
771
  entry.retryCount++;
772
+ entry.reject(entry.error);
773
773
  this.config.onStatusChange?.();
774
774
  this.persist();
775
775
  }
@@ -827,6 +827,7 @@ function useChat({ config, channelId, profile, onMessage }) {
827
827
  const startingRef = (0, import_react.useRef)(false);
828
828
  const pendingOptimisticIds = (0, import_react.useRef)(/* @__PURE__ */ new Set());
829
829
  const [monitor, setMonitor] = (0, import_react.useState)(null);
830
+ const [monitorReady, setMonitorReady] = (0, import_react.useState)(false);
830
831
  const monitorRef = (0, import_react.useRef)(null);
831
832
  monitorRef.current = monitor;
832
833
  const queueRef = (0, import_react.useRef)(null);
@@ -839,18 +840,26 @@ function useChat({ config, channelId, profile, onMessage }) {
839
840
  (0, import_react.useEffect)(() => {
840
841
  if (!resilienceEnabled) {
841
842
  setMonitor(null);
843
+ setMonitorReady(true);
842
844
  return;
843
845
  }
844
846
  if (injectedMonitor) {
845
847
  setMonitor(injectedMonitor);
848
+ setMonitorReady(true);
846
849
  return;
847
850
  }
848
- const m = createNetworkMonitor();
849
- setMonitor(m);
850
- return () => {
851
- m.dispose();
851
+ try {
852
+ const m = createNetworkMonitor();
853
+ setMonitor(m);
854
+ setMonitorReady(true);
855
+ return () => {
856
+ m.dispose();
857
+ setMonitor(null);
858
+ };
859
+ } catch {
852
860
  setMonitor(null);
853
- };
861
+ setMonitorReady(true);
862
+ }
854
863
  }, [resilienceEnabled, injectedMonitor]);
855
864
  const callbacks = {
856
865
  onMessage: (message) => {
@@ -943,7 +952,7 @@ function useChat({ config, channelId, profile, onMessage }) {
943
952
  }
944
953
  (0, import_react.useEffect)(() => {
945
954
  disposedRef.current = false;
946
- if (resilienceEnabled && !monitor) return;
955
+ if (!monitorReady) return;
947
956
  startSession();
948
957
  return () => {
949
958
  disposedRef.current = true;
@@ -961,7 +970,7 @@ function useChat({ config, channelId, profile, onMessage }) {
961
970
  sessionRef.current?.disconnect();
962
971
  sessionRef.current = null;
963
972
  };
964
- }, [channelId, monitor]);
973
+ }, [channelId, monitorReady]);
965
974
  (0, import_react.useEffect)(() => {
966
975
  if (!queueEnabled || !monitor || !retryConfig) return;
967
976
  let entry = queueRegistry.get(channelId);
@@ -1170,24 +1179,33 @@ function useUnread(options) {
1170
1179
  const appStateRef = (0, import_react2.useRef)(import_react_native2.AppState.currentState);
1171
1180
  const backgroundTimerRef = (0, import_react2.useRef)(null);
1172
1181
  const backgroundGraceMs = config.backgroundGraceMs ?? (import_react_native2.Platform.OS === "android" ? DEFAULT_BACKGROUND_GRACE_MS2 : 0);
1173
- const [monitor, setMonitor] = (0, import_react2.useState)(null);
1182
+ const [monitorReady, setMonitorReady] = (0, import_react2.useState)(false);
1183
+ const monitorRef = (0, import_react2.useRef)(null);
1174
1184
  const resilienceEnabled = config.resilience !== false;
1175
1185
  const injectedMonitor = typeof config.resilience === "object" ? config.resilience.networkMonitor : void 0;
1176
1186
  (0, import_react2.useEffect)(() => {
1177
1187
  if (!resilienceEnabled) {
1178
- setMonitor(null);
1188
+ monitorRef.current = null;
1189
+ setMonitorReady(true);
1179
1190
  return;
1180
1191
  }
1181
1192
  if (injectedMonitor) {
1182
- setMonitor(injectedMonitor);
1193
+ monitorRef.current = injectedMonitor;
1194
+ setMonitorReady(true);
1183
1195
  return;
1184
1196
  }
1185
- const m = createNetworkMonitor();
1186
- setMonitor(m);
1187
- return () => {
1188
- m.dispose();
1189
- setMonitor(null);
1190
- };
1197
+ try {
1198
+ const m = createNetworkMonitor();
1199
+ monitorRef.current = m;
1200
+ setMonitorReady(true);
1201
+ return () => {
1202
+ m.dispose();
1203
+ monitorRef.current = null;
1204
+ };
1205
+ } catch {
1206
+ monitorRef.current = null;
1207
+ setMonitorReady(true);
1208
+ }
1191
1209
  }, [resilienceEnabled, injectedMonitor]);
1192
1210
  const connect = (0, import_react2.useCallback)(() => {
1193
1211
  connRef.current?.close();
@@ -1201,7 +1219,7 @@ function useUnread(options) {
1201
1219
  headers: customHeaders,
1202
1220
  reconnectDelayMs: configRef.current.reconnectDelayMs,
1203
1221
  customEvents: UNREAD_CUSTOM_EVENTS,
1204
- networkMonitor: monitor ?? void 0
1222
+ networkMonitor: monitorRef.current ?? void 0
1205
1223
  },
1206
1224
  {
1207
1225
  onOpen: () => {
@@ -1233,7 +1251,7 @@ function useUnread(options) {
1233
1251
  }
1234
1252
  }
1235
1253
  );
1236
- }, [channelId, participantId, monitor]);
1254
+ }, [channelId, participantId]);
1237
1255
  const disconnect = (0, import_react2.useCallback)(() => {
1238
1256
  connRef.current?.close();
1239
1257
  connRef.current = null;
@@ -1246,7 +1264,7 @@ function useUnread(options) {
1246
1264
  disconnect();
1247
1265
  return;
1248
1266
  }
1249
- if (resilienceEnabled && !monitor) return;
1267
+ if (!monitorReady) return;
1250
1268
  connect();
1251
1269
  return () => {
1252
1270
  disconnect();
@@ -1255,7 +1273,7 @@ function useUnread(options) {
1255
1273
  backgroundTimerRef.current = null;
1256
1274
  }
1257
1275
  };
1258
- }, [channelId, participantId, enabled, connect, disconnect]);
1276
+ }, [channelId, participantId, enabled, monitorReady, connect, disconnect]);
1259
1277
  (0, import_react2.useEffect)(() => {
1260
1278
  if (!enabled) return;
1261
1279
  const subscription = import_react_native2.AppState.addEventListener("change", (nextAppState) => {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/use-chat.ts","../src/errors.ts","../src/retry.ts","../src/resolve-url.ts","../src/sse-connection.ts","../src/session.ts","../src/network-monitor.ts","../src/message-queue.ts","../src/use-unread.ts"],"sourcesContent":["export { useChat } from './use-chat';\nexport { useUnread } from './use-unread';\nexport { createChatSession } from './session';\nexport { resolveServerUrl, createManifest } from './resolve-url';\nexport { createSSEConnection } from './sse-connection';\nexport { ChatDisconnectedError, ChannelClosedError, HttpError, RetryExhaustedError, QueueFullError } from './errors';\nexport { withRetry, isRetryableError, calculateBackoff, resolveRetryConfig } from './retry';\nexport { createNetworkMonitor } from './network-monitor';\nexport { createQueueStorage, createAsyncStorageAdapter } from './message-queue';\n\nexport type { ChatConfig, ChatStatus, UseChatOptions, UseChatReturn, ResilienceConfig } from './types';\nexport type { ChatSession, SessionCallbacks } from './session';\nexport type { UseUnreadOptions, UseUnreadReturn } from './use-unread';\nexport type { SSEConnection, SSEConnectionConfig, SSEConnectionCallbacks } from './sse-connection';\nexport type { RetryConfig } from './retry';\nexport type { NetworkMonitor } from './network-monitor';\nexport type { MessageSendStatus, QueuedMessage, QueueStorage } from './message-queue';\n\nexport type {\n ChatDomain,\n DefaultDomain,\n Message,\n Participant,\n MessageAttributes,\n SendMessageResponse,\n ChatManifest,\n ChatBucket,\n UnreadCountResponse,\n MarkReadRequest,\n SSEUnreadUpdateEvent,\n SSEUnreadClearEvent,\n SSEUnreadEvent,\n PediChat,\n PediRole,\n PediVehicle,\n PediLocation,\n PediParticipantMeta,\n PediMessageType,\n PediMessageAttributes,\n} from '@pedi/chika-types';\n","import { useEffect, useRef, useState, useCallback } from 'react';\nimport { AppState, Platform, type AppStateStatus } from 'react-native';\nimport type {\n ChatDomain,\n DefaultDomain,\n Message,\n Participant,\n MessageAttributes,\n SendMessageResponse,\n} from '@pedi/chika-types';\nimport type { UseChatOptions, UseChatReturn, ChatStatus } from './types';\nimport { ChatDisconnectedError, ChannelClosedError, QueueFullError, RetryExhaustedError } from './errors';\nimport { isRetryableError, resolveRetryConfig } from './retry';\nimport { createChatSession, type ChatSession, type SessionCallbacks } from './session';\nimport { createNetworkMonitor, type NetworkMonitor } from './network-monitor';\nimport { MessageQueue, type QueuedMessage } from './message-queue';\n\nconst DEFAULT_BACKGROUND_GRACE_MS = 2000;\nconst DEFAULT_MAX_QUEUE_SIZE = 50;\n\n// Module-scope queue registry, keyed by channelId. Survives component remounts.\nconst queueRegistry = new Map<string, { queue: MessageQueue; refCount: number }>();\n\n/**\n * React hook for real-time chat over SSE.\n * Manages connection lifecycle, AppState transitions, message deduplication, reconnection,\n * and optional network resilience (retry, offline queue, network monitoring).\n *\n * @template D - Chat domain type for role/message type narrowing. Defaults to DefaultDomain.\n */\nexport function useChat<D extends ChatDomain = DefaultDomain>(\n { config, channelId, profile, onMessage }: UseChatOptions<D>,\n): UseChatReturn<D> {\n const [messages, setMessages] = useState<Message<D>[]>([]);\n const [participants, setParticipants] = useState<Participant<D>[]>([]);\n const [status, setStatus] = useState<ChatStatus>('connecting');\n const [error, setError] = useState<Error | null>(null);\n const [pendingMessages, setPendingMessages] = useState<QueuedMessage[]>([]);\n\n const sessionRef = useRef<ChatSession<D> | null>(null);\n const disposedRef = useRef(false);\n const messagesRef = useRef(messages);\n messagesRef.current = messages;\n const statusRef = useRef(status);\n statusRef.current = status;\n const appStateRef = useRef<AppStateStatus>(AppState.currentState);\n const backgroundTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const profileRef = useRef(profile);\n profileRef.current = profile;\n const configRef = useRef(config);\n configRef.current = config;\n const onMessageRef = useRef(onMessage);\n onMessageRef.current = onMessage;\n const startingRef = useRef(false);\n const pendingOptimisticIds = useRef(new Set<string>());\n const [monitor, setMonitor] = useState<NetworkMonitor | null>(null);\n const monitorRef = useRef<NetworkMonitor | null>(null);\n monitorRef.current = monitor;\n const queueRef = useRef<MessageQueue | null>(null);\n\n const resilienceEnabled = config.resilience !== false;\n const queueEnabled =\n resilienceEnabled &&\n (typeof config.resilience === 'object' ? config.resilience.offlineQueue !== false : true);\n const retryConfig = resolveRetryConfig(config.resilience);\n const maxQueueSize =\n (resilienceEnabled && config.resilience && typeof config.resilience === 'object'\n ? config.resilience.maxQueueSize\n : undefined) ?? DEFAULT_MAX_QUEUE_SIZE;\n\n const backgroundGraceMs =\n config.backgroundGraceMs ?? (Platform.OS === 'android' ? DEFAULT_BACKGROUND_GRACE_MS : 0);\n\n // Resolve user-injected monitor (stable reference, no side effect)\n const injectedMonitor =\n typeof config.resilience === 'object' ? config.resilience.networkMonitor : undefined;\n\n // Create monitor in useEffect to avoid side effects during render.\n // Uses state (not ref) so the queue effect re-runs when monitor is ready.\n useEffect(() => {\n if (!resilienceEnabled) {\n setMonitor(null);\n return;\n }\n if (injectedMonitor) {\n setMonitor(injectedMonitor);\n return; // user owns lifecycle\n }\n const m = createNetworkMonitor();\n setMonitor(m);\n return () => {\n m.dispose();\n setMonitor(null);\n };\n }, [resilienceEnabled, injectedMonitor]);\n\n const callbacks: SessionCallbacks<D> = {\n onMessage: (message) => {\n if (disposedRef.current) return;\n let matchedOptimisticId: string | null = null;\n setMessages((prev: Message<D>[]) => {\n if (pendingOptimisticIds.current.size === 0) {\n return [...prev, message];\n }\n\n const optimisticIdx = prev.findIndex(\n (m) =>\n pendingOptimisticIds.current.has(m.id) &&\n m.sender_id === message.sender_id &&\n m.body === message.body &&\n m.type === message.type,\n );\n if (optimisticIdx !== -1) {\n matchedOptimisticId = prev[optimisticIdx]!.id;\n pendingOptimisticIds.current.delete(matchedOptimisticId);\n const next = [...prev];\n next[optimisticIdx] = message;\n return next;\n }\n return [...prev, message];\n });\n // Clean up any queued/failed queue entry — SSE confirmed delivery.\n // Done outside setMessages updater to avoid setState-in-setState.\n if (matchedOptimisticId) {\n const s = queueRef.current?.getStatus(matchedOptimisticId);\n if (s && s.status !== 'sending') {\n queueRef.current?.cancel(matchedOptimisticId);\n }\n }\n onMessageRef.current?.(message);\n },\n onStatusChange: (nextStatus) => {\n if (disposedRef.current) return;\n setStatus(nextStatus);\n if (nextStatus === 'connected') {\n setError(null);\n }\n },\n onError: (err) => {\n if (disposedRef.current) return;\n setError(err);\n },\n onResync: () => {\n if (disposedRef.current) return;\n startSession();\n },\n };\n\n async function startSession(): Promise<void> {\n if (startingRef.current) return;\n startingRef.current = true;\n\n const existing = sessionRef.current;\n if (existing) {\n existing.disconnect();\n sessionRef.current = null;\n }\n\n try {\n const session = await createChatSession<D>(\n configRef.current,\n channelId,\n profileRef.current,\n callbacks,\n monitorRef.current ?? undefined,\n );\n\n if (disposedRef.current) {\n session.disconnect();\n return;\n }\n\n sessionRef.current = session;\n setParticipants(session.initialParticipants);\n\n // Flush any messages queued while session was unavailable\n queueRef.current?.flush();\n\n // Re-merge any pending optimistic messages after resync\n if (pendingOptimisticIds.current.size > 0) {\n const pendingIds = pendingOptimisticIds.current;\n setMessages((prev) => {\n const pendingMsgs = prev.filter((m) => pendingIds.has(m.id));\n return [...session.initialMessages, ...pendingMsgs];\n });\n } else {\n setMessages(session.initialMessages);\n }\n } catch (err) {\n if (disposedRef.current) return;\n\n if (err instanceof ChannelClosedError) {\n setStatus('closed');\n setError(err);\n return;\n }\n\n setStatus('error');\n setError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n startingRef.current = false;\n }\n }\n\n // Session lifecycle\n useEffect(() => {\n disposedRef.current = false;\n // Don't start until monitor is resolved (or resilience is off)\n if (resilienceEnabled && !monitor) return;\n startSession();\n\n return () => {\n disposedRef.current = true;\n if (statusRef.current === 'connected' && sessionRef.current) {\n const lastMsg = messagesRef.current[messagesRef.current.length - 1];\n if (lastMsg) {\n sessionRef.current.markAsRead(lastMsg.id).catch(() => {});\n }\n }\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n }\n sessionRef.current?.disconnect();\n sessionRef.current = null;\n };\n }, [channelId, monitor]);\n\n // Module-scope queue lifecycle (ref-counted, survives remounts)\n useEffect(() => {\n if (!queueEnabled || !monitor || !retryConfig) return;\n\n let entry = queueRegistry.get(channelId);\n if (!entry) {\n const queue = new MessageQueue({\n channelId,\n maxSize: maxQueueSize,\n retryConfig,\n networkMonitor: monitor,\n storage:\n typeof config.resilience === 'object'\n ? config.resilience.queueStorage\n : undefined,\n onError: (err) => {\n if (!disposedRef.current) setError(err);\n },\n onStatusChange: () => {\n if (!disposedRef.current) {\n setPendingMessages(queueRef.current?.getAll() ?? []);\n }\n },\n });\n entry = { queue, refCount: 0 };\n queueRegistry.set(channelId, entry);\n }\n entry.refCount++;\n queueRef.current = entry.queue;\n\n return () => {\n const e = queueRegistry.get(channelId);\n if (e) {\n e.refCount--;\n if (e.refCount <= 0) {\n e.queue.dispose();\n queueRegistry.delete(channelId);\n }\n }\n queueRef.current = null;\n };\n }, [channelId, queueEnabled, monitor]);\n\n // Network monitor: auto-rejoin on connectivity return when in error state\n useEffect(() => {\n if (!monitor || !resilienceEnabled) return;\n\n const unsub = monitor.subscribe((connected: boolean) => {\n if (connected && statusRef.current === 'error' && !startingRef.current) {\n startSession();\n }\n });\n\n return unsub;\n }, [channelId, resilienceEnabled, monitor]);\n\n // AppState lifecycle\n useEffect(() => {\n function teardownSession(): void {\n sessionRef.current?.disconnect();\n sessionRef.current = null;\n setStatus('disconnected');\n }\n\n const subscription = AppState.addEventListener('change', (nextAppState) => {\n const prev = appStateRef.current;\n appStateRef.current = nextAppState;\n\n if (!sessionRef.current && nextAppState !== 'active') return;\n\n const shouldTeardown =\n nextAppState === 'background' ||\n (Platform.OS === 'ios' && nextAppState === 'inactive');\n\n if (nextAppState === 'active') {\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n return;\n }\n\n if (prev.match(/inactive|background/) && !sessionRef.current) {\n startSession();\n }\n } else if (shouldTeardown) {\n if (backgroundTimerRef.current) return;\n\n if (backgroundGraceMs === 0) {\n teardownSession();\n } else {\n backgroundTimerRef.current = setTimeout(() => {\n backgroundTimerRef.current = null;\n teardownSession();\n }, backgroundGraceMs);\n }\n }\n });\n\n return () => {\n subscription.remove();\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n }\n };\n }, [channelId, backgroundGraceMs]);\n\n const sendMessage = useCallback(\n async (type: D['messageType'], body: string, attributes?: MessageAttributes<D>): Promise<SendMessageResponse> => {\n const session = sessionRef.current;\n if (!session) throw new ChatDisconnectedError(statusRef.current);\n\n const optimistic = configRef.current.optimisticSend !== false;\n // Serves as both the optimistic message ID and the server-side idempotency key\n const messageKey = `optimistic_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\n\n if (optimistic) {\n pendingOptimisticIds.current.add(messageKey);\n const provisionalMsg: Message<D> = {\n id: messageKey,\n channel_id: channelId,\n sender_id: profileRef.current.id,\n sender_role: profileRef.current.role,\n type,\n body,\n attributes: (attributes ?? {}) as MessageAttributes<D>,\n created_at: new Date().toISOString(),\n };\n setMessages((prev) => [...prev, provisionalMsg]);\n }\n\n const doSend = () => {\n const s = sessionRef.current;\n if (!s) throw new ChatDisconnectedError(statusRef.current);\n return s.sendMessage(type, body, attributes, messageKey);\n };\n\n const handleSuccess = (response: SendMessageResponse): void => {\n if (optimistic) {\n pendingOptimisticIds.current.delete(messageKey);\n setMessages((prev) => {\n const stillPending = prev.some((m) => m.id === messageKey);\n if (!stillPending) return prev;\n return prev.map((m) =>\n m.id === messageKey\n ? { ...m, id: response.id, created_at: response.created_at }\n : m,\n );\n });\n }\n };\n\n const handleError = (_err: unknown): void => {\n if (optimistic) {\n pendingOptimisticIds.current.delete(messageKey);\n setMessages((prev) => prev.filter((m) => m.id !== messageKey));\n }\n };\n\n // Queue path: enqueue and let queue handle retry + offline\n if (queueRef.current) {\n try {\n const response = await queueRef.current.enqueue(doSend, messageKey);\n handleSuccess(response);\n return response;\n } catch (err) {\n if (err instanceof QueueFullError) {\n handleError(err);\n throw err;\n }\n if (err instanceof RetryExhaustedError || !isRetryableError(err)) {\n // For failed messages: if optimistic, mark as failed (keep in UI)\n // The pendingMessages state shows the status\n if (optimistic && err instanceof RetryExhaustedError) {\n // Keep optimistic message visible — queue tracks status as 'failed'.\n // Keep messageKey in pendingOptimisticIds so SSE reconciliation can\n // still match if the server eventually received the message.\n // Removed on explicit cancelMessage() call.\n } else {\n handleError(err);\n }\n throw err;\n }\n handleError(err);\n throw err;\n }\n }\n\n // Non-queue path: direct send (session.ts handles retry if enabled)\n try {\n const response = await doSend();\n handleSuccess(response);\n return response;\n } catch (err) {\n handleError(err);\n throw err;\n }\n },\n [channelId],\n );\n\n const cancelMessage = useCallback(\n (optimisticId: string) => {\n queueRef.current?.cancel(optimisticId);\n pendingOptimisticIds.current.delete(optimisticId);\n setMessages((prev) => prev.filter((m) => m.id !== optimisticId));\n },\n [],\n );\n\n const retryMessage = useCallback(\n (optimisticId: string) => {\n queueRef.current?.retry(optimisticId);\n },\n [],\n );\n\n const disconnect = useCallback(() => {\n sessionRef.current?.disconnect();\n sessionRef.current = null;\n setStatus('disconnected');\n }, []);\n\n return {\n messages,\n participants,\n status,\n error,\n sendMessage,\n disconnect,\n pendingMessages,\n cancelMessage,\n retryMessage,\n };\n}\n","import type { ChatStatus } from './types';\n\nexport class ChatDisconnectedError extends Error {\n constructor(public readonly status: ChatStatus) {\n super(`Cannot send message while ${status}`);\n this.name = 'ChatDisconnectedError';\n }\n}\n\nexport class ChannelClosedError extends Error {\n constructor(public readonly channelId: string) {\n super(`Channel ${channelId} is closed`);\n this.name = 'ChannelClosedError';\n }\n}\n\nexport class HttpError extends Error {\n constructor(\n public readonly status: number,\n public readonly body: string,\n public readonly retryAfter?: number,\n ) {\n super(`HTTP ${status}: ${body}`);\n this.name = 'HttpError';\n }\n}\n\nexport class RetryExhaustedError extends Error {\n constructor(\n public readonly operation: string,\n public readonly attempts: number,\n public readonly lastError: Error,\n ) {\n super(`${operation} failed after ${attempts} attempts: ${lastError.message}`);\n this.name = 'RetryExhaustedError';\n }\n}\n\nexport class QueueFullError extends Error {\n constructor(public readonly maxSize: number) {\n super(`Message queue full (max ${maxSize})`);\n this.name = 'QueueFullError';\n }\n}\n","import {\n HttpError,\n RetryExhaustedError,\n ChannelClosedError,\n ChatDisconnectedError,\n QueueFullError,\n} from './errors';\n\nexport interface RetryConfig {\n maxAttempts: number;\n baseDelayMs: number;\n maxDelayMs: number;\n jitterFactor: number;\n}\n\nexport const DEFAULT_RETRY_CONFIG: RetryConfig = {\n maxAttempts: 3,\n baseDelayMs: 1000,\n maxDelayMs: 10000,\n jitterFactor: 0.3,\n};\n\nexport function calculateBackoff(attempt: number, config: RetryConfig): number {\n const delay = Math.min(config.baseDelayMs * 2 ** attempt, config.maxDelayMs);\n const jitter = 1 + (Math.random() * 2 - 1) * config.jitterFactor;\n return Math.round(delay * jitter);\n}\n\nexport function isRetryableError(error: unknown): boolean {\n if (error instanceof ChannelClosedError) return false;\n if (error instanceof ChatDisconnectedError) return false;\n if (error instanceof QueueFullError) return false;\n if (error instanceof DOMException && error.name === 'AbortError') return false;\n\n if (error instanceof HttpError) {\n const { status } = error;\n if (status === 408 || status === 429) return true;\n if (status >= 500) return true;\n return false;\n }\n\n // TypeError from fetch = network failure\n if (error instanceof TypeError) return true;\n\n return false;\n}\n\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));\n return;\n }\n\n const timer = setTimeout(resolve, ms);\n\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));\n },\n { once: true },\n );\n });\n}\n\nexport function resolveRetryConfig(\n resilience: { retry?: Partial<RetryConfig> | false } | false | undefined,\n): RetryConfig | null {\n if (resilience === false) return null;\n if (!resilience || resilience.retry === undefined) return DEFAULT_RETRY_CONFIG;\n if (resilience.retry === false) return null;\n return { ...DEFAULT_RETRY_CONFIG, ...resilience.retry };\n}\n\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n config: RetryConfig = DEFAULT_RETRY_CONFIG,\n signal?: AbortSignal,\n): Promise<T> {\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt < config.maxAttempts; attempt++) {\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n\n try {\n return await fn();\n } catch (err) {\n lastError = err instanceof Error ? err : new Error(String(err));\n\n if (!isRetryableError(err)) throw lastError;\n\n if (attempt < config.maxAttempts - 1) {\n // Respect Retry-After header for 429 responses\n const delayMs =\n err instanceof HttpError && err.retryAfter != null\n ? err.retryAfter * 1000\n : calculateBackoff(attempt, config);\n\n await sleep(delayMs, signal);\n }\n }\n }\n\n throw new RetryExhaustedError(\n 'operation',\n config.maxAttempts,\n lastError!,\n );\n}\n","import type { ChatManifest } from '@pedi/chika-types';\n\n/**\n * Creates a single-server manifest. Use this when all channels route to the same server.\n *\n * @example\n * ```ts\n * const config: ChatConfig = { manifest: createManifest('https://chat.example.com') };\n * ```\n */\nexport function createManifest(serverUrl: string): ChatManifest {\n return { buckets: [{ group: 'default', range: [0, 99], server_url: serverUrl }] };\n}\n\nexport function resolveServerUrl(manifest: ChatManifest, channelId: string): string {\n const hash = [...channelId].reduce((sum, c) => sum + c.charCodeAt(0), 0) % 100;\n const bucket = manifest.buckets.find(\n (b) => hash >= b.range[0] && hash <= b.range[1],\n );\n if (!bucket) throw new Error(`No chat bucket for hash ${hash}`);\n return bucket.server_url;\n}\n","import EventSource from 'react-native-sse';\nimport { calculateBackoff, type RetryConfig } from './retry';\nimport type { NetworkMonitor } from './network-monitor';\n\nconst DEFAULT_RECONNECT_DELAY_MS = 3000;\n\nexport interface SSEConnectionConfig {\n url: string;\n headers?: Record<string, string>;\n reconnectDelayMs?: number;\n lastEventId?: string;\n customEvents?: string[];\n networkMonitor?: NetworkMonitor;\n}\n\nexport interface SSEConnectionCallbacks {\n onOpen?: () => void;\n onEvent: (eventType: string, data: string, lastEventId?: string) => void;\n onError?: (error: Error) => void;\n onClosed?: () => void;\n onReconnecting?: () => void;\n}\n\nexport interface SSEConnection {\n close: () => void;\n reconnectImmediate: () => void;\n}\n\nexport function createSSEConnection(\n config: SSEConnectionConfig,\n callbacks: SSEConnectionCallbacks,\n): SSEConnection {\n const baseDelay = config.reconnectDelayMs ?? DEFAULT_RECONNECT_DELAY_MS;\n const customEvents = config.customEvents ?? [];\n const monitor = config.networkMonitor;\n\n const backoffConfig: RetryConfig = {\n maxAttempts: Infinity,\n baseDelayMs: baseDelay,\n maxDelayMs: 30000,\n jitterFactor: 0.3,\n };\n\n let currentLastEventId = config.lastEventId;\n let es: EventSource<string> | null = null;\n let disposed = false;\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n let attempt = 0;\n let waitAbort: AbortController | null = null;\n\n function cleanup(): void {\n if (es) {\n es.removeAllEventListeners();\n es.close();\n es = null;\n }\n if (reconnectTimer) {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n }\n if (waitAbort) {\n waitAbort.abort();\n waitAbort = null;\n }\n }\n\n async function scheduleReconnect(): Promise<void> {\n if (disposed || reconnectTimer || waitAbort) return;\n callbacks.onReconnecting?.();\n cleanup();\n\n // Wait for network if monitor available\n if (monitor && !monitor.isConnected()) {\n waitAbort = new AbortController();\n try {\n await monitor.waitForOnline(waitAbort.signal);\n } catch {\n return; // aborted via dispose\n }\n waitAbort = null;\n if (disposed) return;\n }\n\n const delay = calculateBackoff(attempt++, backoffConfig);\n reconnectTimer = setTimeout(() => {\n reconnectTimer = null;\n connect();\n }, delay);\n }\n\n function connect(): void {\n if (disposed) return;\n\n es = new EventSource<string>(config.url, {\n headers: {\n ...config.headers,\n ...(currentLastEventId && { 'Last-Event-ID': currentLastEventId }),\n },\n pollingInterval: 0,\n });\n\n es.addEventListener('open', () => {\n if (disposed) return;\n attempt = 0;\n callbacks.onOpen?.();\n });\n\n es.addEventListener('message', (event) => {\n if (disposed || !event.data) return;\n if (event.lastEventId) {\n currentLastEventId = event.lastEventId;\n }\n callbacks.onEvent('message', event.data, event.lastEventId ?? undefined);\n });\n\n for (const eventName of customEvents) {\n es.addEventListener(eventName, (event) => {\n if (disposed) return;\n callbacks.onEvent(eventName, event.data ?? '', undefined);\n });\n }\n\n es.addEventListener('error', (event) => {\n if (disposed) return;\n\n const msg = 'message' in event ? String(event.message) : '';\n\n if (msg.includes('Channel is closed') || msg.includes('410')) {\n callbacks.onClosed?.();\n cleanup();\n disposed = true;\n return;\n }\n\n if (msg) callbacks.onError?.(new Error(msg));\n\n scheduleReconnect();\n });\n\n es.addEventListener('close', () => {\n if (disposed) return;\n scheduleReconnect();\n });\n }\n\n connect();\n\n return {\n close: () => {\n disposed = true;\n cleanup();\n },\n\n reconnectImmediate: () => {\n if (disposed) return;\n cleanup(); // does NOT set disposed\n attempt = 0;\n connect();\n },\n };\n}\n","import type {\n ChatDomain,\n DefaultDomain,\n Participant,\n Message,\n JoinResponse,\n SendMessageRequest,\n SendMessageResponse,\n MessageAttributes,\n} from '@pedi/chika-types';\nimport type { ChatConfig, ChatStatus } from './types';\nimport { ChatDisconnectedError, ChannelClosedError, HttpError } from './errors';\nimport { resolveServerUrl } from './resolve-url';\nimport { createSSEConnection, type SSEConnection } from './sse-connection';\nimport { withRetry, resolveRetryConfig, type RetryConfig } from './retry';\nimport type { NetworkMonitor } from './network-monitor';\n\nconst DEFAULT_RECONNECT_DELAY_MS = 3000;\nconst MAX_SEEN_IDS = 500;\n\nconst MARK_READ_RETRY_CONFIG: RetryConfig = {\n maxAttempts: 2,\n baseDelayMs: 500,\n maxDelayMs: 2000,\n jitterFactor: 0.3,\n};\n\nexport interface SessionCallbacks<D extends ChatDomain = DefaultDomain> {\n onMessage: (message: Message<D>) => void;\n onStatusChange: (status: ChatStatus) => void;\n onError: (error: Error) => void;\n onResync: () => void;\n}\n\nexport interface ChatSession<D extends ChatDomain = DefaultDomain> {\n serviceUrl: string;\n channelId: string;\n initialParticipants: Participant<D>[];\n initialMessages: Message<D>[];\n networkMonitor: NetworkMonitor | null;\n sendMessage: (\n type: D['messageType'],\n body: string,\n attributes?: MessageAttributes<D>,\n idempotencyKey?: string,\n ) => Promise<SendMessageResponse>;\n markAsRead: (messageId: string) => Promise<void>;\n disconnect: () => void;\n}\n\nfunction parseRetryAfter(res: Response): number | undefined {\n const header = res.headers.get('Retry-After');\n if (!header) return undefined;\n const seconds = Number(header);\n return Number.isFinite(seconds) ? seconds : undefined;\n}\n\nasync function throwHttpError(res: Response): Promise<never> {\n const body = await res.text().catch(() => '');\n throw new HttpError(res.status, body, parseRetryAfter(res));\n}\n\nexport async function createChatSession<D extends ChatDomain = DefaultDomain>(\n config: ChatConfig,\n channelId: string,\n profile: Participant<D>,\n callbacks: SessionCallbacks<D>,\n networkMonitor?: NetworkMonitor,\n): Promise<ChatSession<D>> {\n const serviceUrl = resolveServerUrl(config.manifest, channelId);\n const customHeaders = config.headers ?? {};\n const reconnectDelay = config.reconnectDelayMs ?? DEFAULT_RECONNECT_DELAY_MS;\n const retryConfig = resolveRetryConfig(config.resilience);\n\n const sessionAbort = new AbortController();\n\n callbacks.onStatusChange('connecting');\n\n const joinFn = async (): Promise<JoinResponse<D>> => {\n const joinRes = await fetch(`${serviceUrl}/channels/${channelId}/join`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...customHeaders },\n body: JSON.stringify(profile),\n signal: sessionAbort.signal,\n });\n\n if (joinRes.status === 410) {\n throw new ChannelClosedError(channelId);\n }\n\n if (!joinRes.ok) {\n await throwHttpError(joinRes);\n }\n\n return joinRes.json();\n };\n\n const joinData = retryConfig\n ? await withRetry(joinFn, retryConfig, sessionAbort.signal)\n : await joinFn();\n\n const { messages, participants, joined_at }: JoinResponse<D> = joinData;\n\n let lastEventId =\n messages.length > 0 ? messages[messages.length - 1]!.id : undefined;\n\n const joinedAt = joined_at;\n\n const seenMessageIds = new Set<string>(messages.map((m) => m.id));\n\n let sseConn: SSEConnection | null = null;\n let disposed = false;\n\n const TRIM_THRESHOLD = MAX_SEEN_IDS * 1.5;\n\n function trimSeenIds(): void {\n if (seenMessageIds.size <= TRIM_THRESHOLD) return;\n const ids = [...seenMessageIds];\n seenMessageIds.clear();\n for (const id of ids.slice(-MAX_SEEN_IDS)) {\n seenMessageIds.add(id);\n }\n }\n\n function connect(): void {\n if (disposed) return;\n\n const streamUrl = lastEventId\n ? `${serviceUrl}/channels/${channelId}/stream`\n : `${serviceUrl}/channels/${channelId}/stream?since_time=${encodeURIComponent(joinedAt)}`;\n\n sseConn = createSSEConnection(\n {\n url: streamUrl,\n headers: customHeaders,\n reconnectDelayMs: reconnectDelay,\n lastEventId,\n customEvents: ['resync'],\n networkMonitor,\n },\n {\n onOpen: () => {\n if (!disposed) callbacks.onStatusChange('connected');\n },\n onEvent: (eventType, data, eventId) => {\n if (disposed) return;\n\n if (eventType === 'message') {\n let message: Message<D>;\n try {\n message = JSON.parse(data);\n } catch {\n callbacks.onError(new Error('Failed to parse SSE message'));\n return;\n }\n\n if (eventId) {\n lastEventId = eventId;\n }\n\n if (seenMessageIds.has(message.id)) return;\n seenMessageIds.add(message.id);\n trimSeenIds();\n\n callbacks.onMessage(message);\n } else if (eventType === 'resync') {\n // Let onResync → startSession handle full session recreation.\n // Don't call reconnectImmediate() — startSession disconnects this\n // session anyway, so the reconnect would be immediately thrown away.\n callbacks.onResync();\n }\n },\n onError: (err) => {\n if (!disposed) callbacks.onError(err);\n },\n onClosed: () => {\n callbacks.onStatusChange('closed');\n disposed = true;\n },\n onReconnecting: () => {\n if (!disposed) callbacks.onStatusChange('reconnecting');\n },\n },\n );\n }\n\n connect();\n\n return {\n serviceUrl,\n channelId,\n initialParticipants: participants,\n initialMessages: messages,\n networkMonitor: networkMonitor ?? null,\n\n sendMessage: async (type, body, attributes, idempotencyKey) => {\n if (disposed) throw new ChatDisconnectedError('disconnected');\n\n const payload: SendMessageRequest<D> = {\n sender_id: profile.id,\n type,\n body,\n attributes,\n ...(idempotencyKey ? { idempotency_key: idempotencyKey } : {}),\n };\n\n const sendFn = async (): Promise<SendMessageResponse> => {\n const res = await fetch(\n `${serviceUrl}/channels/${channelId}/messages`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...customHeaders },\n body: JSON.stringify(payload),\n signal: sessionAbort.signal,\n },\n );\n\n if (!res.ok) {\n await throwHttpError(res);\n }\n\n return res.json();\n };\n\n const response = retryConfig\n ? await withRetry(sendFn, retryConfig, sessionAbort.signal)\n : await sendFn();\n\n seenMessageIds.add(response.id);\n return response;\n },\n\n markAsRead: async (messageId: string) => {\n const readFn = async (): Promise<void> => {\n const res = await fetch(`${serviceUrl}/channels/${channelId}/read`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...customHeaders },\n body: JSON.stringify({\n participant_id: profile.id,\n message_id: messageId,\n }),\n signal: sessionAbort.signal,\n });\n if (!res.ok) {\n await throwHttpError(res);\n }\n };\n\n if (retryConfig) {\n try {\n await withRetry(readFn, MARK_READ_RETRY_CONFIG, sessionAbort.signal);\n } catch (err) {\n // Surface non-retryable errors (403, 404) for dev diagnostics\n if (err instanceof HttpError) {\n callbacks.onError(err);\n }\n // Swallow RetryExhaustedError — markAsRead is best-effort\n }\n } else {\n await readFn();\n }\n },\n\n disconnect: () => {\n disposed = true;\n sessionAbort.abort();\n sseConn?.close();\n sseConn = null;\n callbacks.onStatusChange('disconnected');\n },\n };\n}\n","export interface NetworkMonitor {\n isConnected(): boolean;\n subscribe(cb: (connected: boolean) => void): () => void;\n waitForOnline(signal?: AbortSignal): Promise<void>;\n dispose(): void;\n}\n\nfunction createStubMonitor(): NetworkMonitor {\n return {\n isConnected: () => true,\n subscribe: () => () => {},\n waitForOnline: () => Promise.resolve(),\n dispose: () => {},\n };\n}\n\nlet resolvedNetInfo: any = undefined;\nlet netInfoResolved = false;\n\nfunction getNetInfo(): any {\n if (netInfoResolved) return resolvedNetInfo;\n netInfoResolved = true;\n try {\n resolvedNetInfo = require('@react-native-community/netinfo');\n } catch {\n resolvedNetInfo = null;\n }\n return resolvedNetInfo;\n}\n\nexport function createNetworkMonitor(): NetworkMonitor {\n const NetInfo = getNetInfo();\n if (!NetInfo) return createStubMonitor();\n\n const netInfoModule = NetInfo.default ?? NetInfo;\n\n let connected = true;\n const listeners = new Set<(connected: boolean) => void>();\n let unsubscribeNetInfo: (() => void) | null = null;\n\n unsubscribeNetInfo = netInfoModule.addEventListener(\n (state: { isConnected: boolean | null }) => {\n const next = state.isConnected !== false;\n if (next === connected) return;\n connected = next;\n for (const cb of listeners) {\n cb(connected);\n }\n },\n );\n\n return {\n isConnected: () => connected,\n\n subscribe: (cb) => {\n listeners.add(cb);\n return () => {\n listeners.delete(cb);\n };\n },\n\n waitForOnline: (signal?) => {\n if (connected) return Promise.resolve();\n\n return new Promise<void>((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));\n return;\n }\n\n const unsub = (): void => {\n listeners.delete(handler);\n signal?.removeEventListener('abort', onAbort);\n };\n\n const handler = (isOnline: boolean): void => {\n if (isOnline) {\n unsub();\n resolve();\n }\n };\n\n const onAbort = (): void => {\n unsub();\n reject(signal!.reason ?? new DOMException('Aborted', 'AbortError'));\n };\n\n listeners.add(handler);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n },\n\n dispose: () => {\n listeners.clear();\n unsubscribeNetInfo?.();\n unsubscribeNetInfo = null;\n },\n };\n}\n","import type { SendMessageResponse } from '@pedi/chika-types';\nimport { withRetry, type RetryConfig } from './retry';\nimport { QueueFullError, ChatDisconnectedError } from './errors';\nimport type { NetworkMonitor } from './network-monitor';\n\nexport interface QueueStorage {\n getItem(key: string): Promise<string | null>;\n setItem(key: string, value: string): Promise<void>;\n removeItem(key: string): Promise<void>;\n}\n\nlet resolvedStorage: { type: string; adapter: QueueStorage } | null | undefined;\n\nfunction tryRequire(name: string): any {\n try {\n return require(name);\n } catch {\n return null;\n }\n}\n\nfunction createMmkvAdapter(mod: any): QueueStorage {\n const MMKV = mod.MMKV ?? mod.default?.MMKV ?? mod;\n const instance = new MMKV({ id: 'chika-queue' });\n return {\n getItem: (key) => Promise.resolve(instance.getString(key) ?? null),\n setItem: (key, value) => { instance.set(key, value); return Promise.resolve(); },\n removeItem: (key) => { instance.delete(key); return Promise.resolve(); },\n };\n}\n\nfunction createAsyncStorageAdapterFrom(mod: any): QueueStorage {\n const storage = mod.default ?? mod;\n return {\n getItem: (key) => storage.getItem(key),\n setItem: (key, value) => storage.setItem(key, value),\n removeItem: (key) => storage.removeItem(key),\n };\n}\n\n/**\n * Auto-detect the best available storage for queue persistence.\n * Priority: react-native-mmkv (fastest, sync) > AsyncStorage > null.\n * Returns `null` if neither is installed — no hard dependencies.\n *\n * Usage:\n * ```ts\n * import { createQueueStorage } from '@pedi/chika-sdk';\n *\n * const config: ChatConfig = {\n * resilience: {\n * queueStorage: createQueueStorage() ?? undefined,\n * },\n * };\n * ```\n */\nexport function createQueueStorage(): QueueStorage | null {\n if (resolvedStorage !== undefined) return resolvedStorage?.adapter ?? null;\n\n // Priority 1: MMKV (synchronous, fastest)\n const mmkv = tryRequire('react-native-mmkv');\n if (mmkv) {\n try {\n const adapter = createMmkvAdapter(mmkv);\n resolvedStorage = { type: 'mmkv', adapter };\n return adapter;\n } catch {\n // MMKV instantiation can fail if native module isn't linked\n }\n }\n\n // Priority 2: AsyncStorage\n const asyncStorage = tryRequire('@react-native-async-storage/async-storage');\n if (asyncStorage) {\n const adapter = createAsyncStorageAdapterFrom(asyncStorage);\n resolvedStorage = { type: 'async-storage', adapter };\n return adapter;\n }\n\n resolvedStorage = null;\n return null;\n}\n\n/**\n * Creates a QueueStorage adapter backed by `@react-native-async-storage/async-storage`.\n * Returns `null` if the package is not installed.\n */\nexport function createAsyncStorageAdapter(): QueueStorage | null {\n const mod = tryRequire('@react-native-async-storage/async-storage');\n if (!mod) return null;\n return createAsyncStorageAdapterFrom(mod);\n}\n\nexport type MessageSendStatus = 'sending' | 'queued' | 'failed';\n\nexport interface QueuedMessage {\n optimisticId: string;\n status: MessageSendStatus;\n error?: Error;\n retryCount: number;\n}\n\ntype SendFn = () => Promise<SendMessageResponse>;\n\ninterface QueueEntry {\n optimisticId: string;\n sendFn: SendFn;\n status: MessageSendStatus;\n error?: Error;\n retryCount: number;\n abort: AbortController;\n resolve: (value: SendMessageResponse) => void;\n reject: (reason: Error) => void;\n}\n\n/** Serializable subset persisted to storage. */\ninterface PersistedEntry {\n optimisticId: string;\n retryCount: number;\n}\n\nexport interface MessageQueueConfig {\n channelId: string;\n maxSize: number;\n retryConfig: RetryConfig;\n networkMonitor: NetworkMonitor;\n storage?: QueueStorage;\n onError?: (error: Error) => void;\n onStatusChange?: () => void;\n}\n\nexport class MessageQueue {\n private entries: QueueEntry[] = [];\n private flushing = false;\n private unsubNetwork: (() => void) | null = null;\n private readonly storageKey: string;\n\n constructor(private readonly config: MessageQueueConfig) {\n this.storageKey = `chika_queue_${config.channelId}`;\n\n this.unsubNetwork = config.networkMonitor.subscribe((connected) => {\n if (connected) this.flush();\n });\n }\n\n /**\n * Restore queued messages from persistent storage on cold start.\n * Restored entries are fire-and-forget — there is no caller awaiting their\n * promise (the original enqueue() caller is gone after app restart).\n * Success/failure is reported via onStatusChange/onError callbacks.\n */\n async restore(\n rebuildSendFn: (optimisticId: string) => SendFn | null,\n ): Promise<void> {\n if (!this.config.storage) return;\n\n try {\n const raw = await this.config.storage.getItem(this.storageKey);\n if (!raw) return;\n\n const persisted: PersistedEntry[] = JSON.parse(raw);\n for (const entry of persisted) {\n const sendFn = rebuildSendFn(entry.optimisticId);\n if (!sendFn) continue;\n\n const abort = new AbortController();\n this.entries.push({\n optimisticId: entry.optimisticId,\n sendFn,\n status: 'queued',\n retryCount: entry.retryCount,\n abort,\n // No-op: restored entries have no caller awaiting the promise.\n resolve: () => {},\n reject: () => {},\n });\n }\n\n if (this.entries.length > 0) {\n this.config.onStatusChange?.();\n this.flush();\n }\n } catch {\n this.config.onError?.(new Error('Failed to restore message queue from storage'));\n }\n }\n\n get pendingCount(): number {\n return this.entries.length;\n }\n\n getAll(): QueuedMessage[] {\n return this.entries.map((e) => ({\n optimisticId: e.optimisticId,\n status: e.status,\n error: e.error,\n retryCount: e.retryCount,\n }));\n }\n\n getStatus(optimisticId: string): QueuedMessage | undefined {\n const entry = this.entries.find((e) => e.optimisticId === optimisticId);\n if (!entry) return undefined;\n return {\n optimisticId: entry.optimisticId,\n status: entry.status,\n error: entry.error,\n retryCount: entry.retryCount,\n };\n }\n\n enqueue(sendFn: SendFn, optimisticId: string): Promise<SendMessageResponse> {\n if (this.entries.length >= this.config.maxSize) {\n throw new QueueFullError(this.config.maxSize);\n }\n\n const abort = new AbortController();\n\n return new Promise<SendMessageResponse>((resolve, reject) => {\n const entry: QueueEntry = {\n optimisticId,\n sendFn,\n status: 'queued',\n retryCount: 0,\n abort,\n resolve,\n reject,\n };\n\n this.entries.push(entry);\n this.config.onStatusChange?.();\n this.persist();\n this.flush();\n });\n }\n\n cancel(optimisticId: string): void {\n const idx = this.entries.findIndex((e) => e.optimisticId === optimisticId);\n if (idx === -1) return;\n\n const entry = this.entries[idx]!;\n entry.abort.abort();\n entry.reject(new DOMException('Cancelled', 'AbortError'));\n this.entries.splice(idx, 1);\n this.config.onStatusChange?.();\n this.persist();\n }\n\n retry(optimisticId: string): void {\n const entry = this.entries.find((e) => e.optimisticId === optimisticId);\n if (!entry || entry.status !== 'failed') return;\n\n entry.status = 'queued';\n entry.error = undefined;\n entry.abort = new AbortController();\n this.config.onStatusChange?.();\n this.flush();\n }\n\n dispose(): void {\n this.unsubNetwork?.();\n this.unsubNetwork = null;\n\n for (const entry of this.entries) {\n entry.abort.abort();\n entry.reject(new DOMException('Queue disposed', 'AbortError'));\n }\n this.entries = [];\n }\n\n /**\n * Trigger a flush of queued messages. Returns immediately if a flush is\n * already in progress or the network is offline. The in-progress flush\n * will pick up any newly queued entries via its while loop.\n */\n async flush(): Promise<void> {\n if (this.flushing) return;\n if (!this.config.networkMonitor.isConnected()) return;\n\n this.flushing = true;\n let awaitingSession = false;\n\n try {\n while (this.entries.length > 0) {\n // Only pick up 'queued' entries — 'sending' means a previous flush is\n // mid-request. Server-side idempotency key prevents duplicates if the\n // prior request actually succeeded but we never got the response.\n const entry = this.entries.find((e) => e.status === 'queued');\n if (!entry) break;\n if (!this.config.networkMonitor.isConnected()) break;\n\n entry.status = 'sending';\n this.config.onStatusChange?.();\n\n try {\n const result = await withRetry(\n entry.sendFn,\n this.config.retryConfig,\n entry.abort.signal,\n );\n\n entry.resolve(result);\n const idx = this.entries.indexOf(entry);\n if (idx !== -1) this.entries.splice(idx, 1);\n this.config.onStatusChange?.();\n this.persist();\n } catch (err) {\n if (entry.abort.signal.aborted) continue; // cancelled, already removed\n\n // Session not yet reconnected — revert to 'queued' and stop flushing.\n // flush() will be called again when the session is re-established\n // via the explicit flush() call in use-chat.ts startSession().\n if (err instanceof ChatDisconnectedError) {\n entry.status = 'queued';\n this.config.onStatusChange?.();\n awaitingSession = true;\n break;\n }\n\n entry.status = 'failed';\n entry.error = err instanceof Error ? err : new Error(String(err));\n entry.retryCount++;\n this.config.onStatusChange?.();\n this.persist();\n\n // Don't block the queue on a failed entry — skip to next\n }\n }\n } finally {\n this.flushing = false;\n // Re-check: entries may have been added while we were flushing.\n // Skip if we're waiting for session — startSession() will call flush().\n if (\n !awaitingSession &&\n this.entries.some((e) => e.status === 'queued') &&\n this.config.networkMonitor.isConnected()\n ) {\n queueMicrotask(() => this.flush());\n }\n }\n }\n\n private persist(): void {\n if (!this.config.storage) return;\n\n const data: PersistedEntry[] = this.entries.map((e) => ({\n optimisticId: e.optimisticId,\n retryCount: e.retryCount,\n }));\n\n const onErr = (err: unknown) => {\n this.config.onError?.(\n err instanceof Error ? err : new Error('Queue storage write failed'),\n );\n };\n\n if (data.length === 0) {\n this.config.storage.removeItem(this.storageKey).catch(onErr);\n } else {\n this.config.storage\n .setItem(this.storageKey, JSON.stringify(data))\n .catch(onErr);\n }\n }\n}\n","import { useEffect, useRef, useState, useCallback } from 'react';\nimport { AppState, Platform, type AppStateStatus } from 'react-native';\nimport type { UnreadCountResponse, SSEUnreadUpdateEvent } from '@pedi/chika-types';\nimport type { ChatConfig } from './types';\nimport { resolveServerUrl } from './resolve-url';\nimport { createSSEConnection, type SSEConnection } from './sse-connection';\nimport { createNetworkMonitor, type NetworkMonitor } from './network-monitor';\n\nconst DEFAULT_BACKGROUND_GRACE_MS = 2000;\nconst UNREAD_CUSTOM_EVENTS = ['unread_snapshot', 'unread_update', 'unread_clear'];\n\nexport interface UseUnreadOptions {\n config: ChatConfig;\n channelId: string;\n participantId: string;\n enabled?: boolean;\n}\n\nexport interface UseUnreadReturn {\n unreadCount: number;\n hasUnread: boolean;\n lastMessageAt: string | null;\n error: Error | null;\n}\n\nexport function useUnread(options: UseUnreadOptions): UseUnreadReturn {\n const { config, channelId, participantId, enabled = true } = options;\n\n const [unreadCount, setUnreadCount] = useState(0);\n const [lastMessageAt, setLastMessageAt] = useState<string | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n const connRef = useRef<SSEConnection | null>(null);\n const configRef = useRef(config);\n configRef.current = config;\n const appStateRef = useRef<AppStateStatus>(AppState.currentState);\n const backgroundTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const backgroundGraceMs =\n config.backgroundGraceMs ?? (Platform.OS === 'android' ? DEFAULT_BACKGROUND_GRACE_MS : 0);\n\n const [monitor, setMonitor] = useState<NetworkMonitor | null>(null);\n const resilienceEnabled = config.resilience !== false;\n const injectedMonitor =\n typeof config.resilience === 'object' ? config.resilience.networkMonitor : undefined;\n\n useEffect(() => {\n if (!resilienceEnabled) {\n setMonitor(null);\n return;\n }\n if (injectedMonitor) {\n setMonitor(injectedMonitor);\n return;\n }\n const m = createNetworkMonitor();\n setMonitor(m);\n return () => {\n m.dispose();\n setMonitor(null);\n };\n }, [resilienceEnabled, injectedMonitor]);\n\n const connect = useCallback(() => {\n connRef.current?.close();\n connRef.current = null;\n\n const serviceUrl = resolveServerUrl(configRef.current.manifest, channelId);\n const customHeaders = configRef.current.headers ?? {};\n const url = `${serviceUrl}/channels/${channelId}/unread?participant_id=${encodeURIComponent(participantId)}`;\n\n connRef.current = createSSEConnection(\n {\n url,\n headers: customHeaders,\n reconnectDelayMs: configRef.current.reconnectDelayMs,\n customEvents: UNREAD_CUSTOM_EVENTS,\n networkMonitor: monitor ?? undefined,\n },\n {\n onOpen: () => {\n setError(null);\n },\n onEvent: (eventType, data) => {\n try {\n if (eventType === 'unread_snapshot') {\n const snapshot: UnreadCountResponse = JSON.parse(data);\n setUnreadCount(snapshot.unread_count);\n setLastMessageAt(snapshot.last_message_at);\n } else if (eventType === 'unread_update') {\n const update: SSEUnreadUpdateEvent['data'] = JSON.parse(data);\n setUnreadCount((prev) => prev + 1);\n setLastMessageAt(update.created_at);\n } else if (eventType === 'unread_clear') {\n const clear: { channel_id: string; unread_count: number } = JSON.parse(data);\n setUnreadCount(clear.unread_count);\n }\n } catch {\n setError(new Error('Failed to parse unread SSE event'));\n }\n },\n onError: (err) => {\n setError(err);\n },\n onClosed: () => {\n connRef.current = null;\n },\n },\n );\n }, [channelId, participantId, monitor]);\n\n const disconnect = useCallback(() => {\n connRef.current?.close();\n connRef.current = null;\n }, []);\n\n useEffect(() => {\n setUnreadCount(0);\n setLastMessageAt(null);\n setError(null);\n\n if (!enabled) {\n disconnect();\n return;\n }\n\n // Wait for monitor before connecting (avoids double-connect on mount)\n if (resilienceEnabled && !monitor) return;\n\n connect();\n\n return () => {\n disconnect();\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n }\n };\n }, [channelId, participantId, enabled, connect, disconnect]);\n\n useEffect(() => {\n if (!enabled) return;\n\n const subscription = AppState.addEventListener('change', (nextAppState) => {\n const prev = appStateRef.current;\n appStateRef.current = nextAppState;\n\n if (!connRef.current && nextAppState !== 'active') return;\n\n const shouldTeardown =\n nextAppState === 'background' ||\n (Platform.OS === 'ios' && nextAppState === 'inactive');\n\n if (nextAppState === 'active') {\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n return;\n }\n\n if (prev.match(/inactive|background/) && !connRef.current) {\n connect();\n }\n } else if (shouldTeardown) {\n if (backgroundTimerRef.current) return;\n\n if (backgroundGraceMs === 0) {\n disconnect();\n } else {\n backgroundTimerRef.current = setTimeout(() => {\n backgroundTimerRef.current = null;\n disconnect();\n }, backgroundGraceMs);\n }\n }\n });\n\n return () => {\n subscription.remove();\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n }\n };\n }, [enabled, backgroundGraceMs, connect, disconnect]);\n\n return { unreadCount, hasUnread: unreadCount > 0, lastMessageAt, error };\n}\n"],"mappings":"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","names":["EventSource","DEFAULT_RECONNECT_DELAY_MS","import_react","import_react_native","DEFAULT_BACKGROUND_GRACE_MS"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/use-chat.ts","../src/errors.ts","../src/retry.ts","../src/resolve-url.ts","../src/sse-connection.ts","../src/session.ts","../src/network-monitor.ts","../src/message-queue.ts","../src/use-unread.ts"],"sourcesContent":["export { useChat } from './use-chat';\nexport { useUnread } from './use-unread';\nexport { createChatSession } from './session';\nexport { resolveServerUrl, createManifest } from './resolve-url';\nexport { createSSEConnection } from './sse-connection';\nexport { ChatDisconnectedError, ChannelClosedError, HttpError, RetryExhaustedError, QueueFullError } from './errors';\nexport { withRetry, isRetryableError, calculateBackoff, resolveRetryConfig } from './retry';\nexport { createNetworkMonitor } from './network-monitor';\nexport { createQueueStorage, createAsyncStorageAdapter } from './message-queue';\n\nexport type { ChatConfig, ChatStatus, UseChatOptions, UseChatReturn, ResilienceConfig } from './types';\nexport type { ChatSession, SessionCallbacks } from './session';\nexport type { UseUnreadOptions, UseUnreadReturn } from './use-unread';\nexport type { SSEConnection, SSEConnectionConfig, SSEConnectionCallbacks } from './sse-connection';\nexport type { RetryConfig } from './retry';\nexport type { NetworkMonitor } from './network-monitor';\nexport type { MessageSendStatus, QueuedMessage, QueueStorage } from './message-queue';\n\nexport type {\n ChatDomain,\n DefaultDomain,\n Message,\n Participant,\n MessageAttributes,\n SendMessageResponse,\n ChatManifest,\n ChatBucket,\n UnreadCountResponse,\n MarkReadRequest,\n SSEUnreadUpdateEvent,\n SSEUnreadClearEvent,\n SSEUnreadEvent,\n PediChat,\n PediRole,\n PediVehicle,\n PediLocation,\n PediParticipantMeta,\n PediMessageType,\n PediMessageAttributes,\n} from '@pedi/chika-types';\n","import { useEffect, useRef, useState, useCallback } from 'react';\nimport { AppState, Platform, type AppStateStatus } from 'react-native';\nimport type {\n ChatDomain,\n DefaultDomain,\n Message,\n Participant,\n MessageAttributes,\n SendMessageResponse,\n} from '@pedi/chika-types';\nimport type { UseChatOptions, UseChatReturn, ChatStatus } from './types';\nimport { ChatDisconnectedError, ChannelClosedError, QueueFullError, RetryExhaustedError } from './errors';\nimport { isRetryableError, resolveRetryConfig } from './retry';\nimport { createChatSession, type ChatSession, type SessionCallbacks } from './session';\nimport { createNetworkMonitor, type NetworkMonitor } from './network-monitor';\nimport { MessageQueue, type QueuedMessage } from './message-queue';\n\nconst DEFAULT_BACKGROUND_GRACE_MS = 2000;\nconst DEFAULT_MAX_QUEUE_SIZE = 50;\n\n// Module-scope queue registry, keyed by channelId. Survives component remounts.\nconst queueRegistry = new Map<string, { queue: MessageQueue; refCount: number }>();\n\n/**\n * React hook for real-time chat over SSE.\n * Manages connection lifecycle, AppState transitions, message deduplication, reconnection,\n * and optional network resilience (retry, offline queue, network monitoring).\n *\n * @template D - Chat domain type for role/message type narrowing. Defaults to DefaultDomain.\n */\nexport function useChat<D extends ChatDomain = DefaultDomain>(\n { config, channelId, profile, onMessage }: UseChatOptions<D>,\n): UseChatReturn<D> {\n const [messages, setMessages] = useState<Message<D>[]>([]);\n const [participants, setParticipants] = useState<Participant<D>[]>([]);\n const [status, setStatus] = useState<ChatStatus>('connecting');\n const [error, setError] = useState<Error | null>(null);\n const [pendingMessages, setPendingMessages] = useState<QueuedMessage[]>([]);\n\n const sessionRef = useRef<ChatSession<D> | null>(null);\n const disposedRef = useRef(false);\n const messagesRef = useRef(messages);\n messagesRef.current = messages;\n const statusRef = useRef(status);\n statusRef.current = status;\n const appStateRef = useRef<AppStateStatus>(AppState.currentState);\n const backgroundTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const profileRef = useRef(profile);\n profileRef.current = profile;\n const configRef = useRef(config);\n configRef.current = config;\n const onMessageRef = useRef(onMessage);\n onMessageRef.current = onMessage;\n const startingRef = useRef(false);\n const pendingOptimisticIds = useRef(new Set<string>());\n const [monitor, setMonitor] = useState<NetworkMonitor | null>(null);\n const [monitorReady, setMonitorReady] = useState(false);\n const monitorRef = useRef<NetworkMonitor | null>(null);\n monitorRef.current = monitor;\n const queueRef = useRef<MessageQueue | null>(null);\n\n const resilienceEnabled = config.resilience !== false;\n const queueEnabled =\n resilienceEnabled &&\n (typeof config.resilience === 'object' ? config.resilience.offlineQueue !== false : true);\n const retryConfig = resolveRetryConfig(config.resilience);\n const maxQueueSize =\n (resilienceEnabled && config.resilience && typeof config.resilience === 'object'\n ? config.resilience.maxQueueSize\n : undefined) ?? DEFAULT_MAX_QUEUE_SIZE;\n\n const backgroundGraceMs =\n config.backgroundGraceMs ?? (Platform.OS === 'android' ? DEFAULT_BACKGROUND_GRACE_MS : 0);\n\n // Resolve user-injected monitor (stable reference, no side effect)\n const injectedMonitor =\n typeof config.resilience === 'object' ? config.resilience.networkMonitor : undefined;\n\n // Create monitor in useEffect to avoid side effects during render.\n // Uses state (not ref) so the queue effect re-runs when monitor is ready.\n useEffect(() => {\n if (!resilienceEnabled) {\n setMonitor(null);\n setMonitorReady(true);\n return;\n }\n if (injectedMonitor) {\n setMonitor(injectedMonitor);\n setMonitorReady(true);\n return; // user owns lifecycle\n }\n try {\n const m = createNetworkMonitor();\n setMonitor(m);\n setMonitorReady(true);\n return () => {\n m.dispose();\n setMonitor(null);\n };\n } catch {\n // NetInfo native module may be present but not linked — fall back to\n // no monitor so the session effect guard doesn't block forever.\n setMonitor(null);\n setMonitorReady(true);\n }\n }, [resilienceEnabled, injectedMonitor]);\n\n const callbacks: SessionCallbacks<D> = {\n onMessage: (message) => {\n if (disposedRef.current) return;\n let matchedOptimisticId: string | null = null;\n setMessages((prev: Message<D>[]) => {\n if (pendingOptimisticIds.current.size === 0) {\n return [...prev, message];\n }\n\n const optimisticIdx = prev.findIndex(\n (m) =>\n pendingOptimisticIds.current.has(m.id) &&\n m.sender_id === message.sender_id &&\n m.body === message.body &&\n m.type === message.type,\n );\n if (optimisticIdx !== -1) {\n matchedOptimisticId = prev[optimisticIdx]!.id;\n pendingOptimisticIds.current.delete(matchedOptimisticId);\n const next = [...prev];\n next[optimisticIdx] = message;\n return next;\n }\n return [...prev, message];\n });\n // Clean up any queued/failed queue entry — SSE confirmed delivery.\n // Done outside setMessages updater to avoid setState-in-setState.\n if (matchedOptimisticId) {\n const s = queueRef.current?.getStatus(matchedOptimisticId);\n if (s && s.status !== 'sending') {\n queueRef.current?.cancel(matchedOptimisticId);\n }\n }\n onMessageRef.current?.(message);\n },\n onStatusChange: (nextStatus) => {\n if (disposedRef.current) return;\n setStatus(nextStatus);\n if (nextStatus === 'connected') {\n setError(null);\n }\n },\n onError: (err) => {\n if (disposedRef.current) return;\n setError(err);\n },\n onResync: () => {\n if (disposedRef.current) return;\n startSession();\n },\n };\n\n async function startSession(): Promise<void> {\n if (startingRef.current) return;\n startingRef.current = true;\n\n const existing = sessionRef.current;\n if (existing) {\n existing.disconnect();\n sessionRef.current = null;\n }\n\n try {\n const session = await createChatSession<D>(\n configRef.current,\n channelId,\n profileRef.current,\n callbacks,\n monitorRef.current ?? undefined,\n );\n\n if (disposedRef.current) {\n session.disconnect();\n return;\n }\n\n sessionRef.current = session;\n setParticipants(session.initialParticipants);\n\n // Flush any messages queued while session was unavailable\n queueRef.current?.flush();\n\n // Re-merge any pending optimistic messages after resync\n if (pendingOptimisticIds.current.size > 0) {\n const pendingIds = pendingOptimisticIds.current;\n setMessages((prev) => {\n const pendingMsgs = prev.filter((m) => pendingIds.has(m.id));\n return [...session.initialMessages, ...pendingMsgs];\n });\n } else {\n setMessages(session.initialMessages);\n }\n } catch (err) {\n if (disposedRef.current) return;\n\n if (err instanceof ChannelClosedError) {\n setStatus('closed');\n setError(err);\n return;\n }\n\n setStatus('error');\n setError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n startingRef.current = false;\n }\n }\n\n // Session lifecycle\n useEffect(() => {\n disposedRef.current = false;\n // Don't start until monitor initialization is complete\n if (!monitorReady) return;\n startSession();\n\n return () => {\n disposedRef.current = true;\n if (statusRef.current === 'connected' && sessionRef.current) {\n const lastMsg = messagesRef.current[messagesRef.current.length - 1];\n if (lastMsg) {\n sessionRef.current.markAsRead(lastMsg.id).catch(() => {});\n }\n }\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n }\n sessionRef.current?.disconnect();\n sessionRef.current = null;\n };\n }, [channelId, monitorReady]);\n\n // Module-scope queue lifecycle (ref-counted, survives remounts)\n useEffect(() => {\n if (!queueEnabled || !monitor || !retryConfig) return;\n\n let entry = queueRegistry.get(channelId);\n if (!entry) {\n const queue = new MessageQueue({\n channelId,\n maxSize: maxQueueSize,\n retryConfig,\n networkMonitor: monitor,\n storage:\n typeof config.resilience === 'object'\n ? config.resilience.queueStorage\n : undefined,\n onError: (err) => {\n if (!disposedRef.current) setError(err);\n },\n onStatusChange: () => {\n if (!disposedRef.current) {\n setPendingMessages(queueRef.current?.getAll() ?? []);\n }\n },\n });\n entry = { queue, refCount: 0 };\n queueRegistry.set(channelId, entry);\n }\n entry.refCount++;\n queueRef.current = entry.queue;\n\n return () => {\n const e = queueRegistry.get(channelId);\n if (e) {\n e.refCount--;\n if (e.refCount <= 0) {\n e.queue.dispose();\n queueRegistry.delete(channelId);\n }\n }\n queueRef.current = null;\n };\n }, [channelId, queueEnabled, monitor]);\n\n // Network monitor: auto-rejoin on connectivity return when in error state\n useEffect(() => {\n if (!monitor || !resilienceEnabled) return;\n\n const unsub = monitor.subscribe((connected: boolean) => {\n if (connected && statusRef.current === 'error' && !startingRef.current) {\n startSession();\n }\n });\n\n return unsub;\n }, [channelId, resilienceEnabled, monitor]);\n\n // AppState lifecycle\n useEffect(() => {\n function teardownSession(): void {\n sessionRef.current?.disconnect();\n sessionRef.current = null;\n setStatus('disconnected');\n }\n\n const subscription = AppState.addEventListener('change', (nextAppState) => {\n const prev = appStateRef.current;\n appStateRef.current = nextAppState;\n\n if (!sessionRef.current && nextAppState !== 'active') return;\n\n const shouldTeardown =\n nextAppState === 'background' ||\n (Platform.OS === 'ios' && nextAppState === 'inactive');\n\n if (nextAppState === 'active') {\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n return;\n }\n\n if (prev.match(/inactive|background/) && !sessionRef.current) {\n startSession();\n }\n } else if (shouldTeardown) {\n if (backgroundTimerRef.current) return;\n\n if (backgroundGraceMs === 0) {\n teardownSession();\n } else {\n backgroundTimerRef.current = setTimeout(() => {\n backgroundTimerRef.current = null;\n teardownSession();\n }, backgroundGraceMs);\n }\n }\n });\n\n return () => {\n subscription.remove();\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n }\n };\n }, [channelId, backgroundGraceMs]);\n\n const sendMessage = useCallback(\n async (type: D['messageType'], body: string, attributes?: MessageAttributes<D>): Promise<SendMessageResponse> => {\n const session = sessionRef.current;\n if (!session) throw new ChatDisconnectedError(statusRef.current);\n\n const optimistic = configRef.current.optimisticSend !== false;\n // Serves as both the optimistic message ID and the server-side idempotency key\n const messageKey = `optimistic_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\n\n if (optimistic) {\n pendingOptimisticIds.current.add(messageKey);\n const provisionalMsg: Message<D> = {\n id: messageKey,\n channel_id: channelId,\n sender_id: profileRef.current.id,\n sender_role: profileRef.current.role,\n type,\n body,\n attributes: (attributes ?? {}) as MessageAttributes<D>,\n created_at: new Date().toISOString(),\n };\n setMessages((prev) => [...prev, provisionalMsg]);\n }\n\n const doSend = () => {\n const s = sessionRef.current;\n if (!s) throw new ChatDisconnectedError(statusRef.current);\n return s.sendMessage(type, body, attributes, messageKey);\n };\n\n const handleSuccess = (response: SendMessageResponse): void => {\n if (optimistic) {\n pendingOptimisticIds.current.delete(messageKey);\n setMessages((prev) => {\n const stillPending = prev.some((m) => m.id === messageKey);\n if (!stillPending) return prev;\n return prev.map((m) =>\n m.id === messageKey\n ? { ...m, id: response.id, created_at: response.created_at }\n : m,\n );\n });\n }\n };\n\n const handleError = (_err: unknown): void => {\n if (optimistic) {\n pendingOptimisticIds.current.delete(messageKey);\n setMessages((prev) => prev.filter((m) => m.id !== messageKey));\n }\n };\n\n // Queue path: enqueue and let queue handle retry + offline\n if (queueRef.current) {\n try {\n const response = await queueRef.current.enqueue(doSend, messageKey);\n handleSuccess(response);\n return response;\n } catch (err) {\n if (err instanceof QueueFullError) {\n handleError(err);\n throw err;\n }\n if (err instanceof RetryExhaustedError || !isRetryableError(err)) {\n // For failed messages: if optimistic, mark as failed (keep in UI)\n // The pendingMessages state shows the status\n if (optimistic && err instanceof RetryExhaustedError) {\n // Keep optimistic message visible — queue tracks status as 'failed'.\n // Keep messageKey in pendingOptimisticIds so SSE reconciliation can\n // still match if the server eventually received the message.\n // Removed on explicit cancelMessage() call.\n } else {\n handleError(err);\n }\n throw err;\n }\n handleError(err);\n throw err;\n }\n }\n\n // Non-queue path: direct send (session.ts handles retry if enabled)\n try {\n const response = await doSend();\n handleSuccess(response);\n return response;\n } catch (err) {\n handleError(err);\n throw err;\n }\n },\n [channelId],\n );\n\n const cancelMessage = useCallback(\n (optimisticId: string) => {\n queueRef.current?.cancel(optimisticId);\n pendingOptimisticIds.current.delete(optimisticId);\n setMessages((prev) => prev.filter((m) => m.id !== optimisticId));\n },\n [],\n );\n\n const retryMessage = useCallback(\n (optimisticId: string) => {\n queueRef.current?.retry(optimisticId);\n },\n [],\n );\n\n const disconnect = useCallback(() => {\n sessionRef.current?.disconnect();\n sessionRef.current = null;\n setStatus('disconnected');\n }, []);\n\n return {\n messages,\n participants,\n status,\n error,\n sendMessage,\n disconnect,\n pendingMessages,\n cancelMessage,\n retryMessage,\n };\n}\n","import type { ChatStatus } from './types';\n\nexport class ChatDisconnectedError extends Error {\n constructor(public readonly status: ChatStatus) {\n super(`Cannot send message while ${status}`);\n this.name = 'ChatDisconnectedError';\n }\n}\n\nexport class ChannelClosedError extends Error {\n constructor(public readonly channelId: string) {\n super(`Channel ${channelId} is closed`);\n this.name = 'ChannelClosedError';\n }\n}\n\nexport class HttpError extends Error {\n constructor(\n public readonly status: number,\n public readonly body: string,\n public readonly retryAfter?: number,\n ) {\n super(`HTTP ${status}: ${body}`);\n this.name = 'HttpError';\n }\n}\n\nexport class RetryExhaustedError extends Error {\n constructor(\n public readonly operation: string,\n public readonly attempts: number,\n public readonly lastError: Error,\n ) {\n super(`${operation} failed after ${attempts} attempts: ${lastError.message}`);\n this.name = 'RetryExhaustedError';\n }\n}\n\nexport class QueueFullError extends Error {\n constructor(public readonly maxSize: number) {\n super(`Message queue full (max ${maxSize})`);\n this.name = 'QueueFullError';\n }\n}\n","import {\n HttpError,\n RetryExhaustedError,\n ChannelClosedError,\n ChatDisconnectedError,\n QueueFullError,\n} from './errors';\n\nexport interface RetryConfig {\n maxAttempts: number;\n baseDelayMs: number;\n maxDelayMs: number;\n jitterFactor: number;\n}\n\nexport const DEFAULT_RETRY_CONFIG: RetryConfig = {\n maxAttempts: 3,\n baseDelayMs: 1000,\n maxDelayMs: 10000,\n jitterFactor: 0.3,\n};\n\nexport function calculateBackoff(attempt: number, config: RetryConfig): number {\n const delay = Math.min(config.baseDelayMs * 2 ** attempt, config.maxDelayMs);\n const jitter = 1 + (Math.random() * 2 - 1) * config.jitterFactor;\n return Math.round(delay * jitter);\n}\n\nexport function isRetryableError(error: unknown): boolean {\n if (error instanceof ChannelClosedError) return false;\n if (error instanceof ChatDisconnectedError) return false;\n if (error instanceof QueueFullError) return false;\n if (error instanceof DOMException && error.name === 'AbortError') return false;\n\n if (error instanceof HttpError) {\n const { status } = error;\n if (status === 408 || status === 429) return true;\n if (status >= 500) return true;\n return false;\n }\n\n // TypeError from fetch = network failure\n if (error instanceof TypeError) return true;\n\n return false;\n}\n\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));\n return;\n }\n\n const timer = setTimeout(resolve, ms);\n\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));\n },\n { once: true },\n );\n });\n}\n\nexport function resolveRetryConfig(\n resilience: { retry?: Partial<RetryConfig> | false } | false | undefined,\n): RetryConfig | null {\n if (resilience === false) return null;\n if (!resilience || resilience.retry === undefined) return DEFAULT_RETRY_CONFIG;\n if (resilience.retry === false) return null;\n return { ...DEFAULT_RETRY_CONFIG, ...resilience.retry };\n}\n\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n config: RetryConfig = DEFAULT_RETRY_CONFIG,\n signal?: AbortSignal,\n): Promise<T> {\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt < config.maxAttempts; attempt++) {\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n\n try {\n return await fn();\n } catch (err) {\n lastError = err instanceof Error ? err : new Error(String(err));\n\n if (!isRetryableError(err)) throw lastError;\n\n if (attempt < config.maxAttempts - 1) {\n // Respect Retry-After header for 429 responses\n const delayMs =\n err instanceof HttpError && err.retryAfter != null\n ? err.retryAfter * 1000\n : calculateBackoff(attempt, config);\n\n await sleep(delayMs, signal);\n }\n }\n }\n\n throw new RetryExhaustedError(\n 'operation',\n config.maxAttempts,\n lastError!,\n );\n}\n","import type { ChatManifest } from '@pedi/chika-types';\n\n/**\n * Creates a single-server manifest. Use this when all channels route to the same server.\n *\n * @example\n * ```ts\n * const config: ChatConfig = { manifest: createManifest('https://chat.example.com') };\n * ```\n */\nexport function createManifest(serverUrl: string): ChatManifest {\n return { buckets: [{ group: 'default', range: [0, 99], server_url: serverUrl }] };\n}\n\nexport function resolveServerUrl(manifest: ChatManifest, channelId: string): string {\n const hash = [...channelId].reduce((sum, c) => sum + c.charCodeAt(0), 0) % 100;\n const bucket = manifest.buckets.find(\n (b) => hash >= b.range[0] && hash <= b.range[1],\n );\n if (!bucket) throw new Error(`No chat bucket for hash ${hash}`);\n return bucket.server_url;\n}\n","import EventSource from 'react-native-sse';\nimport { calculateBackoff, type RetryConfig } from './retry';\nimport type { NetworkMonitor } from './network-monitor';\n\nconst DEFAULT_RECONNECT_DELAY_MS = 3000;\n\nexport interface SSEConnectionConfig {\n url: string;\n headers?: Record<string, string>;\n reconnectDelayMs?: number;\n lastEventId?: string;\n customEvents?: string[];\n networkMonitor?: NetworkMonitor;\n}\n\nexport interface SSEConnectionCallbacks {\n onOpen?: () => void;\n onEvent: (eventType: string, data: string, lastEventId?: string) => void;\n onError?: (error: Error) => void;\n onClosed?: () => void;\n onReconnecting?: () => void;\n}\n\nexport interface SSEConnection {\n close: () => void;\n reconnectImmediate: () => void;\n}\n\nexport function createSSEConnection(\n config: SSEConnectionConfig,\n callbacks: SSEConnectionCallbacks,\n): SSEConnection {\n const baseDelay = config.reconnectDelayMs ?? DEFAULT_RECONNECT_DELAY_MS;\n const customEvents = config.customEvents ?? [];\n const monitor = config.networkMonitor;\n\n const backoffConfig: RetryConfig = {\n maxAttempts: Infinity,\n baseDelayMs: baseDelay,\n maxDelayMs: 30000,\n jitterFactor: 0.3,\n };\n\n let currentLastEventId = config.lastEventId;\n let es: EventSource<string> | null = null;\n let disposed = false;\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n let attempt = 0;\n let waitAbort: AbortController | null = null;\n\n function cleanup(): void {\n if (es) {\n es.removeAllEventListeners();\n es.close();\n es = null;\n }\n if (reconnectTimer) {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n }\n if (waitAbort) {\n waitAbort.abort();\n waitAbort = null;\n }\n }\n\n async function scheduleReconnect(): Promise<void> {\n if (disposed || reconnectTimer || waitAbort) return;\n callbacks.onReconnecting?.();\n cleanup();\n\n // Wait for network if monitor available\n if (monitor && !monitor.isConnected()) {\n waitAbort = new AbortController();\n try {\n await monitor.waitForOnline(waitAbort.signal);\n } catch {\n return; // aborted via dispose\n }\n waitAbort = null;\n if (disposed) return;\n }\n\n const delay = calculateBackoff(attempt++, backoffConfig);\n reconnectTimer = setTimeout(() => {\n reconnectTimer = null;\n connect();\n }, delay);\n }\n\n function connect(): void {\n if (disposed) return;\n\n es = new EventSource<string>(config.url, {\n headers: {\n ...config.headers,\n ...(currentLastEventId && { 'Last-Event-ID': currentLastEventId }),\n },\n pollingInterval: 0,\n });\n\n es.addEventListener('open', () => {\n if (disposed) return;\n attempt = 0;\n callbacks.onOpen?.();\n });\n\n es.addEventListener('message', (event) => {\n if (disposed || !event.data) return;\n if (event.lastEventId) {\n currentLastEventId = event.lastEventId;\n }\n callbacks.onEvent('message', event.data, event.lastEventId ?? undefined);\n });\n\n for (const eventName of customEvents) {\n es.addEventListener(eventName, (event) => {\n if (disposed) return;\n callbacks.onEvent(eventName, event.data ?? '', undefined);\n });\n }\n\n es.addEventListener('error', (event) => {\n if (disposed) return;\n\n const msg = 'message' in event ? String(event.message) : '';\n\n if (msg.includes('Channel is closed') || msg.includes('410')) {\n callbacks.onClosed?.();\n cleanup();\n disposed = true;\n return;\n }\n\n if (msg) callbacks.onError?.(new Error(msg));\n\n scheduleReconnect();\n });\n\n es.addEventListener('close', () => {\n if (disposed) return;\n scheduleReconnect();\n });\n }\n\n connect();\n\n return {\n close: () => {\n disposed = true;\n cleanup();\n },\n\n reconnectImmediate: () => {\n if (disposed) return;\n cleanup(); // does NOT set disposed\n attempt = 0;\n connect();\n },\n };\n}\n","import type {\n ChatDomain,\n DefaultDomain,\n Participant,\n Message,\n JoinResponse,\n SendMessageRequest,\n SendMessageResponse,\n MessageAttributes,\n} from '@pedi/chika-types';\nimport type { ChatConfig, ChatStatus } from './types';\nimport { ChatDisconnectedError, ChannelClosedError, HttpError } from './errors';\nimport { resolveServerUrl } from './resolve-url';\nimport { createSSEConnection, type SSEConnection } from './sse-connection';\nimport { withRetry, resolveRetryConfig, type RetryConfig } from './retry';\nimport type { NetworkMonitor } from './network-monitor';\n\nconst DEFAULT_RECONNECT_DELAY_MS = 3000;\nconst MAX_SEEN_IDS = 500;\n\nconst MARK_READ_RETRY_CONFIG: RetryConfig = {\n maxAttempts: 2,\n baseDelayMs: 500,\n maxDelayMs: 2000,\n jitterFactor: 0.3,\n};\n\nexport interface SessionCallbacks<D extends ChatDomain = DefaultDomain> {\n onMessage: (message: Message<D>) => void;\n onStatusChange: (status: ChatStatus) => void;\n onError: (error: Error) => void;\n onResync: () => void;\n}\n\nexport interface ChatSession<D extends ChatDomain = DefaultDomain> {\n serviceUrl: string;\n channelId: string;\n initialParticipants: Participant<D>[];\n initialMessages: Message<D>[];\n networkMonitor: NetworkMonitor | null;\n sendMessage: (\n type: D['messageType'],\n body: string,\n attributes?: MessageAttributes<D>,\n idempotencyKey?: string,\n ) => Promise<SendMessageResponse>;\n markAsRead: (messageId: string) => Promise<void>;\n disconnect: () => void;\n}\n\nfunction parseRetryAfter(res: Response): number | undefined {\n const header = res.headers.get('Retry-After');\n if (!header) return undefined;\n const seconds = Number(header);\n return Number.isFinite(seconds) ? seconds : undefined;\n}\n\nasync function throwHttpError(res: Response): Promise<never> {\n const body = await res.text().catch(() => '');\n throw new HttpError(res.status, body, parseRetryAfter(res));\n}\n\nexport async function createChatSession<D extends ChatDomain = DefaultDomain>(\n config: ChatConfig,\n channelId: string,\n profile: Participant<D>,\n callbacks: SessionCallbacks<D>,\n networkMonitor?: NetworkMonitor,\n): Promise<ChatSession<D>> {\n const serviceUrl = resolveServerUrl(config.manifest, channelId);\n const customHeaders = config.headers ?? {};\n const reconnectDelay = config.reconnectDelayMs ?? DEFAULT_RECONNECT_DELAY_MS;\n const retryConfig = resolveRetryConfig(config.resilience);\n\n const sessionAbort = new AbortController();\n\n callbacks.onStatusChange('connecting');\n\n const joinFn = async (): Promise<JoinResponse<D>> => {\n const joinRes = await fetch(`${serviceUrl}/channels/${channelId}/join`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...customHeaders },\n body: JSON.stringify(profile),\n signal: sessionAbort.signal,\n });\n\n if (joinRes.status === 410) {\n throw new ChannelClosedError(channelId);\n }\n\n if (!joinRes.ok) {\n await throwHttpError(joinRes);\n }\n\n return joinRes.json();\n };\n\n const joinData = retryConfig\n ? await withRetry(joinFn, retryConfig, sessionAbort.signal)\n : await joinFn();\n\n const { messages, participants, joined_at }: JoinResponse<D> = joinData;\n\n let lastEventId =\n messages.length > 0 ? messages[messages.length - 1]!.id : undefined;\n\n const joinedAt = joined_at;\n\n const seenMessageIds = new Set<string>(messages.map((m) => m.id));\n\n let sseConn: SSEConnection | null = null;\n let disposed = false;\n\n const TRIM_THRESHOLD = MAX_SEEN_IDS * 1.5;\n\n function trimSeenIds(): void {\n if (seenMessageIds.size <= TRIM_THRESHOLD) return;\n const ids = [...seenMessageIds];\n seenMessageIds.clear();\n for (const id of ids.slice(-MAX_SEEN_IDS)) {\n seenMessageIds.add(id);\n }\n }\n\n function connect(): void {\n if (disposed) return;\n\n const streamUrl = lastEventId\n ? `${serviceUrl}/channels/${channelId}/stream`\n : `${serviceUrl}/channels/${channelId}/stream?since_time=${encodeURIComponent(joinedAt)}`;\n\n sseConn = createSSEConnection(\n {\n url: streamUrl,\n headers: customHeaders,\n reconnectDelayMs: reconnectDelay,\n lastEventId,\n customEvents: ['resync'],\n networkMonitor,\n },\n {\n onOpen: () => {\n if (!disposed) callbacks.onStatusChange('connected');\n },\n onEvent: (eventType, data, eventId) => {\n if (disposed) return;\n\n if (eventType === 'message') {\n let message: Message<D>;\n try {\n message = JSON.parse(data);\n } catch {\n callbacks.onError(new Error('Failed to parse SSE message'));\n return;\n }\n\n if (eventId) {\n lastEventId = eventId;\n }\n\n if (seenMessageIds.has(message.id)) return;\n seenMessageIds.add(message.id);\n trimSeenIds();\n\n callbacks.onMessage(message);\n } else if (eventType === 'resync') {\n // Let onResync → startSession handle full session recreation.\n // Don't call reconnectImmediate() — startSession disconnects this\n // session anyway, so the reconnect would be immediately thrown away.\n callbacks.onResync();\n }\n },\n onError: (err) => {\n if (!disposed) callbacks.onError(err);\n },\n onClosed: () => {\n callbacks.onStatusChange('closed');\n disposed = true;\n },\n onReconnecting: () => {\n if (!disposed) callbacks.onStatusChange('reconnecting');\n },\n },\n );\n }\n\n connect();\n\n return {\n serviceUrl,\n channelId,\n initialParticipants: participants,\n initialMessages: messages,\n networkMonitor: networkMonitor ?? null,\n\n sendMessage: async (type, body, attributes, idempotencyKey) => {\n if (disposed) throw new ChatDisconnectedError('disconnected');\n\n const payload: SendMessageRequest<D> = {\n sender_id: profile.id,\n type,\n body,\n attributes,\n ...(idempotencyKey ? { idempotency_key: idempotencyKey } : {}),\n };\n\n const sendFn = async (): Promise<SendMessageResponse> => {\n const res = await fetch(\n `${serviceUrl}/channels/${channelId}/messages`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...customHeaders },\n body: JSON.stringify(payload),\n signal: sessionAbort.signal,\n },\n );\n\n if (!res.ok) {\n await throwHttpError(res);\n }\n\n return res.json();\n };\n\n const response = retryConfig\n ? await withRetry(sendFn, retryConfig, sessionAbort.signal)\n : await sendFn();\n\n seenMessageIds.add(response.id);\n return response;\n },\n\n markAsRead: async (messageId: string) => {\n // markAsRead intentionally does NOT use sessionAbort.signal — it must\n // survive disconnect() since it's called on unmount right before disconnect.\n const readFn = async (): Promise<void> => {\n const res = await fetch(`${serviceUrl}/channels/${channelId}/read`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...customHeaders },\n body: JSON.stringify({\n participant_id: profile.id,\n message_id: messageId,\n }),\n });\n if (!res.ok) {\n await throwHttpError(res);\n }\n };\n\n if (retryConfig) {\n try {\n await withRetry(readFn, MARK_READ_RETRY_CONFIG);\n } catch (err) {\n // Surface non-retryable errors (403, 404) for dev diagnostics\n if (err instanceof HttpError) {\n callbacks.onError(err);\n }\n // Swallow RetryExhaustedError — markAsRead is best-effort\n }\n } else {\n await readFn();\n }\n },\n\n disconnect: () => {\n disposed = true;\n sessionAbort.abort();\n sseConn?.close();\n sseConn = null;\n callbacks.onStatusChange('disconnected');\n },\n };\n}\n","export interface NetworkMonitor {\n isConnected(): boolean;\n subscribe(cb: (connected: boolean) => void): () => void;\n waitForOnline(signal?: AbortSignal): Promise<void>;\n dispose(): void;\n}\n\nfunction createStubMonitor(): NetworkMonitor {\n return {\n isConnected: () => true,\n subscribe: () => () => {},\n waitForOnline: () => Promise.resolve(),\n dispose: () => {},\n };\n}\n\nlet resolvedNetInfo: any = undefined;\nlet netInfoResolved = false;\n\nfunction getNetInfo(): any {\n if (netInfoResolved) return resolvedNetInfo;\n netInfoResolved = true;\n try {\n resolvedNetInfo = require('@react-native-community/netinfo');\n } catch {\n resolvedNetInfo = null;\n }\n return resolvedNetInfo;\n}\n\nexport function createNetworkMonitor(): NetworkMonitor {\n const NetInfo = getNetInfo();\n if (!NetInfo) return createStubMonitor();\n\n const netInfoModule = NetInfo.default ?? NetInfo;\n\n let connected = true;\n const listeners = new Set<(connected: boolean) => void>();\n let unsubscribeNetInfo: (() => void) | null = null;\n\n unsubscribeNetInfo = netInfoModule.addEventListener(\n (state: { isConnected: boolean | null }) => {\n const next = state.isConnected !== false;\n if (next === connected) return;\n connected = next;\n for (const cb of listeners) {\n cb(connected);\n }\n },\n );\n\n return {\n isConnected: () => connected,\n\n subscribe: (cb) => {\n listeners.add(cb);\n return () => {\n listeners.delete(cb);\n };\n },\n\n waitForOnline: (signal?) => {\n if (connected) return Promise.resolve();\n\n return new Promise<void>((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));\n return;\n }\n\n const unsub = (): void => {\n listeners.delete(handler);\n signal?.removeEventListener('abort', onAbort);\n };\n\n const handler = (isOnline: boolean): void => {\n if (isOnline) {\n unsub();\n resolve();\n }\n };\n\n const onAbort = (): void => {\n unsub();\n reject(signal!.reason ?? new DOMException('Aborted', 'AbortError'));\n };\n\n listeners.add(handler);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n },\n\n dispose: () => {\n listeners.clear();\n unsubscribeNetInfo?.();\n unsubscribeNetInfo = null;\n },\n };\n}\n","import type { SendMessageResponse } from '@pedi/chika-types';\nimport { withRetry, type RetryConfig } from './retry';\nimport { QueueFullError, ChatDisconnectedError } from './errors';\nimport type { NetworkMonitor } from './network-monitor';\n\nexport interface QueueStorage {\n getItem(key: string): Promise<string | null>;\n setItem(key: string, value: string): Promise<void>;\n removeItem(key: string): Promise<void>;\n}\n\nlet resolvedStorage: { type: string; adapter: QueueStorage } | null | undefined;\n\nfunction tryRequire(name: string): any {\n try {\n return require(name);\n } catch {\n return null;\n }\n}\n\nfunction createMmkvAdapter(mod: any): QueueStorage {\n const MMKV = mod.MMKV ?? mod.default?.MMKV ?? mod;\n const instance = new MMKV({ id: 'chika-queue' });\n return {\n getItem: (key) => Promise.resolve(instance.getString(key) ?? null),\n setItem: (key, value) => { instance.set(key, value); return Promise.resolve(); },\n removeItem: (key) => { instance.delete(key); return Promise.resolve(); },\n };\n}\n\nfunction createAsyncStorageAdapterFrom(mod: any): QueueStorage {\n const storage = mod.default ?? mod;\n return {\n getItem: (key) => storage.getItem(key),\n setItem: (key, value) => storage.setItem(key, value),\n removeItem: (key) => storage.removeItem(key),\n };\n}\n\n/**\n * Auto-detect the best available storage for queue persistence.\n * Priority: react-native-mmkv (fastest, sync) > AsyncStorage > null.\n * Returns `null` if neither is installed — no hard dependencies.\n *\n * Usage:\n * ```ts\n * import { createQueueStorage } from '@pedi/chika-sdk';\n *\n * const config: ChatConfig = {\n * resilience: {\n * queueStorage: createQueueStorage() ?? undefined,\n * },\n * };\n * ```\n */\nexport function createQueueStorage(): QueueStorage | null {\n if (resolvedStorage !== undefined) return resolvedStorage?.adapter ?? null;\n\n // Priority 1: MMKV (synchronous, fastest)\n const mmkv = tryRequire('react-native-mmkv');\n if (mmkv) {\n try {\n const adapter = createMmkvAdapter(mmkv);\n resolvedStorage = { type: 'mmkv', adapter };\n return adapter;\n } catch {\n // MMKV instantiation can fail if native module isn't linked\n }\n }\n\n // Priority 2: AsyncStorage\n const asyncStorage = tryRequire('@react-native-async-storage/async-storage');\n if (asyncStorage) {\n const adapter = createAsyncStorageAdapterFrom(asyncStorage);\n resolvedStorage = { type: 'async-storage', adapter };\n return adapter;\n }\n\n resolvedStorage = null;\n return null;\n}\n\n/**\n * Creates a QueueStorage adapter backed by `@react-native-async-storage/async-storage`.\n * Returns `null` if the package is not installed.\n */\nexport function createAsyncStorageAdapter(): QueueStorage | null {\n const mod = tryRequire('@react-native-async-storage/async-storage');\n if (!mod) return null;\n return createAsyncStorageAdapterFrom(mod);\n}\n\nexport type MessageSendStatus = 'sending' | 'queued' | 'failed';\n\nexport interface QueuedMessage {\n optimisticId: string;\n status: MessageSendStatus;\n error?: Error;\n retryCount: number;\n}\n\ntype SendFn = () => Promise<SendMessageResponse>;\n\ninterface QueueEntry {\n optimisticId: string;\n sendFn: SendFn;\n status: MessageSendStatus;\n error?: Error;\n retryCount: number;\n abort: AbortController;\n resolve: (value: SendMessageResponse) => void;\n reject: (reason: Error) => void;\n}\n\n/** Serializable subset persisted to storage. */\ninterface PersistedEntry {\n optimisticId: string;\n retryCount: number;\n}\n\nexport interface MessageQueueConfig {\n channelId: string;\n maxSize: number;\n retryConfig: RetryConfig;\n networkMonitor: NetworkMonitor;\n storage?: QueueStorage;\n onError?: (error: Error) => void;\n onStatusChange?: () => void;\n}\n\nexport class MessageQueue {\n private entries: QueueEntry[] = [];\n private flushing = false;\n private unsubNetwork: (() => void) | null = null;\n private readonly storageKey: string;\n\n constructor(private readonly config: MessageQueueConfig) {\n this.storageKey = `chika_queue_${config.channelId}`;\n\n this.unsubNetwork = config.networkMonitor.subscribe((connected) => {\n if (connected) this.flush();\n });\n }\n\n /**\n * Restore queued messages from persistent storage on cold start.\n * Restored entries are fire-and-forget — there is no caller awaiting their\n * promise (the original enqueue() caller is gone after app restart).\n * Success/failure is reported via onStatusChange/onError callbacks.\n */\n async restore(\n rebuildSendFn: (optimisticId: string) => SendFn | null,\n ): Promise<void> {\n if (!this.config.storage) return;\n\n try {\n const raw = await this.config.storage.getItem(this.storageKey);\n if (!raw) return;\n\n const persisted: PersistedEntry[] = JSON.parse(raw);\n for (const entry of persisted) {\n const sendFn = rebuildSendFn(entry.optimisticId);\n if (!sendFn) continue;\n\n const abort = new AbortController();\n this.entries.push({\n optimisticId: entry.optimisticId,\n sendFn,\n status: 'queued',\n retryCount: entry.retryCount,\n abort,\n // No-op: restored entries have no caller awaiting the promise.\n resolve: () => {},\n reject: () => {},\n });\n }\n\n if (this.entries.length > 0) {\n this.config.onStatusChange?.();\n this.flush();\n }\n } catch {\n this.config.onError?.(new Error('Failed to restore message queue from storage'));\n }\n }\n\n get pendingCount(): number {\n return this.entries.length;\n }\n\n getAll(): QueuedMessage[] {\n return this.entries.map((e) => ({\n optimisticId: e.optimisticId,\n status: e.status,\n error: e.error,\n retryCount: e.retryCount,\n }));\n }\n\n getStatus(optimisticId: string): QueuedMessage | undefined {\n const entry = this.entries.find((e) => e.optimisticId === optimisticId);\n if (!entry) return undefined;\n return {\n optimisticId: entry.optimisticId,\n status: entry.status,\n error: entry.error,\n retryCount: entry.retryCount,\n };\n }\n\n enqueue(sendFn: SendFn, optimisticId: string): Promise<SendMessageResponse> {\n if (this.entries.length >= this.config.maxSize) {\n throw new QueueFullError(this.config.maxSize);\n }\n\n const abort = new AbortController();\n\n return new Promise<SendMessageResponse>((resolve, reject) => {\n const entry: QueueEntry = {\n optimisticId,\n sendFn,\n status: 'queued',\n retryCount: 0,\n abort,\n resolve,\n reject,\n };\n\n this.entries.push(entry);\n this.config.onStatusChange?.();\n this.persist();\n this.flush();\n });\n }\n\n cancel(optimisticId: string): void {\n const idx = this.entries.findIndex((e) => e.optimisticId === optimisticId);\n if (idx === -1) return;\n\n const entry = this.entries[idx]!;\n entry.abort.abort();\n entry.reject(new DOMException('Cancelled', 'AbortError'));\n this.entries.splice(idx, 1);\n this.config.onStatusChange?.();\n this.persist();\n }\n\n retry(optimisticId: string): void {\n const entry = this.entries.find((e) => e.optimisticId === optimisticId);\n if (!entry || entry.status !== 'failed') return;\n\n entry.status = 'queued';\n entry.error = undefined;\n entry.abort = new AbortController();\n this.config.onStatusChange?.();\n this.flush();\n }\n\n dispose(): void {\n this.unsubNetwork?.();\n this.unsubNetwork = null;\n\n for (const entry of this.entries) {\n entry.abort.abort();\n entry.reject(new DOMException('Queue disposed', 'AbortError'));\n }\n this.entries = [];\n }\n\n /**\n * Trigger a flush of queued messages. Returns immediately if a flush is\n * already in progress or the network is offline. The in-progress flush\n * will pick up any newly queued entries via its while loop.\n */\n async flush(): Promise<void> {\n if (this.flushing) return;\n if (!this.config.networkMonitor.isConnected()) return;\n\n this.flushing = true;\n let awaitingSession = false;\n\n try {\n while (this.entries.length > 0) {\n // Only pick up 'queued' entries — 'sending' means a previous flush is\n // mid-request. Server-side idempotency key prevents duplicates if the\n // prior request actually succeeded but we never got the response.\n const entry = this.entries.find((e) => e.status === 'queued');\n if (!entry) break;\n if (!this.config.networkMonitor.isConnected()) break;\n\n entry.status = 'sending';\n this.config.onStatusChange?.();\n\n try {\n const result = await withRetry(\n entry.sendFn,\n this.config.retryConfig,\n entry.abort.signal,\n );\n\n entry.resolve(result);\n const idx = this.entries.indexOf(entry);\n if (idx !== -1) this.entries.splice(idx, 1);\n this.config.onStatusChange?.();\n this.persist();\n } catch (err) {\n if (entry.abort.signal.aborted) continue; // cancelled, already removed\n\n // Session not yet reconnected — revert to 'queued' and stop flushing.\n // flush() will be called again when the session is re-established\n // via the explicit flush() call in use-chat.ts startSession().\n if (err instanceof ChatDisconnectedError) {\n entry.status = 'queued';\n this.config.onStatusChange?.();\n awaitingSession = true;\n break;\n }\n\n entry.status = 'failed';\n entry.error = err instanceof Error ? err : new Error(String(err));\n entry.retryCount++;\n entry.reject(entry.error);\n this.config.onStatusChange?.();\n this.persist();\n\n // Don't block the queue on a failed entry — skip to next\n }\n }\n } finally {\n this.flushing = false;\n // Re-check: entries may have been added while we were flushing.\n // Skip if we're waiting for session — startSession() will call flush().\n if (\n !awaitingSession &&\n this.entries.some((e) => e.status === 'queued') &&\n this.config.networkMonitor.isConnected()\n ) {\n queueMicrotask(() => this.flush());\n }\n }\n }\n\n private persist(): void {\n if (!this.config.storage) return;\n\n const data: PersistedEntry[] = this.entries.map((e) => ({\n optimisticId: e.optimisticId,\n retryCount: e.retryCount,\n }));\n\n const onErr = (err: unknown) => {\n this.config.onError?.(\n err instanceof Error ? err : new Error('Queue storage write failed'),\n );\n };\n\n if (data.length === 0) {\n this.config.storage.removeItem(this.storageKey).catch(onErr);\n } else {\n this.config.storage\n .setItem(this.storageKey, JSON.stringify(data))\n .catch(onErr);\n }\n }\n}\n","import { useEffect, useRef, useState, useCallback } from 'react';\nimport { AppState, Platform, type AppStateStatus } from 'react-native';\nimport type { UnreadCountResponse, SSEUnreadUpdateEvent } from '@pedi/chika-types';\nimport type { ChatConfig } from './types';\nimport { resolveServerUrl } from './resolve-url';\nimport { createSSEConnection, type SSEConnection } from './sse-connection';\nimport { createNetworkMonitor, type NetworkMonitor } from './network-monitor';\n\nconst DEFAULT_BACKGROUND_GRACE_MS = 2000;\nconst UNREAD_CUSTOM_EVENTS = ['unread_snapshot', 'unread_update', 'unread_clear'];\n\nexport interface UseUnreadOptions {\n config: ChatConfig;\n channelId: string;\n participantId: string;\n enabled?: boolean;\n}\n\nexport interface UseUnreadReturn {\n unreadCount: number;\n hasUnread: boolean;\n lastMessageAt: string | null;\n error: Error | null;\n}\n\nexport function useUnread(options: UseUnreadOptions): UseUnreadReturn {\n const { config, channelId, participantId, enabled = true } = options;\n\n const [unreadCount, setUnreadCount] = useState(0);\n const [lastMessageAt, setLastMessageAt] = useState<string | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n const connRef = useRef<SSEConnection | null>(null);\n const configRef = useRef(config);\n configRef.current = config;\n const appStateRef = useRef<AppStateStatus>(AppState.currentState);\n const backgroundTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const backgroundGraceMs =\n config.backgroundGraceMs ?? (Platform.OS === 'android' ? DEFAULT_BACKGROUND_GRACE_MS : 0);\n\n const [monitorReady, setMonitorReady] = useState(false);\n const monitorRef = useRef<NetworkMonitor | null>(null);\n const resilienceEnabled = config.resilience !== false;\n const injectedMonitor =\n typeof config.resilience === 'object' ? config.resilience.networkMonitor : undefined;\n\n useEffect(() => {\n if (!resilienceEnabled) {\n monitorRef.current = null;\n setMonitorReady(true);\n return;\n }\n if (injectedMonitor) {\n monitorRef.current = injectedMonitor;\n setMonitorReady(true);\n return;\n }\n try {\n const m = createNetworkMonitor();\n monitorRef.current = m;\n setMonitorReady(true);\n return () => {\n m.dispose();\n monitorRef.current = null;\n };\n } catch {\n monitorRef.current = null;\n setMonitorReady(true);\n }\n }, [resilienceEnabled, injectedMonitor]);\n\n const connect = useCallback(() => {\n connRef.current?.close();\n connRef.current = null;\n\n const serviceUrl = resolveServerUrl(configRef.current.manifest, channelId);\n const customHeaders = configRef.current.headers ?? {};\n const url = `${serviceUrl}/channels/${channelId}/unread?participant_id=${encodeURIComponent(participantId)}`;\n\n connRef.current = createSSEConnection(\n {\n url,\n headers: customHeaders,\n reconnectDelayMs: configRef.current.reconnectDelayMs,\n customEvents: UNREAD_CUSTOM_EVENTS,\n networkMonitor: monitorRef.current ?? undefined,\n },\n {\n onOpen: () => {\n setError(null);\n },\n onEvent: (eventType, data) => {\n try {\n if (eventType === 'unread_snapshot') {\n const snapshot: UnreadCountResponse = JSON.parse(data);\n setUnreadCount(snapshot.unread_count);\n setLastMessageAt(snapshot.last_message_at);\n } else if (eventType === 'unread_update') {\n const update: SSEUnreadUpdateEvent['data'] = JSON.parse(data);\n setUnreadCount((prev) => prev + 1);\n setLastMessageAt(update.created_at);\n } else if (eventType === 'unread_clear') {\n const clear: { channel_id: string; unread_count: number } = JSON.parse(data);\n setUnreadCount(clear.unread_count);\n }\n } catch {\n setError(new Error('Failed to parse unread SSE event'));\n }\n },\n onError: (err) => {\n setError(err);\n },\n onClosed: () => {\n connRef.current = null;\n },\n },\n );\n }, [channelId, participantId]);\n\n const disconnect = useCallback(() => {\n connRef.current?.close();\n connRef.current = null;\n }, []);\n\n useEffect(() => {\n setUnreadCount(0);\n setLastMessageAt(null);\n setError(null);\n\n if (!enabled) {\n disconnect();\n return;\n }\n\n // Don't connect until monitor initialization is complete\n if (!monitorReady) return;\n\n connect();\n\n return () => {\n disconnect();\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n }\n };\n }, [channelId, participantId, enabled, monitorReady, connect, disconnect]);\n\n useEffect(() => {\n if (!enabled) return;\n\n const subscription = AppState.addEventListener('change', (nextAppState) => {\n const prev = appStateRef.current;\n appStateRef.current = nextAppState;\n\n if (!connRef.current && nextAppState !== 'active') return;\n\n const shouldTeardown =\n nextAppState === 'background' ||\n (Platform.OS === 'ios' && nextAppState === 'inactive');\n\n if (nextAppState === 'active') {\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n return;\n }\n\n if (prev.match(/inactive|background/) && !connRef.current) {\n connect();\n }\n } else if (shouldTeardown) {\n if (backgroundTimerRef.current) return;\n\n if (backgroundGraceMs === 0) {\n disconnect();\n } else {\n backgroundTimerRef.current = setTimeout(() => {\n backgroundTimerRef.current = null;\n disconnect();\n }, backgroundGraceMs);\n }\n }\n });\n\n return () => {\n subscription.remove();\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n }\n };\n }, [enabled, backgroundGraceMs, connect, disconnect]);\n\n return { unreadCount, hasUnread: unreadCount > 0, lastMessageAt, error };\n}\n"],"mappings":"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","names":["EventSource","DEFAULT_RECONNECT_DELAY_MS","import_react","import_react_native","DEFAULT_BACKGROUND_GRACE_MS"]}
package/dist/index.mjs CHANGED
@@ -399,8 +399,7 @@ async function createChatSession(config, channelId, profile, callbacks, networkM
399
399
  body: JSON.stringify({
400
400
  participant_id: profile.id,
401
401
  message_id: messageId
402
- }),
403
- signal: sessionAbort.signal
402
+ })
404
403
  });
405
404
  if (!res.ok) {
406
405
  await throwHttpError(res);
@@ -408,7 +407,7 @@ async function createChatSession(config, channelId, profile, callbacks, networkM
408
407
  };
409
408
  if (retryConfig) {
410
409
  try {
411
- await withRetry(readFn, MARK_READ_RETRY_CONFIG, sessionAbort.signal);
410
+ await withRetry(readFn, MARK_READ_RETRY_CONFIG);
412
411
  } catch (err) {
413
412
  if (err instanceof HttpError) {
414
413
  callbacks.onError(err);
@@ -724,6 +723,7 @@ var MessageQueue = class {
724
723
  entry.status = "failed";
725
724
  entry.error = err instanceof Error ? err : new Error(String(err));
726
725
  entry.retryCount++;
726
+ entry.reject(entry.error);
727
727
  this.config.onStatusChange?.();
728
728
  this.persist();
729
729
  }
@@ -781,6 +781,7 @@ function useChat({ config, channelId, profile, onMessage }) {
781
781
  const startingRef = useRef(false);
782
782
  const pendingOptimisticIds = useRef(/* @__PURE__ */ new Set());
783
783
  const [monitor, setMonitor] = useState(null);
784
+ const [monitorReady, setMonitorReady] = useState(false);
784
785
  const monitorRef = useRef(null);
785
786
  monitorRef.current = monitor;
786
787
  const queueRef = useRef(null);
@@ -793,18 +794,26 @@ function useChat({ config, channelId, profile, onMessage }) {
793
794
  useEffect(() => {
794
795
  if (!resilienceEnabled) {
795
796
  setMonitor(null);
797
+ setMonitorReady(true);
796
798
  return;
797
799
  }
798
800
  if (injectedMonitor) {
799
801
  setMonitor(injectedMonitor);
802
+ setMonitorReady(true);
800
803
  return;
801
804
  }
802
- const m = createNetworkMonitor();
803
- setMonitor(m);
804
- return () => {
805
- m.dispose();
805
+ try {
806
+ const m = createNetworkMonitor();
807
+ setMonitor(m);
808
+ setMonitorReady(true);
809
+ return () => {
810
+ m.dispose();
811
+ setMonitor(null);
812
+ };
813
+ } catch {
806
814
  setMonitor(null);
807
- };
815
+ setMonitorReady(true);
816
+ }
808
817
  }, [resilienceEnabled, injectedMonitor]);
809
818
  const callbacks = {
810
819
  onMessage: (message) => {
@@ -897,7 +906,7 @@ function useChat({ config, channelId, profile, onMessage }) {
897
906
  }
898
907
  useEffect(() => {
899
908
  disposedRef.current = false;
900
- if (resilienceEnabled && !monitor) return;
909
+ if (!monitorReady) return;
901
910
  startSession();
902
911
  return () => {
903
912
  disposedRef.current = true;
@@ -915,7 +924,7 @@ function useChat({ config, channelId, profile, onMessage }) {
915
924
  sessionRef.current?.disconnect();
916
925
  sessionRef.current = null;
917
926
  };
918
- }, [channelId, monitor]);
927
+ }, [channelId, monitorReady]);
919
928
  useEffect(() => {
920
929
  if (!queueEnabled || !monitor || !retryConfig) return;
921
930
  let entry = queueRegistry.get(channelId);
@@ -1124,24 +1133,33 @@ function useUnread(options) {
1124
1133
  const appStateRef = useRef2(AppState2.currentState);
1125
1134
  const backgroundTimerRef = useRef2(null);
1126
1135
  const backgroundGraceMs = config.backgroundGraceMs ?? (Platform2.OS === "android" ? DEFAULT_BACKGROUND_GRACE_MS2 : 0);
1127
- const [monitor, setMonitor] = useState2(null);
1136
+ const [monitorReady, setMonitorReady] = useState2(false);
1137
+ const monitorRef = useRef2(null);
1128
1138
  const resilienceEnabled = config.resilience !== false;
1129
1139
  const injectedMonitor = typeof config.resilience === "object" ? config.resilience.networkMonitor : void 0;
1130
1140
  useEffect2(() => {
1131
1141
  if (!resilienceEnabled) {
1132
- setMonitor(null);
1142
+ monitorRef.current = null;
1143
+ setMonitorReady(true);
1133
1144
  return;
1134
1145
  }
1135
1146
  if (injectedMonitor) {
1136
- setMonitor(injectedMonitor);
1147
+ monitorRef.current = injectedMonitor;
1148
+ setMonitorReady(true);
1137
1149
  return;
1138
1150
  }
1139
- const m = createNetworkMonitor();
1140
- setMonitor(m);
1141
- return () => {
1142
- m.dispose();
1143
- setMonitor(null);
1144
- };
1151
+ try {
1152
+ const m = createNetworkMonitor();
1153
+ monitorRef.current = m;
1154
+ setMonitorReady(true);
1155
+ return () => {
1156
+ m.dispose();
1157
+ monitorRef.current = null;
1158
+ };
1159
+ } catch {
1160
+ monitorRef.current = null;
1161
+ setMonitorReady(true);
1162
+ }
1145
1163
  }, [resilienceEnabled, injectedMonitor]);
1146
1164
  const connect = useCallback2(() => {
1147
1165
  connRef.current?.close();
@@ -1155,7 +1173,7 @@ function useUnread(options) {
1155
1173
  headers: customHeaders,
1156
1174
  reconnectDelayMs: configRef.current.reconnectDelayMs,
1157
1175
  customEvents: UNREAD_CUSTOM_EVENTS,
1158
- networkMonitor: monitor ?? void 0
1176
+ networkMonitor: monitorRef.current ?? void 0
1159
1177
  },
1160
1178
  {
1161
1179
  onOpen: () => {
@@ -1187,7 +1205,7 @@ function useUnread(options) {
1187
1205
  }
1188
1206
  }
1189
1207
  );
1190
- }, [channelId, participantId, monitor]);
1208
+ }, [channelId, participantId]);
1191
1209
  const disconnect = useCallback2(() => {
1192
1210
  connRef.current?.close();
1193
1211
  connRef.current = null;
@@ -1200,7 +1218,7 @@ function useUnread(options) {
1200
1218
  disconnect();
1201
1219
  return;
1202
1220
  }
1203
- if (resilienceEnabled && !monitor) return;
1221
+ if (!monitorReady) return;
1204
1222
  connect();
1205
1223
  return () => {
1206
1224
  disconnect();
@@ -1209,7 +1227,7 @@ function useUnread(options) {
1209
1227
  backgroundTimerRef.current = null;
1210
1228
  }
1211
1229
  };
1212
- }, [channelId, participantId, enabled, connect, disconnect]);
1230
+ }, [channelId, participantId, enabled, monitorReady, connect, disconnect]);
1213
1231
  useEffect2(() => {
1214
1232
  if (!enabled) return;
1215
1233
  const subscription = AppState2.addEventListener("change", (nextAppState) => {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/use-chat.ts","../src/errors.ts","../src/retry.ts","../src/resolve-url.ts","../src/sse-connection.ts","../src/session.ts","../src/network-monitor.ts","../src/message-queue.ts","../src/use-unread.ts"],"sourcesContent":["import { useEffect, useRef, useState, useCallback } from 'react';\nimport { AppState, Platform, type AppStateStatus } from 'react-native';\nimport type {\n ChatDomain,\n DefaultDomain,\n Message,\n Participant,\n MessageAttributes,\n SendMessageResponse,\n} from '@pedi/chika-types';\nimport type { UseChatOptions, UseChatReturn, ChatStatus } from './types';\nimport { ChatDisconnectedError, ChannelClosedError, QueueFullError, RetryExhaustedError } from './errors';\nimport { isRetryableError, resolveRetryConfig } from './retry';\nimport { createChatSession, type ChatSession, type SessionCallbacks } from './session';\nimport { createNetworkMonitor, type NetworkMonitor } from './network-monitor';\nimport { MessageQueue, type QueuedMessage } from './message-queue';\n\nconst DEFAULT_BACKGROUND_GRACE_MS = 2000;\nconst DEFAULT_MAX_QUEUE_SIZE = 50;\n\n// Module-scope queue registry, keyed by channelId. Survives component remounts.\nconst queueRegistry = new Map<string, { queue: MessageQueue; refCount: number }>();\n\n/**\n * React hook for real-time chat over SSE.\n * Manages connection lifecycle, AppState transitions, message deduplication, reconnection,\n * and optional network resilience (retry, offline queue, network monitoring).\n *\n * @template D - Chat domain type for role/message type narrowing. Defaults to DefaultDomain.\n */\nexport function useChat<D extends ChatDomain = DefaultDomain>(\n { config, channelId, profile, onMessage }: UseChatOptions<D>,\n): UseChatReturn<D> {\n const [messages, setMessages] = useState<Message<D>[]>([]);\n const [participants, setParticipants] = useState<Participant<D>[]>([]);\n const [status, setStatus] = useState<ChatStatus>('connecting');\n const [error, setError] = useState<Error | null>(null);\n const [pendingMessages, setPendingMessages] = useState<QueuedMessage[]>([]);\n\n const sessionRef = useRef<ChatSession<D> | null>(null);\n const disposedRef = useRef(false);\n const messagesRef = useRef(messages);\n messagesRef.current = messages;\n const statusRef = useRef(status);\n statusRef.current = status;\n const appStateRef = useRef<AppStateStatus>(AppState.currentState);\n const backgroundTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const profileRef = useRef(profile);\n profileRef.current = profile;\n const configRef = useRef(config);\n configRef.current = config;\n const onMessageRef = useRef(onMessage);\n onMessageRef.current = onMessage;\n const startingRef = useRef(false);\n const pendingOptimisticIds = useRef(new Set<string>());\n const [monitor, setMonitor] = useState<NetworkMonitor | null>(null);\n const monitorRef = useRef<NetworkMonitor | null>(null);\n monitorRef.current = monitor;\n const queueRef = useRef<MessageQueue | null>(null);\n\n const resilienceEnabled = config.resilience !== false;\n const queueEnabled =\n resilienceEnabled &&\n (typeof config.resilience === 'object' ? config.resilience.offlineQueue !== false : true);\n const retryConfig = resolveRetryConfig(config.resilience);\n const maxQueueSize =\n (resilienceEnabled && config.resilience && typeof config.resilience === 'object'\n ? config.resilience.maxQueueSize\n : undefined) ?? DEFAULT_MAX_QUEUE_SIZE;\n\n const backgroundGraceMs =\n config.backgroundGraceMs ?? (Platform.OS === 'android' ? DEFAULT_BACKGROUND_GRACE_MS : 0);\n\n // Resolve user-injected monitor (stable reference, no side effect)\n const injectedMonitor =\n typeof config.resilience === 'object' ? config.resilience.networkMonitor : undefined;\n\n // Create monitor in useEffect to avoid side effects during render.\n // Uses state (not ref) so the queue effect re-runs when monitor is ready.\n useEffect(() => {\n if (!resilienceEnabled) {\n setMonitor(null);\n return;\n }\n if (injectedMonitor) {\n setMonitor(injectedMonitor);\n return; // user owns lifecycle\n }\n const m = createNetworkMonitor();\n setMonitor(m);\n return () => {\n m.dispose();\n setMonitor(null);\n };\n }, [resilienceEnabled, injectedMonitor]);\n\n const callbacks: SessionCallbacks<D> = {\n onMessage: (message) => {\n if (disposedRef.current) return;\n let matchedOptimisticId: string | null = null;\n setMessages((prev: Message<D>[]) => {\n if (pendingOptimisticIds.current.size === 0) {\n return [...prev, message];\n }\n\n const optimisticIdx = prev.findIndex(\n (m) =>\n pendingOptimisticIds.current.has(m.id) &&\n m.sender_id === message.sender_id &&\n m.body === message.body &&\n m.type === message.type,\n );\n if (optimisticIdx !== -1) {\n matchedOptimisticId = prev[optimisticIdx]!.id;\n pendingOptimisticIds.current.delete(matchedOptimisticId);\n const next = [...prev];\n next[optimisticIdx] = message;\n return next;\n }\n return [...prev, message];\n });\n // Clean up any queued/failed queue entry — SSE confirmed delivery.\n // Done outside setMessages updater to avoid setState-in-setState.\n if (matchedOptimisticId) {\n const s = queueRef.current?.getStatus(matchedOptimisticId);\n if (s && s.status !== 'sending') {\n queueRef.current?.cancel(matchedOptimisticId);\n }\n }\n onMessageRef.current?.(message);\n },\n onStatusChange: (nextStatus) => {\n if (disposedRef.current) return;\n setStatus(nextStatus);\n if (nextStatus === 'connected') {\n setError(null);\n }\n },\n onError: (err) => {\n if (disposedRef.current) return;\n setError(err);\n },\n onResync: () => {\n if (disposedRef.current) return;\n startSession();\n },\n };\n\n async function startSession(): Promise<void> {\n if (startingRef.current) return;\n startingRef.current = true;\n\n const existing = sessionRef.current;\n if (existing) {\n existing.disconnect();\n sessionRef.current = null;\n }\n\n try {\n const session = await createChatSession<D>(\n configRef.current,\n channelId,\n profileRef.current,\n callbacks,\n monitorRef.current ?? undefined,\n );\n\n if (disposedRef.current) {\n session.disconnect();\n return;\n }\n\n sessionRef.current = session;\n setParticipants(session.initialParticipants);\n\n // Flush any messages queued while session was unavailable\n queueRef.current?.flush();\n\n // Re-merge any pending optimistic messages after resync\n if (pendingOptimisticIds.current.size > 0) {\n const pendingIds = pendingOptimisticIds.current;\n setMessages((prev) => {\n const pendingMsgs = prev.filter((m) => pendingIds.has(m.id));\n return [...session.initialMessages, ...pendingMsgs];\n });\n } else {\n setMessages(session.initialMessages);\n }\n } catch (err) {\n if (disposedRef.current) return;\n\n if (err instanceof ChannelClosedError) {\n setStatus('closed');\n setError(err);\n return;\n }\n\n setStatus('error');\n setError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n startingRef.current = false;\n }\n }\n\n // Session lifecycle\n useEffect(() => {\n disposedRef.current = false;\n // Don't start until monitor is resolved (or resilience is off)\n if (resilienceEnabled && !monitor) return;\n startSession();\n\n return () => {\n disposedRef.current = true;\n if (statusRef.current === 'connected' && sessionRef.current) {\n const lastMsg = messagesRef.current[messagesRef.current.length - 1];\n if (lastMsg) {\n sessionRef.current.markAsRead(lastMsg.id).catch(() => {});\n }\n }\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n }\n sessionRef.current?.disconnect();\n sessionRef.current = null;\n };\n }, [channelId, monitor]);\n\n // Module-scope queue lifecycle (ref-counted, survives remounts)\n useEffect(() => {\n if (!queueEnabled || !monitor || !retryConfig) return;\n\n let entry = queueRegistry.get(channelId);\n if (!entry) {\n const queue = new MessageQueue({\n channelId,\n maxSize: maxQueueSize,\n retryConfig,\n networkMonitor: monitor,\n storage:\n typeof config.resilience === 'object'\n ? config.resilience.queueStorage\n : undefined,\n onError: (err) => {\n if (!disposedRef.current) setError(err);\n },\n onStatusChange: () => {\n if (!disposedRef.current) {\n setPendingMessages(queueRef.current?.getAll() ?? []);\n }\n },\n });\n entry = { queue, refCount: 0 };\n queueRegistry.set(channelId, entry);\n }\n entry.refCount++;\n queueRef.current = entry.queue;\n\n return () => {\n const e = queueRegistry.get(channelId);\n if (e) {\n e.refCount--;\n if (e.refCount <= 0) {\n e.queue.dispose();\n queueRegistry.delete(channelId);\n }\n }\n queueRef.current = null;\n };\n }, [channelId, queueEnabled, monitor]);\n\n // Network monitor: auto-rejoin on connectivity return when in error state\n useEffect(() => {\n if (!monitor || !resilienceEnabled) return;\n\n const unsub = monitor.subscribe((connected: boolean) => {\n if (connected && statusRef.current === 'error' && !startingRef.current) {\n startSession();\n }\n });\n\n return unsub;\n }, [channelId, resilienceEnabled, monitor]);\n\n // AppState lifecycle\n useEffect(() => {\n function teardownSession(): void {\n sessionRef.current?.disconnect();\n sessionRef.current = null;\n setStatus('disconnected');\n }\n\n const subscription = AppState.addEventListener('change', (nextAppState) => {\n const prev = appStateRef.current;\n appStateRef.current = nextAppState;\n\n if (!sessionRef.current && nextAppState !== 'active') return;\n\n const shouldTeardown =\n nextAppState === 'background' ||\n (Platform.OS === 'ios' && nextAppState === 'inactive');\n\n if (nextAppState === 'active') {\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n return;\n }\n\n if (prev.match(/inactive|background/) && !sessionRef.current) {\n startSession();\n }\n } else if (shouldTeardown) {\n if (backgroundTimerRef.current) return;\n\n if (backgroundGraceMs === 0) {\n teardownSession();\n } else {\n backgroundTimerRef.current = setTimeout(() => {\n backgroundTimerRef.current = null;\n teardownSession();\n }, backgroundGraceMs);\n }\n }\n });\n\n return () => {\n subscription.remove();\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n }\n };\n }, [channelId, backgroundGraceMs]);\n\n const sendMessage = useCallback(\n async (type: D['messageType'], body: string, attributes?: MessageAttributes<D>): Promise<SendMessageResponse> => {\n const session = sessionRef.current;\n if (!session) throw new ChatDisconnectedError(statusRef.current);\n\n const optimistic = configRef.current.optimisticSend !== false;\n // Serves as both the optimistic message ID and the server-side idempotency key\n const messageKey = `optimistic_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\n\n if (optimistic) {\n pendingOptimisticIds.current.add(messageKey);\n const provisionalMsg: Message<D> = {\n id: messageKey,\n channel_id: channelId,\n sender_id: profileRef.current.id,\n sender_role: profileRef.current.role,\n type,\n body,\n attributes: (attributes ?? {}) as MessageAttributes<D>,\n created_at: new Date().toISOString(),\n };\n setMessages((prev) => [...prev, provisionalMsg]);\n }\n\n const doSend = () => {\n const s = sessionRef.current;\n if (!s) throw new ChatDisconnectedError(statusRef.current);\n return s.sendMessage(type, body, attributes, messageKey);\n };\n\n const handleSuccess = (response: SendMessageResponse): void => {\n if (optimistic) {\n pendingOptimisticIds.current.delete(messageKey);\n setMessages((prev) => {\n const stillPending = prev.some((m) => m.id === messageKey);\n if (!stillPending) return prev;\n return prev.map((m) =>\n m.id === messageKey\n ? { ...m, id: response.id, created_at: response.created_at }\n : m,\n );\n });\n }\n };\n\n const handleError = (_err: unknown): void => {\n if (optimistic) {\n pendingOptimisticIds.current.delete(messageKey);\n setMessages((prev) => prev.filter((m) => m.id !== messageKey));\n }\n };\n\n // Queue path: enqueue and let queue handle retry + offline\n if (queueRef.current) {\n try {\n const response = await queueRef.current.enqueue(doSend, messageKey);\n handleSuccess(response);\n return response;\n } catch (err) {\n if (err instanceof QueueFullError) {\n handleError(err);\n throw err;\n }\n if (err instanceof RetryExhaustedError || !isRetryableError(err)) {\n // For failed messages: if optimistic, mark as failed (keep in UI)\n // The pendingMessages state shows the status\n if (optimistic && err instanceof RetryExhaustedError) {\n // Keep optimistic message visible — queue tracks status as 'failed'.\n // Keep messageKey in pendingOptimisticIds so SSE reconciliation can\n // still match if the server eventually received the message.\n // Removed on explicit cancelMessage() call.\n } else {\n handleError(err);\n }\n throw err;\n }\n handleError(err);\n throw err;\n }\n }\n\n // Non-queue path: direct send (session.ts handles retry if enabled)\n try {\n const response = await doSend();\n handleSuccess(response);\n return response;\n } catch (err) {\n handleError(err);\n throw err;\n }\n },\n [channelId],\n );\n\n const cancelMessage = useCallback(\n (optimisticId: string) => {\n queueRef.current?.cancel(optimisticId);\n pendingOptimisticIds.current.delete(optimisticId);\n setMessages((prev) => prev.filter((m) => m.id !== optimisticId));\n },\n [],\n );\n\n const retryMessage = useCallback(\n (optimisticId: string) => {\n queueRef.current?.retry(optimisticId);\n },\n [],\n );\n\n const disconnect = useCallback(() => {\n sessionRef.current?.disconnect();\n sessionRef.current = null;\n setStatus('disconnected');\n }, []);\n\n return {\n messages,\n participants,\n status,\n error,\n sendMessage,\n disconnect,\n pendingMessages,\n cancelMessage,\n retryMessage,\n };\n}\n","import type { ChatStatus } from './types';\n\nexport class ChatDisconnectedError extends Error {\n constructor(public readonly status: ChatStatus) {\n super(`Cannot send message while ${status}`);\n this.name = 'ChatDisconnectedError';\n }\n}\n\nexport class ChannelClosedError extends Error {\n constructor(public readonly channelId: string) {\n super(`Channel ${channelId} is closed`);\n this.name = 'ChannelClosedError';\n }\n}\n\nexport class HttpError extends Error {\n constructor(\n public readonly status: number,\n public readonly body: string,\n public readonly retryAfter?: number,\n ) {\n super(`HTTP ${status}: ${body}`);\n this.name = 'HttpError';\n }\n}\n\nexport class RetryExhaustedError extends Error {\n constructor(\n public readonly operation: string,\n public readonly attempts: number,\n public readonly lastError: Error,\n ) {\n super(`${operation} failed after ${attempts} attempts: ${lastError.message}`);\n this.name = 'RetryExhaustedError';\n }\n}\n\nexport class QueueFullError extends Error {\n constructor(public readonly maxSize: number) {\n super(`Message queue full (max ${maxSize})`);\n this.name = 'QueueFullError';\n }\n}\n","import {\n HttpError,\n RetryExhaustedError,\n ChannelClosedError,\n ChatDisconnectedError,\n QueueFullError,\n} from './errors';\n\nexport interface RetryConfig {\n maxAttempts: number;\n baseDelayMs: number;\n maxDelayMs: number;\n jitterFactor: number;\n}\n\nexport const DEFAULT_RETRY_CONFIG: RetryConfig = {\n maxAttempts: 3,\n baseDelayMs: 1000,\n maxDelayMs: 10000,\n jitterFactor: 0.3,\n};\n\nexport function calculateBackoff(attempt: number, config: RetryConfig): number {\n const delay = Math.min(config.baseDelayMs * 2 ** attempt, config.maxDelayMs);\n const jitter = 1 + (Math.random() * 2 - 1) * config.jitterFactor;\n return Math.round(delay * jitter);\n}\n\nexport function isRetryableError(error: unknown): boolean {\n if (error instanceof ChannelClosedError) return false;\n if (error instanceof ChatDisconnectedError) return false;\n if (error instanceof QueueFullError) return false;\n if (error instanceof DOMException && error.name === 'AbortError') return false;\n\n if (error instanceof HttpError) {\n const { status } = error;\n if (status === 408 || status === 429) return true;\n if (status >= 500) return true;\n return false;\n }\n\n // TypeError from fetch = network failure\n if (error instanceof TypeError) return true;\n\n return false;\n}\n\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));\n return;\n }\n\n const timer = setTimeout(resolve, ms);\n\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));\n },\n { once: true },\n );\n });\n}\n\nexport function resolveRetryConfig(\n resilience: { retry?: Partial<RetryConfig> | false } | false | undefined,\n): RetryConfig | null {\n if (resilience === false) return null;\n if (!resilience || resilience.retry === undefined) return DEFAULT_RETRY_CONFIG;\n if (resilience.retry === false) return null;\n return { ...DEFAULT_RETRY_CONFIG, ...resilience.retry };\n}\n\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n config: RetryConfig = DEFAULT_RETRY_CONFIG,\n signal?: AbortSignal,\n): Promise<T> {\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt < config.maxAttempts; attempt++) {\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n\n try {\n return await fn();\n } catch (err) {\n lastError = err instanceof Error ? err : new Error(String(err));\n\n if (!isRetryableError(err)) throw lastError;\n\n if (attempt < config.maxAttempts - 1) {\n // Respect Retry-After header for 429 responses\n const delayMs =\n err instanceof HttpError && err.retryAfter != null\n ? err.retryAfter * 1000\n : calculateBackoff(attempt, config);\n\n await sleep(delayMs, signal);\n }\n }\n }\n\n throw new RetryExhaustedError(\n 'operation',\n config.maxAttempts,\n lastError!,\n );\n}\n","import type { ChatManifest } from '@pedi/chika-types';\n\n/**\n * Creates a single-server manifest. Use this when all channels route to the same server.\n *\n * @example\n * ```ts\n * const config: ChatConfig = { manifest: createManifest('https://chat.example.com') };\n * ```\n */\nexport function createManifest(serverUrl: string): ChatManifest {\n return { buckets: [{ group: 'default', range: [0, 99], server_url: serverUrl }] };\n}\n\nexport function resolveServerUrl(manifest: ChatManifest, channelId: string): string {\n const hash = [...channelId].reduce((sum, c) => sum + c.charCodeAt(0), 0) % 100;\n const bucket = manifest.buckets.find(\n (b) => hash >= b.range[0] && hash <= b.range[1],\n );\n if (!bucket) throw new Error(`No chat bucket for hash ${hash}`);\n return bucket.server_url;\n}\n","import EventSource from 'react-native-sse';\nimport { calculateBackoff, type RetryConfig } from './retry';\nimport type { NetworkMonitor } from './network-monitor';\n\nconst DEFAULT_RECONNECT_DELAY_MS = 3000;\n\nexport interface SSEConnectionConfig {\n url: string;\n headers?: Record<string, string>;\n reconnectDelayMs?: number;\n lastEventId?: string;\n customEvents?: string[];\n networkMonitor?: NetworkMonitor;\n}\n\nexport interface SSEConnectionCallbacks {\n onOpen?: () => void;\n onEvent: (eventType: string, data: string, lastEventId?: string) => void;\n onError?: (error: Error) => void;\n onClosed?: () => void;\n onReconnecting?: () => void;\n}\n\nexport interface SSEConnection {\n close: () => void;\n reconnectImmediate: () => void;\n}\n\nexport function createSSEConnection(\n config: SSEConnectionConfig,\n callbacks: SSEConnectionCallbacks,\n): SSEConnection {\n const baseDelay = config.reconnectDelayMs ?? DEFAULT_RECONNECT_DELAY_MS;\n const customEvents = config.customEvents ?? [];\n const monitor = config.networkMonitor;\n\n const backoffConfig: RetryConfig = {\n maxAttempts: Infinity,\n baseDelayMs: baseDelay,\n maxDelayMs: 30000,\n jitterFactor: 0.3,\n };\n\n let currentLastEventId = config.lastEventId;\n let es: EventSource<string> | null = null;\n let disposed = false;\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n let attempt = 0;\n let waitAbort: AbortController | null = null;\n\n function cleanup(): void {\n if (es) {\n es.removeAllEventListeners();\n es.close();\n es = null;\n }\n if (reconnectTimer) {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n }\n if (waitAbort) {\n waitAbort.abort();\n waitAbort = null;\n }\n }\n\n async function scheduleReconnect(): Promise<void> {\n if (disposed || reconnectTimer || waitAbort) return;\n callbacks.onReconnecting?.();\n cleanup();\n\n // Wait for network if monitor available\n if (monitor && !monitor.isConnected()) {\n waitAbort = new AbortController();\n try {\n await monitor.waitForOnline(waitAbort.signal);\n } catch {\n return; // aborted via dispose\n }\n waitAbort = null;\n if (disposed) return;\n }\n\n const delay = calculateBackoff(attempt++, backoffConfig);\n reconnectTimer = setTimeout(() => {\n reconnectTimer = null;\n connect();\n }, delay);\n }\n\n function connect(): void {\n if (disposed) return;\n\n es = new EventSource<string>(config.url, {\n headers: {\n ...config.headers,\n ...(currentLastEventId && { 'Last-Event-ID': currentLastEventId }),\n },\n pollingInterval: 0,\n });\n\n es.addEventListener('open', () => {\n if (disposed) return;\n attempt = 0;\n callbacks.onOpen?.();\n });\n\n es.addEventListener('message', (event) => {\n if (disposed || !event.data) return;\n if (event.lastEventId) {\n currentLastEventId = event.lastEventId;\n }\n callbacks.onEvent('message', event.data, event.lastEventId ?? undefined);\n });\n\n for (const eventName of customEvents) {\n es.addEventListener(eventName, (event) => {\n if (disposed) return;\n callbacks.onEvent(eventName, event.data ?? '', undefined);\n });\n }\n\n es.addEventListener('error', (event) => {\n if (disposed) return;\n\n const msg = 'message' in event ? String(event.message) : '';\n\n if (msg.includes('Channel is closed') || msg.includes('410')) {\n callbacks.onClosed?.();\n cleanup();\n disposed = true;\n return;\n }\n\n if (msg) callbacks.onError?.(new Error(msg));\n\n scheduleReconnect();\n });\n\n es.addEventListener('close', () => {\n if (disposed) return;\n scheduleReconnect();\n });\n }\n\n connect();\n\n return {\n close: () => {\n disposed = true;\n cleanup();\n },\n\n reconnectImmediate: () => {\n if (disposed) return;\n cleanup(); // does NOT set disposed\n attempt = 0;\n connect();\n },\n };\n}\n","import type {\n ChatDomain,\n DefaultDomain,\n Participant,\n Message,\n JoinResponse,\n SendMessageRequest,\n SendMessageResponse,\n MessageAttributes,\n} from '@pedi/chika-types';\nimport type { ChatConfig, ChatStatus } from './types';\nimport { ChatDisconnectedError, ChannelClosedError, HttpError } from './errors';\nimport { resolveServerUrl } from './resolve-url';\nimport { createSSEConnection, type SSEConnection } from './sse-connection';\nimport { withRetry, resolveRetryConfig, type RetryConfig } from './retry';\nimport type { NetworkMonitor } from './network-monitor';\n\nconst DEFAULT_RECONNECT_DELAY_MS = 3000;\nconst MAX_SEEN_IDS = 500;\n\nconst MARK_READ_RETRY_CONFIG: RetryConfig = {\n maxAttempts: 2,\n baseDelayMs: 500,\n maxDelayMs: 2000,\n jitterFactor: 0.3,\n};\n\nexport interface SessionCallbacks<D extends ChatDomain = DefaultDomain> {\n onMessage: (message: Message<D>) => void;\n onStatusChange: (status: ChatStatus) => void;\n onError: (error: Error) => void;\n onResync: () => void;\n}\n\nexport interface ChatSession<D extends ChatDomain = DefaultDomain> {\n serviceUrl: string;\n channelId: string;\n initialParticipants: Participant<D>[];\n initialMessages: Message<D>[];\n networkMonitor: NetworkMonitor | null;\n sendMessage: (\n type: D['messageType'],\n body: string,\n attributes?: MessageAttributes<D>,\n idempotencyKey?: string,\n ) => Promise<SendMessageResponse>;\n markAsRead: (messageId: string) => Promise<void>;\n disconnect: () => void;\n}\n\nfunction parseRetryAfter(res: Response): number | undefined {\n const header = res.headers.get('Retry-After');\n if (!header) return undefined;\n const seconds = Number(header);\n return Number.isFinite(seconds) ? seconds : undefined;\n}\n\nasync function throwHttpError(res: Response): Promise<never> {\n const body = await res.text().catch(() => '');\n throw new HttpError(res.status, body, parseRetryAfter(res));\n}\n\nexport async function createChatSession<D extends ChatDomain = DefaultDomain>(\n config: ChatConfig,\n channelId: string,\n profile: Participant<D>,\n callbacks: SessionCallbacks<D>,\n networkMonitor?: NetworkMonitor,\n): Promise<ChatSession<D>> {\n const serviceUrl = resolveServerUrl(config.manifest, channelId);\n const customHeaders = config.headers ?? {};\n const reconnectDelay = config.reconnectDelayMs ?? DEFAULT_RECONNECT_DELAY_MS;\n const retryConfig = resolveRetryConfig(config.resilience);\n\n const sessionAbort = new AbortController();\n\n callbacks.onStatusChange('connecting');\n\n const joinFn = async (): Promise<JoinResponse<D>> => {\n const joinRes = await fetch(`${serviceUrl}/channels/${channelId}/join`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...customHeaders },\n body: JSON.stringify(profile),\n signal: sessionAbort.signal,\n });\n\n if (joinRes.status === 410) {\n throw new ChannelClosedError(channelId);\n }\n\n if (!joinRes.ok) {\n await throwHttpError(joinRes);\n }\n\n return joinRes.json();\n };\n\n const joinData = retryConfig\n ? await withRetry(joinFn, retryConfig, sessionAbort.signal)\n : await joinFn();\n\n const { messages, participants, joined_at }: JoinResponse<D> = joinData;\n\n let lastEventId =\n messages.length > 0 ? messages[messages.length - 1]!.id : undefined;\n\n const joinedAt = joined_at;\n\n const seenMessageIds = new Set<string>(messages.map((m) => m.id));\n\n let sseConn: SSEConnection | null = null;\n let disposed = false;\n\n const TRIM_THRESHOLD = MAX_SEEN_IDS * 1.5;\n\n function trimSeenIds(): void {\n if (seenMessageIds.size <= TRIM_THRESHOLD) return;\n const ids = [...seenMessageIds];\n seenMessageIds.clear();\n for (const id of ids.slice(-MAX_SEEN_IDS)) {\n seenMessageIds.add(id);\n }\n }\n\n function connect(): void {\n if (disposed) return;\n\n const streamUrl = lastEventId\n ? `${serviceUrl}/channels/${channelId}/stream`\n : `${serviceUrl}/channels/${channelId}/stream?since_time=${encodeURIComponent(joinedAt)}`;\n\n sseConn = createSSEConnection(\n {\n url: streamUrl,\n headers: customHeaders,\n reconnectDelayMs: reconnectDelay,\n lastEventId,\n customEvents: ['resync'],\n networkMonitor,\n },\n {\n onOpen: () => {\n if (!disposed) callbacks.onStatusChange('connected');\n },\n onEvent: (eventType, data, eventId) => {\n if (disposed) return;\n\n if (eventType === 'message') {\n let message: Message<D>;\n try {\n message = JSON.parse(data);\n } catch {\n callbacks.onError(new Error('Failed to parse SSE message'));\n return;\n }\n\n if (eventId) {\n lastEventId = eventId;\n }\n\n if (seenMessageIds.has(message.id)) return;\n seenMessageIds.add(message.id);\n trimSeenIds();\n\n callbacks.onMessage(message);\n } else if (eventType === 'resync') {\n // Let onResync → startSession handle full session recreation.\n // Don't call reconnectImmediate() — startSession disconnects this\n // session anyway, so the reconnect would be immediately thrown away.\n callbacks.onResync();\n }\n },\n onError: (err) => {\n if (!disposed) callbacks.onError(err);\n },\n onClosed: () => {\n callbacks.onStatusChange('closed');\n disposed = true;\n },\n onReconnecting: () => {\n if (!disposed) callbacks.onStatusChange('reconnecting');\n },\n },\n );\n }\n\n connect();\n\n return {\n serviceUrl,\n channelId,\n initialParticipants: participants,\n initialMessages: messages,\n networkMonitor: networkMonitor ?? null,\n\n sendMessage: async (type, body, attributes, idempotencyKey) => {\n if (disposed) throw new ChatDisconnectedError('disconnected');\n\n const payload: SendMessageRequest<D> = {\n sender_id: profile.id,\n type,\n body,\n attributes,\n ...(idempotencyKey ? { idempotency_key: idempotencyKey } : {}),\n };\n\n const sendFn = async (): Promise<SendMessageResponse> => {\n const res = await fetch(\n `${serviceUrl}/channels/${channelId}/messages`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...customHeaders },\n body: JSON.stringify(payload),\n signal: sessionAbort.signal,\n },\n );\n\n if (!res.ok) {\n await throwHttpError(res);\n }\n\n return res.json();\n };\n\n const response = retryConfig\n ? await withRetry(sendFn, retryConfig, sessionAbort.signal)\n : await sendFn();\n\n seenMessageIds.add(response.id);\n return response;\n },\n\n markAsRead: async (messageId: string) => {\n const readFn = async (): Promise<void> => {\n const res = await fetch(`${serviceUrl}/channels/${channelId}/read`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...customHeaders },\n body: JSON.stringify({\n participant_id: profile.id,\n message_id: messageId,\n }),\n signal: sessionAbort.signal,\n });\n if (!res.ok) {\n await throwHttpError(res);\n }\n };\n\n if (retryConfig) {\n try {\n await withRetry(readFn, MARK_READ_RETRY_CONFIG, sessionAbort.signal);\n } catch (err) {\n // Surface non-retryable errors (403, 404) for dev diagnostics\n if (err instanceof HttpError) {\n callbacks.onError(err);\n }\n // Swallow RetryExhaustedError — markAsRead is best-effort\n }\n } else {\n await readFn();\n }\n },\n\n disconnect: () => {\n disposed = true;\n sessionAbort.abort();\n sseConn?.close();\n sseConn = null;\n callbacks.onStatusChange('disconnected');\n },\n };\n}\n","export interface NetworkMonitor {\n isConnected(): boolean;\n subscribe(cb: (connected: boolean) => void): () => void;\n waitForOnline(signal?: AbortSignal): Promise<void>;\n dispose(): void;\n}\n\nfunction createStubMonitor(): NetworkMonitor {\n return {\n isConnected: () => true,\n subscribe: () => () => {},\n waitForOnline: () => Promise.resolve(),\n dispose: () => {},\n };\n}\n\nlet resolvedNetInfo: any = undefined;\nlet netInfoResolved = false;\n\nfunction getNetInfo(): any {\n if (netInfoResolved) return resolvedNetInfo;\n netInfoResolved = true;\n try {\n resolvedNetInfo = require('@react-native-community/netinfo');\n } catch {\n resolvedNetInfo = null;\n }\n return resolvedNetInfo;\n}\n\nexport function createNetworkMonitor(): NetworkMonitor {\n const NetInfo = getNetInfo();\n if (!NetInfo) return createStubMonitor();\n\n const netInfoModule = NetInfo.default ?? NetInfo;\n\n let connected = true;\n const listeners = new Set<(connected: boolean) => void>();\n let unsubscribeNetInfo: (() => void) | null = null;\n\n unsubscribeNetInfo = netInfoModule.addEventListener(\n (state: { isConnected: boolean | null }) => {\n const next = state.isConnected !== false;\n if (next === connected) return;\n connected = next;\n for (const cb of listeners) {\n cb(connected);\n }\n },\n );\n\n return {\n isConnected: () => connected,\n\n subscribe: (cb) => {\n listeners.add(cb);\n return () => {\n listeners.delete(cb);\n };\n },\n\n waitForOnline: (signal?) => {\n if (connected) return Promise.resolve();\n\n return new Promise<void>((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));\n return;\n }\n\n const unsub = (): void => {\n listeners.delete(handler);\n signal?.removeEventListener('abort', onAbort);\n };\n\n const handler = (isOnline: boolean): void => {\n if (isOnline) {\n unsub();\n resolve();\n }\n };\n\n const onAbort = (): void => {\n unsub();\n reject(signal!.reason ?? new DOMException('Aborted', 'AbortError'));\n };\n\n listeners.add(handler);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n },\n\n dispose: () => {\n listeners.clear();\n unsubscribeNetInfo?.();\n unsubscribeNetInfo = null;\n },\n };\n}\n","import type { SendMessageResponse } from '@pedi/chika-types';\nimport { withRetry, type RetryConfig } from './retry';\nimport { QueueFullError, ChatDisconnectedError } from './errors';\nimport type { NetworkMonitor } from './network-monitor';\n\nexport interface QueueStorage {\n getItem(key: string): Promise<string | null>;\n setItem(key: string, value: string): Promise<void>;\n removeItem(key: string): Promise<void>;\n}\n\nlet resolvedStorage: { type: string; adapter: QueueStorage } | null | undefined;\n\nfunction tryRequire(name: string): any {\n try {\n return require(name);\n } catch {\n return null;\n }\n}\n\nfunction createMmkvAdapter(mod: any): QueueStorage {\n const MMKV = mod.MMKV ?? mod.default?.MMKV ?? mod;\n const instance = new MMKV({ id: 'chika-queue' });\n return {\n getItem: (key) => Promise.resolve(instance.getString(key) ?? null),\n setItem: (key, value) => { instance.set(key, value); return Promise.resolve(); },\n removeItem: (key) => { instance.delete(key); return Promise.resolve(); },\n };\n}\n\nfunction createAsyncStorageAdapterFrom(mod: any): QueueStorage {\n const storage = mod.default ?? mod;\n return {\n getItem: (key) => storage.getItem(key),\n setItem: (key, value) => storage.setItem(key, value),\n removeItem: (key) => storage.removeItem(key),\n };\n}\n\n/**\n * Auto-detect the best available storage for queue persistence.\n * Priority: react-native-mmkv (fastest, sync) > AsyncStorage > null.\n * Returns `null` if neither is installed — no hard dependencies.\n *\n * Usage:\n * ```ts\n * import { createQueueStorage } from '@pedi/chika-sdk';\n *\n * const config: ChatConfig = {\n * resilience: {\n * queueStorage: createQueueStorage() ?? undefined,\n * },\n * };\n * ```\n */\nexport function createQueueStorage(): QueueStorage | null {\n if (resolvedStorage !== undefined) return resolvedStorage?.adapter ?? null;\n\n // Priority 1: MMKV (synchronous, fastest)\n const mmkv = tryRequire('react-native-mmkv');\n if (mmkv) {\n try {\n const adapter = createMmkvAdapter(mmkv);\n resolvedStorage = { type: 'mmkv', adapter };\n return adapter;\n } catch {\n // MMKV instantiation can fail if native module isn't linked\n }\n }\n\n // Priority 2: AsyncStorage\n const asyncStorage = tryRequire('@react-native-async-storage/async-storage');\n if (asyncStorage) {\n const adapter = createAsyncStorageAdapterFrom(asyncStorage);\n resolvedStorage = { type: 'async-storage', adapter };\n return adapter;\n }\n\n resolvedStorage = null;\n return null;\n}\n\n/**\n * Creates a QueueStorage adapter backed by `@react-native-async-storage/async-storage`.\n * Returns `null` if the package is not installed.\n */\nexport function createAsyncStorageAdapter(): QueueStorage | null {\n const mod = tryRequire('@react-native-async-storage/async-storage');\n if (!mod) return null;\n return createAsyncStorageAdapterFrom(mod);\n}\n\nexport type MessageSendStatus = 'sending' | 'queued' | 'failed';\n\nexport interface QueuedMessage {\n optimisticId: string;\n status: MessageSendStatus;\n error?: Error;\n retryCount: number;\n}\n\ntype SendFn = () => Promise<SendMessageResponse>;\n\ninterface QueueEntry {\n optimisticId: string;\n sendFn: SendFn;\n status: MessageSendStatus;\n error?: Error;\n retryCount: number;\n abort: AbortController;\n resolve: (value: SendMessageResponse) => void;\n reject: (reason: Error) => void;\n}\n\n/** Serializable subset persisted to storage. */\ninterface PersistedEntry {\n optimisticId: string;\n retryCount: number;\n}\n\nexport interface MessageQueueConfig {\n channelId: string;\n maxSize: number;\n retryConfig: RetryConfig;\n networkMonitor: NetworkMonitor;\n storage?: QueueStorage;\n onError?: (error: Error) => void;\n onStatusChange?: () => void;\n}\n\nexport class MessageQueue {\n private entries: QueueEntry[] = [];\n private flushing = false;\n private unsubNetwork: (() => void) | null = null;\n private readonly storageKey: string;\n\n constructor(private readonly config: MessageQueueConfig) {\n this.storageKey = `chika_queue_${config.channelId}`;\n\n this.unsubNetwork = config.networkMonitor.subscribe((connected) => {\n if (connected) this.flush();\n });\n }\n\n /**\n * Restore queued messages from persistent storage on cold start.\n * Restored entries are fire-and-forget — there is no caller awaiting their\n * promise (the original enqueue() caller is gone after app restart).\n * Success/failure is reported via onStatusChange/onError callbacks.\n */\n async restore(\n rebuildSendFn: (optimisticId: string) => SendFn | null,\n ): Promise<void> {\n if (!this.config.storage) return;\n\n try {\n const raw = await this.config.storage.getItem(this.storageKey);\n if (!raw) return;\n\n const persisted: PersistedEntry[] = JSON.parse(raw);\n for (const entry of persisted) {\n const sendFn = rebuildSendFn(entry.optimisticId);\n if (!sendFn) continue;\n\n const abort = new AbortController();\n this.entries.push({\n optimisticId: entry.optimisticId,\n sendFn,\n status: 'queued',\n retryCount: entry.retryCount,\n abort,\n // No-op: restored entries have no caller awaiting the promise.\n resolve: () => {},\n reject: () => {},\n });\n }\n\n if (this.entries.length > 0) {\n this.config.onStatusChange?.();\n this.flush();\n }\n } catch {\n this.config.onError?.(new Error('Failed to restore message queue from storage'));\n }\n }\n\n get pendingCount(): number {\n return this.entries.length;\n }\n\n getAll(): QueuedMessage[] {\n return this.entries.map((e) => ({\n optimisticId: e.optimisticId,\n status: e.status,\n error: e.error,\n retryCount: e.retryCount,\n }));\n }\n\n getStatus(optimisticId: string): QueuedMessage | undefined {\n const entry = this.entries.find((e) => e.optimisticId === optimisticId);\n if (!entry) return undefined;\n return {\n optimisticId: entry.optimisticId,\n status: entry.status,\n error: entry.error,\n retryCount: entry.retryCount,\n };\n }\n\n enqueue(sendFn: SendFn, optimisticId: string): Promise<SendMessageResponse> {\n if (this.entries.length >= this.config.maxSize) {\n throw new QueueFullError(this.config.maxSize);\n }\n\n const abort = new AbortController();\n\n return new Promise<SendMessageResponse>((resolve, reject) => {\n const entry: QueueEntry = {\n optimisticId,\n sendFn,\n status: 'queued',\n retryCount: 0,\n abort,\n resolve,\n reject,\n };\n\n this.entries.push(entry);\n this.config.onStatusChange?.();\n this.persist();\n this.flush();\n });\n }\n\n cancel(optimisticId: string): void {\n const idx = this.entries.findIndex((e) => e.optimisticId === optimisticId);\n if (idx === -1) return;\n\n const entry = this.entries[idx]!;\n entry.abort.abort();\n entry.reject(new DOMException('Cancelled', 'AbortError'));\n this.entries.splice(idx, 1);\n this.config.onStatusChange?.();\n this.persist();\n }\n\n retry(optimisticId: string): void {\n const entry = this.entries.find((e) => e.optimisticId === optimisticId);\n if (!entry || entry.status !== 'failed') return;\n\n entry.status = 'queued';\n entry.error = undefined;\n entry.abort = new AbortController();\n this.config.onStatusChange?.();\n this.flush();\n }\n\n dispose(): void {\n this.unsubNetwork?.();\n this.unsubNetwork = null;\n\n for (const entry of this.entries) {\n entry.abort.abort();\n entry.reject(new DOMException('Queue disposed', 'AbortError'));\n }\n this.entries = [];\n }\n\n /**\n * Trigger a flush of queued messages. Returns immediately if a flush is\n * already in progress or the network is offline. The in-progress flush\n * will pick up any newly queued entries via its while loop.\n */\n async flush(): Promise<void> {\n if (this.flushing) return;\n if (!this.config.networkMonitor.isConnected()) return;\n\n this.flushing = true;\n let awaitingSession = false;\n\n try {\n while (this.entries.length > 0) {\n // Only pick up 'queued' entries — 'sending' means a previous flush is\n // mid-request. Server-side idempotency key prevents duplicates if the\n // prior request actually succeeded but we never got the response.\n const entry = this.entries.find((e) => e.status === 'queued');\n if (!entry) break;\n if (!this.config.networkMonitor.isConnected()) break;\n\n entry.status = 'sending';\n this.config.onStatusChange?.();\n\n try {\n const result = await withRetry(\n entry.sendFn,\n this.config.retryConfig,\n entry.abort.signal,\n );\n\n entry.resolve(result);\n const idx = this.entries.indexOf(entry);\n if (idx !== -1) this.entries.splice(idx, 1);\n this.config.onStatusChange?.();\n this.persist();\n } catch (err) {\n if (entry.abort.signal.aborted) continue; // cancelled, already removed\n\n // Session not yet reconnected — revert to 'queued' and stop flushing.\n // flush() will be called again when the session is re-established\n // via the explicit flush() call in use-chat.ts startSession().\n if (err instanceof ChatDisconnectedError) {\n entry.status = 'queued';\n this.config.onStatusChange?.();\n awaitingSession = true;\n break;\n }\n\n entry.status = 'failed';\n entry.error = err instanceof Error ? err : new Error(String(err));\n entry.retryCount++;\n this.config.onStatusChange?.();\n this.persist();\n\n // Don't block the queue on a failed entry — skip to next\n }\n }\n } finally {\n this.flushing = false;\n // Re-check: entries may have been added while we were flushing.\n // Skip if we're waiting for session — startSession() will call flush().\n if (\n !awaitingSession &&\n this.entries.some((e) => e.status === 'queued') &&\n this.config.networkMonitor.isConnected()\n ) {\n queueMicrotask(() => this.flush());\n }\n }\n }\n\n private persist(): void {\n if (!this.config.storage) return;\n\n const data: PersistedEntry[] = this.entries.map((e) => ({\n optimisticId: e.optimisticId,\n retryCount: e.retryCount,\n }));\n\n const onErr = (err: unknown) => {\n this.config.onError?.(\n err instanceof Error ? err : new Error('Queue storage write failed'),\n );\n };\n\n if (data.length === 0) {\n this.config.storage.removeItem(this.storageKey).catch(onErr);\n } else {\n this.config.storage\n .setItem(this.storageKey, JSON.stringify(data))\n .catch(onErr);\n }\n }\n}\n","import { useEffect, useRef, useState, useCallback } from 'react';\nimport { AppState, Platform, type AppStateStatus } from 'react-native';\nimport type { UnreadCountResponse, SSEUnreadUpdateEvent } from '@pedi/chika-types';\nimport type { ChatConfig } from './types';\nimport { resolveServerUrl } from './resolve-url';\nimport { createSSEConnection, type SSEConnection } from './sse-connection';\nimport { createNetworkMonitor, type NetworkMonitor } from './network-monitor';\n\nconst DEFAULT_BACKGROUND_GRACE_MS = 2000;\nconst UNREAD_CUSTOM_EVENTS = ['unread_snapshot', 'unread_update', 'unread_clear'];\n\nexport interface UseUnreadOptions {\n config: ChatConfig;\n channelId: string;\n participantId: string;\n enabled?: boolean;\n}\n\nexport interface UseUnreadReturn {\n unreadCount: number;\n hasUnread: boolean;\n lastMessageAt: string | null;\n error: Error | null;\n}\n\nexport function useUnread(options: UseUnreadOptions): UseUnreadReturn {\n const { config, channelId, participantId, enabled = true } = options;\n\n const [unreadCount, setUnreadCount] = useState(0);\n const [lastMessageAt, setLastMessageAt] = useState<string | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n const connRef = useRef<SSEConnection | null>(null);\n const configRef = useRef(config);\n configRef.current = config;\n const appStateRef = useRef<AppStateStatus>(AppState.currentState);\n const backgroundTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const backgroundGraceMs =\n config.backgroundGraceMs ?? (Platform.OS === 'android' ? DEFAULT_BACKGROUND_GRACE_MS : 0);\n\n const [monitor, setMonitor] = useState<NetworkMonitor | null>(null);\n const resilienceEnabled = config.resilience !== false;\n const injectedMonitor =\n typeof config.resilience === 'object' ? config.resilience.networkMonitor : undefined;\n\n useEffect(() => {\n if (!resilienceEnabled) {\n setMonitor(null);\n return;\n }\n if (injectedMonitor) {\n setMonitor(injectedMonitor);\n return;\n }\n const m = createNetworkMonitor();\n setMonitor(m);\n return () => {\n m.dispose();\n setMonitor(null);\n };\n }, [resilienceEnabled, injectedMonitor]);\n\n const connect = useCallback(() => {\n connRef.current?.close();\n connRef.current = null;\n\n const serviceUrl = resolveServerUrl(configRef.current.manifest, channelId);\n const customHeaders = configRef.current.headers ?? {};\n const url = `${serviceUrl}/channels/${channelId}/unread?participant_id=${encodeURIComponent(participantId)}`;\n\n connRef.current = createSSEConnection(\n {\n url,\n headers: customHeaders,\n reconnectDelayMs: configRef.current.reconnectDelayMs,\n customEvents: UNREAD_CUSTOM_EVENTS,\n networkMonitor: monitor ?? undefined,\n },\n {\n onOpen: () => {\n setError(null);\n },\n onEvent: (eventType, data) => {\n try {\n if (eventType === 'unread_snapshot') {\n const snapshot: UnreadCountResponse = JSON.parse(data);\n setUnreadCount(snapshot.unread_count);\n setLastMessageAt(snapshot.last_message_at);\n } else if (eventType === 'unread_update') {\n const update: SSEUnreadUpdateEvent['data'] = JSON.parse(data);\n setUnreadCount((prev) => prev + 1);\n setLastMessageAt(update.created_at);\n } else if (eventType === 'unread_clear') {\n const clear: { channel_id: string; unread_count: number } = JSON.parse(data);\n setUnreadCount(clear.unread_count);\n }\n } catch {\n setError(new Error('Failed to parse unread SSE event'));\n }\n },\n onError: (err) => {\n setError(err);\n },\n onClosed: () => {\n connRef.current = null;\n },\n },\n );\n }, [channelId, participantId, monitor]);\n\n const disconnect = useCallback(() => {\n connRef.current?.close();\n connRef.current = null;\n }, []);\n\n useEffect(() => {\n setUnreadCount(0);\n setLastMessageAt(null);\n setError(null);\n\n if (!enabled) {\n disconnect();\n return;\n }\n\n // Wait for monitor before connecting (avoids double-connect on mount)\n if (resilienceEnabled && !monitor) return;\n\n connect();\n\n return () => {\n disconnect();\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n }\n };\n }, [channelId, participantId, enabled, connect, disconnect]);\n\n useEffect(() => {\n if (!enabled) return;\n\n const subscription = AppState.addEventListener('change', (nextAppState) => {\n const prev = appStateRef.current;\n appStateRef.current = nextAppState;\n\n if (!connRef.current && nextAppState !== 'active') return;\n\n const shouldTeardown =\n nextAppState === 'background' ||\n (Platform.OS === 'ios' && nextAppState === 'inactive');\n\n if (nextAppState === 'active') {\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n return;\n }\n\n if (prev.match(/inactive|background/) && !connRef.current) {\n connect();\n }\n } else if (shouldTeardown) {\n if (backgroundTimerRef.current) return;\n\n if (backgroundGraceMs === 0) {\n disconnect();\n } else {\n backgroundTimerRef.current = setTimeout(() => {\n backgroundTimerRef.current = null;\n disconnect();\n }, backgroundGraceMs);\n }\n }\n });\n\n return () => {\n subscription.remove();\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n }\n };\n }, [enabled, backgroundGraceMs, connect, disconnect]);\n\n return { unreadCount, hasUnread: unreadCount > 0, lastMessageAt, error };\n}\n"],"mappings":"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","names":["DEFAULT_RECONNECT_DELAY_MS","useEffect","useRef","useState","useCallback","AppState","Platform","DEFAULT_BACKGROUND_GRACE_MS","useState","useRef","AppState","Platform","useEffect","useCallback"]}
1
+ {"version":3,"sources":["../src/use-chat.ts","../src/errors.ts","../src/retry.ts","../src/resolve-url.ts","../src/sse-connection.ts","../src/session.ts","../src/network-monitor.ts","../src/message-queue.ts","../src/use-unread.ts"],"sourcesContent":["import { useEffect, useRef, useState, useCallback } from 'react';\nimport { AppState, Platform, type AppStateStatus } from 'react-native';\nimport type {\n ChatDomain,\n DefaultDomain,\n Message,\n Participant,\n MessageAttributes,\n SendMessageResponse,\n} from '@pedi/chika-types';\nimport type { UseChatOptions, UseChatReturn, ChatStatus } from './types';\nimport { ChatDisconnectedError, ChannelClosedError, QueueFullError, RetryExhaustedError } from './errors';\nimport { isRetryableError, resolveRetryConfig } from './retry';\nimport { createChatSession, type ChatSession, type SessionCallbacks } from './session';\nimport { createNetworkMonitor, type NetworkMonitor } from './network-monitor';\nimport { MessageQueue, type QueuedMessage } from './message-queue';\n\nconst DEFAULT_BACKGROUND_GRACE_MS = 2000;\nconst DEFAULT_MAX_QUEUE_SIZE = 50;\n\n// Module-scope queue registry, keyed by channelId. Survives component remounts.\nconst queueRegistry = new Map<string, { queue: MessageQueue; refCount: number }>();\n\n/**\n * React hook for real-time chat over SSE.\n * Manages connection lifecycle, AppState transitions, message deduplication, reconnection,\n * and optional network resilience (retry, offline queue, network monitoring).\n *\n * @template D - Chat domain type for role/message type narrowing. Defaults to DefaultDomain.\n */\nexport function useChat<D extends ChatDomain = DefaultDomain>(\n { config, channelId, profile, onMessage }: UseChatOptions<D>,\n): UseChatReturn<D> {\n const [messages, setMessages] = useState<Message<D>[]>([]);\n const [participants, setParticipants] = useState<Participant<D>[]>([]);\n const [status, setStatus] = useState<ChatStatus>('connecting');\n const [error, setError] = useState<Error | null>(null);\n const [pendingMessages, setPendingMessages] = useState<QueuedMessage[]>([]);\n\n const sessionRef = useRef<ChatSession<D> | null>(null);\n const disposedRef = useRef(false);\n const messagesRef = useRef(messages);\n messagesRef.current = messages;\n const statusRef = useRef(status);\n statusRef.current = status;\n const appStateRef = useRef<AppStateStatus>(AppState.currentState);\n const backgroundTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const profileRef = useRef(profile);\n profileRef.current = profile;\n const configRef = useRef(config);\n configRef.current = config;\n const onMessageRef = useRef(onMessage);\n onMessageRef.current = onMessage;\n const startingRef = useRef(false);\n const pendingOptimisticIds = useRef(new Set<string>());\n const [monitor, setMonitor] = useState<NetworkMonitor | null>(null);\n const [monitorReady, setMonitorReady] = useState(false);\n const monitorRef = useRef<NetworkMonitor | null>(null);\n monitorRef.current = monitor;\n const queueRef = useRef<MessageQueue | null>(null);\n\n const resilienceEnabled = config.resilience !== false;\n const queueEnabled =\n resilienceEnabled &&\n (typeof config.resilience === 'object' ? config.resilience.offlineQueue !== false : true);\n const retryConfig = resolveRetryConfig(config.resilience);\n const maxQueueSize =\n (resilienceEnabled && config.resilience && typeof config.resilience === 'object'\n ? config.resilience.maxQueueSize\n : undefined) ?? DEFAULT_MAX_QUEUE_SIZE;\n\n const backgroundGraceMs =\n config.backgroundGraceMs ?? (Platform.OS === 'android' ? DEFAULT_BACKGROUND_GRACE_MS : 0);\n\n // Resolve user-injected monitor (stable reference, no side effect)\n const injectedMonitor =\n typeof config.resilience === 'object' ? config.resilience.networkMonitor : undefined;\n\n // Create monitor in useEffect to avoid side effects during render.\n // Uses state (not ref) so the queue effect re-runs when monitor is ready.\n useEffect(() => {\n if (!resilienceEnabled) {\n setMonitor(null);\n setMonitorReady(true);\n return;\n }\n if (injectedMonitor) {\n setMonitor(injectedMonitor);\n setMonitorReady(true);\n return; // user owns lifecycle\n }\n try {\n const m = createNetworkMonitor();\n setMonitor(m);\n setMonitorReady(true);\n return () => {\n m.dispose();\n setMonitor(null);\n };\n } catch {\n // NetInfo native module may be present but not linked — fall back to\n // no monitor so the session effect guard doesn't block forever.\n setMonitor(null);\n setMonitorReady(true);\n }\n }, [resilienceEnabled, injectedMonitor]);\n\n const callbacks: SessionCallbacks<D> = {\n onMessage: (message) => {\n if (disposedRef.current) return;\n let matchedOptimisticId: string | null = null;\n setMessages((prev: Message<D>[]) => {\n if (pendingOptimisticIds.current.size === 0) {\n return [...prev, message];\n }\n\n const optimisticIdx = prev.findIndex(\n (m) =>\n pendingOptimisticIds.current.has(m.id) &&\n m.sender_id === message.sender_id &&\n m.body === message.body &&\n m.type === message.type,\n );\n if (optimisticIdx !== -1) {\n matchedOptimisticId = prev[optimisticIdx]!.id;\n pendingOptimisticIds.current.delete(matchedOptimisticId);\n const next = [...prev];\n next[optimisticIdx] = message;\n return next;\n }\n return [...prev, message];\n });\n // Clean up any queued/failed queue entry — SSE confirmed delivery.\n // Done outside setMessages updater to avoid setState-in-setState.\n if (matchedOptimisticId) {\n const s = queueRef.current?.getStatus(matchedOptimisticId);\n if (s && s.status !== 'sending') {\n queueRef.current?.cancel(matchedOptimisticId);\n }\n }\n onMessageRef.current?.(message);\n },\n onStatusChange: (nextStatus) => {\n if (disposedRef.current) return;\n setStatus(nextStatus);\n if (nextStatus === 'connected') {\n setError(null);\n }\n },\n onError: (err) => {\n if (disposedRef.current) return;\n setError(err);\n },\n onResync: () => {\n if (disposedRef.current) return;\n startSession();\n },\n };\n\n async function startSession(): Promise<void> {\n if (startingRef.current) return;\n startingRef.current = true;\n\n const existing = sessionRef.current;\n if (existing) {\n existing.disconnect();\n sessionRef.current = null;\n }\n\n try {\n const session = await createChatSession<D>(\n configRef.current,\n channelId,\n profileRef.current,\n callbacks,\n monitorRef.current ?? undefined,\n );\n\n if (disposedRef.current) {\n session.disconnect();\n return;\n }\n\n sessionRef.current = session;\n setParticipants(session.initialParticipants);\n\n // Flush any messages queued while session was unavailable\n queueRef.current?.flush();\n\n // Re-merge any pending optimistic messages after resync\n if (pendingOptimisticIds.current.size > 0) {\n const pendingIds = pendingOptimisticIds.current;\n setMessages((prev) => {\n const pendingMsgs = prev.filter((m) => pendingIds.has(m.id));\n return [...session.initialMessages, ...pendingMsgs];\n });\n } else {\n setMessages(session.initialMessages);\n }\n } catch (err) {\n if (disposedRef.current) return;\n\n if (err instanceof ChannelClosedError) {\n setStatus('closed');\n setError(err);\n return;\n }\n\n setStatus('error');\n setError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n startingRef.current = false;\n }\n }\n\n // Session lifecycle\n useEffect(() => {\n disposedRef.current = false;\n // Don't start until monitor initialization is complete\n if (!monitorReady) return;\n startSession();\n\n return () => {\n disposedRef.current = true;\n if (statusRef.current === 'connected' && sessionRef.current) {\n const lastMsg = messagesRef.current[messagesRef.current.length - 1];\n if (lastMsg) {\n sessionRef.current.markAsRead(lastMsg.id).catch(() => {});\n }\n }\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n }\n sessionRef.current?.disconnect();\n sessionRef.current = null;\n };\n }, [channelId, monitorReady]);\n\n // Module-scope queue lifecycle (ref-counted, survives remounts)\n useEffect(() => {\n if (!queueEnabled || !monitor || !retryConfig) return;\n\n let entry = queueRegistry.get(channelId);\n if (!entry) {\n const queue = new MessageQueue({\n channelId,\n maxSize: maxQueueSize,\n retryConfig,\n networkMonitor: monitor,\n storage:\n typeof config.resilience === 'object'\n ? config.resilience.queueStorage\n : undefined,\n onError: (err) => {\n if (!disposedRef.current) setError(err);\n },\n onStatusChange: () => {\n if (!disposedRef.current) {\n setPendingMessages(queueRef.current?.getAll() ?? []);\n }\n },\n });\n entry = { queue, refCount: 0 };\n queueRegistry.set(channelId, entry);\n }\n entry.refCount++;\n queueRef.current = entry.queue;\n\n return () => {\n const e = queueRegistry.get(channelId);\n if (e) {\n e.refCount--;\n if (e.refCount <= 0) {\n e.queue.dispose();\n queueRegistry.delete(channelId);\n }\n }\n queueRef.current = null;\n };\n }, [channelId, queueEnabled, monitor]);\n\n // Network monitor: auto-rejoin on connectivity return when in error state\n useEffect(() => {\n if (!monitor || !resilienceEnabled) return;\n\n const unsub = monitor.subscribe((connected: boolean) => {\n if (connected && statusRef.current === 'error' && !startingRef.current) {\n startSession();\n }\n });\n\n return unsub;\n }, [channelId, resilienceEnabled, monitor]);\n\n // AppState lifecycle\n useEffect(() => {\n function teardownSession(): void {\n sessionRef.current?.disconnect();\n sessionRef.current = null;\n setStatus('disconnected');\n }\n\n const subscription = AppState.addEventListener('change', (nextAppState) => {\n const prev = appStateRef.current;\n appStateRef.current = nextAppState;\n\n if (!sessionRef.current && nextAppState !== 'active') return;\n\n const shouldTeardown =\n nextAppState === 'background' ||\n (Platform.OS === 'ios' && nextAppState === 'inactive');\n\n if (nextAppState === 'active') {\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n return;\n }\n\n if (prev.match(/inactive|background/) && !sessionRef.current) {\n startSession();\n }\n } else if (shouldTeardown) {\n if (backgroundTimerRef.current) return;\n\n if (backgroundGraceMs === 0) {\n teardownSession();\n } else {\n backgroundTimerRef.current = setTimeout(() => {\n backgroundTimerRef.current = null;\n teardownSession();\n }, backgroundGraceMs);\n }\n }\n });\n\n return () => {\n subscription.remove();\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n }\n };\n }, [channelId, backgroundGraceMs]);\n\n const sendMessage = useCallback(\n async (type: D['messageType'], body: string, attributes?: MessageAttributes<D>): Promise<SendMessageResponse> => {\n const session = sessionRef.current;\n if (!session) throw new ChatDisconnectedError(statusRef.current);\n\n const optimistic = configRef.current.optimisticSend !== false;\n // Serves as both the optimistic message ID and the server-side idempotency key\n const messageKey = `optimistic_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\n\n if (optimistic) {\n pendingOptimisticIds.current.add(messageKey);\n const provisionalMsg: Message<D> = {\n id: messageKey,\n channel_id: channelId,\n sender_id: profileRef.current.id,\n sender_role: profileRef.current.role,\n type,\n body,\n attributes: (attributes ?? {}) as MessageAttributes<D>,\n created_at: new Date().toISOString(),\n };\n setMessages((prev) => [...prev, provisionalMsg]);\n }\n\n const doSend = () => {\n const s = sessionRef.current;\n if (!s) throw new ChatDisconnectedError(statusRef.current);\n return s.sendMessage(type, body, attributes, messageKey);\n };\n\n const handleSuccess = (response: SendMessageResponse): void => {\n if (optimistic) {\n pendingOptimisticIds.current.delete(messageKey);\n setMessages((prev) => {\n const stillPending = prev.some((m) => m.id === messageKey);\n if (!stillPending) return prev;\n return prev.map((m) =>\n m.id === messageKey\n ? { ...m, id: response.id, created_at: response.created_at }\n : m,\n );\n });\n }\n };\n\n const handleError = (_err: unknown): void => {\n if (optimistic) {\n pendingOptimisticIds.current.delete(messageKey);\n setMessages((prev) => prev.filter((m) => m.id !== messageKey));\n }\n };\n\n // Queue path: enqueue and let queue handle retry + offline\n if (queueRef.current) {\n try {\n const response = await queueRef.current.enqueue(doSend, messageKey);\n handleSuccess(response);\n return response;\n } catch (err) {\n if (err instanceof QueueFullError) {\n handleError(err);\n throw err;\n }\n if (err instanceof RetryExhaustedError || !isRetryableError(err)) {\n // For failed messages: if optimistic, mark as failed (keep in UI)\n // The pendingMessages state shows the status\n if (optimistic && err instanceof RetryExhaustedError) {\n // Keep optimistic message visible — queue tracks status as 'failed'.\n // Keep messageKey in pendingOptimisticIds so SSE reconciliation can\n // still match if the server eventually received the message.\n // Removed on explicit cancelMessage() call.\n } else {\n handleError(err);\n }\n throw err;\n }\n handleError(err);\n throw err;\n }\n }\n\n // Non-queue path: direct send (session.ts handles retry if enabled)\n try {\n const response = await doSend();\n handleSuccess(response);\n return response;\n } catch (err) {\n handleError(err);\n throw err;\n }\n },\n [channelId],\n );\n\n const cancelMessage = useCallback(\n (optimisticId: string) => {\n queueRef.current?.cancel(optimisticId);\n pendingOptimisticIds.current.delete(optimisticId);\n setMessages((prev) => prev.filter((m) => m.id !== optimisticId));\n },\n [],\n );\n\n const retryMessage = useCallback(\n (optimisticId: string) => {\n queueRef.current?.retry(optimisticId);\n },\n [],\n );\n\n const disconnect = useCallback(() => {\n sessionRef.current?.disconnect();\n sessionRef.current = null;\n setStatus('disconnected');\n }, []);\n\n return {\n messages,\n participants,\n status,\n error,\n sendMessage,\n disconnect,\n pendingMessages,\n cancelMessage,\n retryMessage,\n };\n}\n","import type { ChatStatus } from './types';\n\nexport class ChatDisconnectedError extends Error {\n constructor(public readonly status: ChatStatus) {\n super(`Cannot send message while ${status}`);\n this.name = 'ChatDisconnectedError';\n }\n}\n\nexport class ChannelClosedError extends Error {\n constructor(public readonly channelId: string) {\n super(`Channel ${channelId} is closed`);\n this.name = 'ChannelClosedError';\n }\n}\n\nexport class HttpError extends Error {\n constructor(\n public readonly status: number,\n public readonly body: string,\n public readonly retryAfter?: number,\n ) {\n super(`HTTP ${status}: ${body}`);\n this.name = 'HttpError';\n }\n}\n\nexport class RetryExhaustedError extends Error {\n constructor(\n public readonly operation: string,\n public readonly attempts: number,\n public readonly lastError: Error,\n ) {\n super(`${operation} failed after ${attempts} attempts: ${lastError.message}`);\n this.name = 'RetryExhaustedError';\n }\n}\n\nexport class QueueFullError extends Error {\n constructor(public readonly maxSize: number) {\n super(`Message queue full (max ${maxSize})`);\n this.name = 'QueueFullError';\n }\n}\n","import {\n HttpError,\n RetryExhaustedError,\n ChannelClosedError,\n ChatDisconnectedError,\n QueueFullError,\n} from './errors';\n\nexport interface RetryConfig {\n maxAttempts: number;\n baseDelayMs: number;\n maxDelayMs: number;\n jitterFactor: number;\n}\n\nexport const DEFAULT_RETRY_CONFIG: RetryConfig = {\n maxAttempts: 3,\n baseDelayMs: 1000,\n maxDelayMs: 10000,\n jitterFactor: 0.3,\n};\n\nexport function calculateBackoff(attempt: number, config: RetryConfig): number {\n const delay = Math.min(config.baseDelayMs * 2 ** attempt, config.maxDelayMs);\n const jitter = 1 + (Math.random() * 2 - 1) * config.jitterFactor;\n return Math.round(delay * jitter);\n}\n\nexport function isRetryableError(error: unknown): boolean {\n if (error instanceof ChannelClosedError) return false;\n if (error instanceof ChatDisconnectedError) return false;\n if (error instanceof QueueFullError) return false;\n if (error instanceof DOMException && error.name === 'AbortError') return false;\n\n if (error instanceof HttpError) {\n const { status } = error;\n if (status === 408 || status === 429) return true;\n if (status >= 500) return true;\n return false;\n }\n\n // TypeError from fetch = network failure\n if (error instanceof TypeError) return true;\n\n return false;\n}\n\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));\n return;\n }\n\n const timer = setTimeout(resolve, ms);\n\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));\n },\n { once: true },\n );\n });\n}\n\nexport function resolveRetryConfig(\n resilience: { retry?: Partial<RetryConfig> | false } | false | undefined,\n): RetryConfig | null {\n if (resilience === false) return null;\n if (!resilience || resilience.retry === undefined) return DEFAULT_RETRY_CONFIG;\n if (resilience.retry === false) return null;\n return { ...DEFAULT_RETRY_CONFIG, ...resilience.retry };\n}\n\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n config: RetryConfig = DEFAULT_RETRY_CONFIG,\n signal?: AbortSignal,\n): Promise<T> {\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt < config.maxAttempts; attempt++) {\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n\n try {\n return await fn();\n } catch (err) {\n lastError = err instanceof Error ? err : new Error(String(err));\n\n if (!isRetryableError(err)) throw lastError;\n\n if (attempt < config.maxAttempts - 1) {\n // Respect Retry-After header for 429 responses\n const delayMs =\n err instanceof HttpError && err.retryAfter != null\n ? err.retryAfter * 1000\n : calculateBackoff(attempt, config);\n\n await sleep(delayMs, signal);\n }\n }\n }\n\n throw new RetryExhaustedError(\n 'operation',\n config.maxAttempts,\n lastError!,\n );\n}\n","import type { ChatManifest } from '@pedi/chika-types';\n\n/**\n * Creates a single-server manifest. Use this when all channels route to the same server.\n *\n * @example\n * ```ts\n * const config: ChatConfig = { manifest: createManifest('https://chat.example.com') };\n * ```\n */\nexport function createManifest(serverUrl: string): ChatManifest {\n return { buckets: [{ group: 'default', range: [0, 99], server_url: serverUrl }] };\n}\n\nexport function resolveServerUrl(manifest: ChatManifest, channelId: string): string {\n const hash = [...channelId].reduce((sum, c) => sum + c.charCodeAt(0), 0) % 100;\n const bucket = manifest.buckets.find(\n (b) => hash >= b.range[0] && hash <= b.range[1],\n );\n if (!bucket) throw new Error(`No chat bucket for hash ${hash}`);\n return bucket.server_url;\n}\n","import EventSource from 'react-native-sse';\nimport { calculateBackoff, type RetryConfig } from './retry';\nimport type { NetworkMonitor } from './network-monitor';\n\nconst DEFAULT_RECONNECT_DELAY_MS = 3000;\n\nexport interface SSEConnectionConfig {\n url: string;\n headers?: Record<string, string>;\n reconnectDelayMs?: number;\n lastEventId?: string;\n customEvents?: string[];\n networkMonitor?: NetworkMonitor;\n}\n\nexport interface SSEConnectionCallbacks {\n onOpen?: () => void;\n onEvent: (eventType: string, data: string, lastEventId?: string) => void;\n onError?: (error: Error) => void;\n onClosed?: () => void;\n onReconnecting?: () => void;\n}\n\nexport interface SSEConnection {\n close: () => void;\n reconnectImmediate: () => void;\n}\n\nexport function createSSEConnection(\n config: SSEConnectionConfig,\n callbacks: SSEConnectionCallbacks,\n): SSEConnection {\n const baseDelay = config.reconnectDelayMs ?? DEFAULT_RECONNECT_DELAY_MS;\n const customEvents = config.customEvents ?? [];\n const monitor = config.networkMonitor;\n\n const backoffConfig: RetryConfig = {\n maxAttempts: Infinity,\n baseDelayMs: baseDelay,\n maxDelayMs: 30000,\n jitterFactor: 0.3,\n };\n\n let currentLastEventId = config.lastEventId;\n let es: EventSource<string> | null = null;\n let disposed = false;\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n let attempt = 0;\n let waitAbort: AbortController | null = null;\n\n function cleanup(): void {\n if (es) {\n es.removeAllEventListeners();\n es.close();\n es = null;\n }\n if (reconnectTimer) {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n }\n if (waitAbort) {\n waitAbort.abort();\n waitAbort = null;\n }\n }\n\n async function scheduleReconnect(): Promise<void> {\n if (disposed || reconnectTimer || waitAbort) return;\n callbacks.onReconnecting?.();\n cleanup();\n\n // Wait for network if monitor available\n if (monitor && !monitor.isConnected()) {\n waitAbort = new AbortController();\n try {\n await monitor.waitForOnline(waitAbort.signal);\n } catch {\n return; // aborted via dispose\n }\n waitAbort = null;\n if (disposed) return;\n }\n\n const delay = calculateBackoff(attempt++, backoffConfig);\n reconnectTimer = setTimeout(() => {\n reconnectTimer = null;\n connect();\n }, delay);\n }\n\n function connect(): void {\n if (disposed) return;\n\n es = new EventSource<string>(config.url, {\n headers: {\n ...config.headers,\n ...(currentLastEventId && { 'Last-Event-ID': currentLastEventId }),\n },\n pollingInterval: 0,\n });\n\n es.addEventListener('open', () => {\n if (disposed) return;\n attempt = 0;\n callbacks.onOpen?.();\n });\n\n es.addEventListener('message', (event) => {\n if (disposed || !event.data) return;\n if (event.lastEventId) {\n currentLastEventId = event.lastEventId;\n }\n callbacks.onEvent('message', event.data, event.lastEventId ?? undefined);\n });\n\n for (const eventName of customEvents) {\n es.addEventListener(eventName, (event) => {\n if (disposed) return;\n callbacks.onEvent(eventName, event.data ?? '', undefined);\n });\n }\n\n es.addEventListener('error', (event) => {\n if (disposed) return;\n\n const msg = 'message' in event ? String(event.message) : '';\n\n if (msg.includes('Channel is closed') || msg.includes('410')) {\n callbacks.onClosed?.();\n cleanup();\n disposed = true;\n return;\n }\n\n if (msg) callbacks.onError?.(new Error(msg));\n\n scheduleReconnect();\n });\n\n es.addEventListener('close', () => {\n if (disposed) return;\n scheduleReconnect();\n });\n }\n\n connect();\n\n return {\n close: () => {\n disposed = true;\n cleanup();\n },\n\n reconnectImmediate: () => {\n if (disposed) return;\n cleanup(); // does NOT set disposed\n attempt = 0;\n connect();\n },\n };\n}\n","import type {\n ChatDomain,\n DefaultDomain,\n Participant,\n Message,\n JoinResponse,\n SendMessageRequest,\n SendMessageResponse,\n MessageAttributes,\n} from '@pedi/chika-types';\nimport type { ChatConfig, ChatStatus } from './types';\nimport { ChatDisconnectedError, ChannelClosedError, HttpError } from './errors';\nimport { resolveServerUrl } from './resolve-url';\nimport { createSSEConnection, type SSEConnection } from './sse-connection';\nimport { withRetry, resolveRetryConfig, type RetryConfig } from './retry';\nimport type { NetworkMonitor } from './network-monitor';\n\nconst DEFAULT_RECONNECT_DELAY_MS = 3000;\nconst MAX_SEEN_IDS = 500;\n\nconst MARK_READ_RETRY_CONFIG: RetryConfig = {\n maxAttempts: 2,\n baseDelayMs: 500,\n maxDelayMs: 2000,\n jitterFactor: 0.3,\n};\n\nexport interface SessionCallbacks<D extends ChatDomain = DefaultDomain> {\n onMessage: (message: Message<D>) => void;\n onStatusChange: (status: ChatStatus) => void;\n onError: (error: Error) => void;\n onResync: () => void;\n}\n\nexport interface ChatSession<D extends ChatDomain = DefaultDomain> {\n serviceUrl: string;\n channelId: string;\n initialParticipants: Participant<D>[];\n initialMessages: Message<D>[];\n networkMonitor: NetworkMonitor | null;\n sendMessage: (\n type: D['messageType'],\n body: string,\n attributes?: MessageAttributes<D>,\n idempotencyKey?: string,\n ) => Promise<SendMessageResponse>;\n markAsRead: (messageId: string) => Promise<void>;\n disconnect: () => void;\n}\n\nfunction parseRetryAfter(res: Response): number | undefined {\n const header = res.headers.get('Retry-After');\n if (!header) return undefined;\n const seconds = Number(header);\n return Number.isFinite(seconds) ? seconds : undefined;\n}\n\nasync function throwHttpError(res: Response): Promise<never> {\n const body = await res.text().catch(() => '');\n throw new HttpError(res.status, body, parseRetryAfter(res));\n}\n\nexport async function createChatSession<D extends ChatDomain = DefaultDomain>(\n config: ChatConfig,\n channelId: string,\n profile: Participant<D>,\n callbacks: SessionCallbacks<D>,\n networkMonitor?: NetworkMonitor,\n): Promise<ChatSession<D>> {\n const serviceUrl = resolveServerUrl(config.manifest, channelId);\n const customHeaders = config.headers ?? {};\n const reconnectDelay = config.reconnectDelayMs ?? DEFAULT_RECONNECT_DELAY_MS;\n const retryConfig = resolveRetryConfig(config.resilience);\n\n const sessionAbort = new AbortController();\n\n callbacks.onStatusChange('connecting');\n\n const joinFn = async (): Promise<JoinResponse<D>> => {\n const joinRes = await fetch(`${serviceUrl}/channels/${channelId}/join`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...customHeaders },\n body: JSON.stringify(profile),\n signal: sessionAbort.signal,\n });\n\n if (joinRes.status === 410) {\n throw new ChannelClosedError(channelId);\n }\n\n if (!joinRes.ok) {\n await throwHttpError(joinRes);\n }\n\n return joinRes.json();\n };\n\n const joinData = retryConfig\n ? await withRetry(joinFn, retryConfig, sessionAbort.signal)\n : await joinFn();\n\n const { messages, participants, joined_at }: JoinResponse<D> = joinData;\n\n let lastEventId =\n messages.length > 0 ? messages[messages.length - 1]!.id : undefined;\n\n const joinedAt = joined_at;\n\n const seenMessageIds = new Set<string>(messages.map((m) => m.id));\n\n let sseConn: SSEConnection | null = null;\n let disposed = false;\n\n const TRIM_THRESHOLD = MAX_SEEN_IDS * 1.5;\n\n function trimSeenIds(): void {\n if (seenMessageIds.size <= TRIM_THRESHOLD) return;\n const ids = [...seenMessageIds];\n seenMessageIds.clear();\n for (const id of ids.slice(-MAX_SEEN_IDS)) {\n seenMessageIds.add(id);\n }\n }\n\n function connect(): void {\n if (disposed) return;\n\n const streamUrl = lastEventId\n ? `${serviceUrl}/channels/${channelId}/stream`\n : `${serviceUrl}/channels/${channelId}/stream?since_time=${encodeURIComponent(joinedAt)}`;\n\n sseConn = createSSEConnection(\n {\n url: streamUrl,\n headers: customHeaders,\n reconnectDelayMs: reconnectDelay,\n lastEventId,\n customEvents: ['resync'],\n networkMonitor,\n },\n {\n onOpen: () => {\n if (!disposed) callbacks.onStatusChange('connected');\n },\n onEvent: (eventType, data, eventId) => {\n if (disposed) return;\n\n if (eventType === 'message') {\n let message: Message<D>;\n try {\n message = JSON.parse(data);\n } catch {\n callbacks.onError(new Error('Failed to parse SSE message'));\n return;\n }\n\n if (eventId) {\n lastEventId = eventId;\n }\n\n if (seenMessageIds.has(message.id)) return;\n seenMessageIds.add(message.id);\n trimSeenIds();\n\n callbacks.onMessage(message);\n } else if (eventType === 'resync') {\n // Let onResync → startSession handle full session recreation.\n // Don't call reconnectImmediate() — startSession disconnects this\n // session anyway, so the reconnect would be immediately thrown away.\n callbacks.onResync();\n }\n },\n onError: (err) => {\n if (!disposed) callbacks.onError(err);\n },\n onClosed: () => {\n callbacks.onStatusChange('closed');\n disposed = true;\n },\n onReconnecting: () => {\n if (!disposed) callbacks.onStatusChange('reconnecting');\n },\n },\n );\n }\n\n connect();\n\n return {\n serviceUrl,\n channelId,\n initialParticipants: participants,\n initialMessages: messages,\n networkMonitor: networkMonitor ?? null,\n\n sendMessage: async (type, body, attributes, idempotencyKey) => {\n if (disposed) throw new ChatDisconnectedError('disconnected');\n\n const payload: SendMessageRequest<D> = {\n sender_id: profile.id,\n type,\n body,\n attributes,\n ...(idempotencyKey ? { idempotency_key: idempotencyKey } : {}),\n };\n\n const sendFn = async (): Promise<SendMessageResponse> => {\n const res = await fetch(\n `${serviceUrl}/channels/${channelId}/messages`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...customHeaders },\n body: JSON.stringify(payload),\n signal: sessionAbort.signal,\n },\n );\n\n if (!res.ok) {\n await throwHttpError(res);\n }\n\n return res.json();\n };\n\n const response = retryConfig\n ? await withRetry(sendFn, retryConfig, sessionAbort.signal)\n : await sendFn();\n\n seenMessageIds.add(response.id);\n return response;\n },\n\n markAsRead: async (messageId: string) => {\n // markAsRead intentionally does NOT use sessionAbort.signal — it must\n // survive disconnect() since it's called on unmount right before disconnect.\n const readFn = async (): Promise<void> => {\n const res = await fetch(`${serviceUrl}/channels/${channelId}/read`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...customHeaders },\n body: JSON.stringify({\n participant_id: profile.id,\n message_id: messageId,\n }),\n });\n if (!res.ok) {\n await throwHttpError(res);\n }\n };\n\n if (retryConfig) {\n try {\n await withRetry(readFn, MARK_READ_RETRY_CONFIG);\n } catch (err) {\n // Surface non-retryable errors (403, 404) for dev diagnostics\n if (err instanceof HttpError) {\n callbacks.onError(err);\n }\n // Swallow RetryExhaustedError — markAsRead is best-effort\n }\n } else {\n await readFn();\n }\n },\n\n disconnect: () => {\n disposed = true;\n sessionAbort.abort();\n sseConn?.close();\n sseConn = null;\n callbacks.onStatusChange('disconnected');\n },\n };\n}\n","export interface NetworkMonitor {\n isConnected(): boolean;\n subscribe(cb: (connected: boolean) => void): () => void;\n waitForOnline(signal?: AbortSignal): Promise<void>;\n dispose(): void;\n}\n\nfunction createStubMonitor(): NetworkMonitor {\n return {\n isConnected: () => true,\n subscribe: () => () => {},\n waitForOnline: () => Promise.resolve(),\n dispose: () => {},\n };\n}\n\nlet resolvedNetInfo: any = undefined;\nlet netInfoResolved = false;\n\nfunction getNetInfo(): any {\n if (netInfoResolved) return resolvedNetInfo;\n netInfoResolved = true;\n try {\n resolvedNetInfo = require('@react-native-community/netinfo');\n } catch {\n resolvedNetInfo = null;\n }\n return resolvedNetInfo;\n}\n\nexport function createNetworkMonitor(): NetworkMonitor {\n const NetInfo = getNetInfo();\n if (!NetInfo) return createStubMonitor();\n\n const netInfoModule = NetInfo.default ?? NetInfo;\n\n let connected = true;\n const listeners = new Set<(connected: boolean) => void>();\n let unsubscribeNetInfo: (() => void) | null = null;\n\n unsubscribeNetInfo = netInfoModule.addEventListener(\n (state: { isConnected: boolean | null }) => {\n const next = state.isConnected !== false;\n if (next === connected) return;\n connected = next;\n for (const cb of listeners) {\n cb(connected);\n }\n },\n );\n\n return {\n isConnected: () => connected,\n\n subscribe: (cb) => {\n listeners.add(cb);\n return () => {\n listeners.delete(cb);\n };\n },\n\n waitForOnline: (signal?) => {\n if (connected) return Promise.resolve();\n\n return new Promise<void>((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));\n return;\n }\n\n const unsub = (): void => {\n listeners.delete(handler);\n signal?.removeEventListener('abort', onAbort);\n };\n\n const handler = (isOnline: boolean): void => {\n if (isOnline) {\n unsub();\n resolve();\n }\n };\n\n const onAbort = (): void => {\n unsub();\n reject(signal!.reason ?? new DOMException('Aborted', 'AbortError'));\n };\n\n listeners.add(handler);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n },\n\n dispose: () => {\n listeners.clear();\n unsubscribeNetInfo?.();\n unsubscribeNetInfo = null;\n },\n };\n}\n","import type { SendMessageResponse } from '@pedi/chika-types';\nimport { withRetry, type RetryConfig } from './retry';\nimport { QueueFullError, ChatDisconnectedError } from './errors';\nimport type { NetworkMonitor } from './network-monitor';\n\nexport interface QueueStorage {\n getItem(key: string): Promise<string | null>;\n setItem(key: string, value: string): Promise<void>;\n removeItem(key: string): Promise<void>;\n}\n\nlet resolvedStorage: { type: string; adapter: QueueStorage } | null | undefined;\n\nfunction tryRequire(name: string): any {\n try {\n return require(name);\n } catch {\n return null;\n }\n}\n\nfunction createMmkvAdapter(mod: any): QueueStorage {\n const MMKV = mod.MMKV ?? mod.default?.MMKV ?? mod;\n const instance = new MMKV({ id: 'chika-queue' });\n return {\n getItem: (key) => Promise.resolve(instance.getString(key) ?? null),\n setItem: (key, value) => { instance.set(key, value); return Promise.resolve(); },\n removeItem: (key) => { instance.delete(key); return Promise.resolve(); },\n };\n}\n\nfunction createAsyncStorageAdapterFrom(mod: any): QueueStorage {\n const storage = mod.default ?? mod;\n return {\n getItem: (key) => storage.getItem(key),\n setItem: (key, value) => storage.setItem(key, value),\n removeItem: (key) => storage.removeItem(key),\n };\n}\n\n/**\n * Auto-detect the best available storage for queue persistence.\n * Priority: react-native-mmkv (fastest, sync) > AsyncStorage > null.\n * Returns `null` if neither is installed — no hard dependencies.\n *\n * Usage:\n * ```ts\n * import { createQueueStorage } from '@pedi/chika-sdk';\n *\n * const config: ChatConfig = {\n * resilience: {\n * queueStorage: createQueueStorage() ?? undefined,\n * },\n * };\n * ```\n */\nexport function createQueueStorage(): QueueStorage | null {\n if (resolvedStorage !== undefined) return resolvedStorage?.adapter ?? null;\n\n // Priority 1: MMKV (synchronous, fastest)\n const mmkv = tryRequire('react-native-mmkv');\n if (mmkv) {\n try {\n const adapter = createMmkvAdapter(mmkv);\n resolvedStorage = { type: 'mmkv', adapter };\n return adapter;\n } catch {\n // MMKV instantiation can fail if native module isn't linked\n }\n }\n\n // Priority 2: AsyncStorage\n const asyncStorage = tryRequire('@react-native-async-storage/async-storage');\n if (asyncStorage) {\n const adapter = createAsyncStorageAdapterFrom(asyncStorage);\n resolvedStorage = { type: 'async-storage', adapter };\n return adapter;\n }\n\n resolvedStorage = null;\n return null;\n}\n\n/**\n * Creates a QueueStorage adapter backed by `@react-native-async-storage/async-storage`.\n * Returns `null` if the package is not installed.\n */\nexport function createAsyncStorageAdapter(): QueueStorage | null {\n const mod = tryRequire('@react-native-async-storage/async-storage');\n if (!mod) return null;\n return createAsyncStorageAdapterFrom(mod);\n}\n\nexport type MessageSendStatus = 'sending' | 'queued' | 'failed';\n\nexport interface QueuedMessage {\n optimisticId: string;\n status: MessageSendStatus;\n error?: Error;\n retryCount: number;\n}\n\ntype SendFn = () => Promise<SendMessageResponse>;\n\ninterface QueueEntry {\n optimisticId: string;\n sendFn: SendFn;\n status: MessageSendStatus;\n error?: Error;\n retryCount: number;\n abort: AbortController;\n resolve: (value: SendMessageResponse) => void;\n reject: (reason: Error) => void;\n}\n\n/** Serializable subset persisted to storage. */\ninterface PersistedEntry {\n optimisticId: string;\n retryCount: number;\n}\n\nexport interface MessageQueueConfig {\n channelId: string;\n maxSize: number;\n retryConfig: RetryConfig;\n networkMonitor: NetworkMonitor;\n storage?: QueueStorage;\n onError?: (error: Error) => void;\n onStatusChange?: () => void;\n}\n\nexport class MessageQueue {\n private entries: QueueEntry[] = [];\n private flushing = false;\n private unsubNetwork: (() => void) | null = null;\n private readonly storageKey: string;\n\n constructor(private readonly config: MessageQueueConfig) {\n this.storageKey = `chika_queue_${config.channelId}`;\n\n this.unsubNetwork = config.networkMonitor.subscribe((connected) => {\n if (connected) this.flush();\n });\n }\n\n /**\n * Restore queued messages from persistent storage on cold start.\n * Restored entries are fire-and-forget — there is no caller awaiting their\n * promise (the original enqueue() caller is gone after app restart).\n * Success/failure is reported via onStatusChange/onError callbacks.\n */\n async restore(\n rebuildSendFn: (optimisticId: string) => SendFn | null,\n ): Promise<void> {\n if (!this.config.storage) return;\n\n try {\n const raw = await this.config.storage.getItem(this.storageKey);\n if (!raw) return;\n\n const persisted: PersistedEntry[] = JSON.parse(raw);\n for (const entry of persisted) {\n const sendFn = rebuildSendFn(entry.optimisticId);\n if (!sendFn) continue;\n\n const abort = new AbortController();\n this.entries.push({\n optimisticId: entry.optimisticId,\n sendFn,\n status: 'queued',\n retryCount: entry.retryCount,\n abort,\n // No-op: restored entries have no caller awaiting the promise.\n resolve: () => {},\n reject: () => {},\n });\n }\n\n if (this.entries.length > 0) {\n this.config.onStatusChange?.();\n this.flush();\n }\n } catch {\n this.config.onError?.(new Error('Failed to restore message queue from storage'));\n }\n }\n\n get pendingCount(): number {\n return this.entries.length;\n }\n\n getAll(): QueuedMessage[] {\n return this.entries.map((e) => ({\n optimisticId: e.optimisticId,\n status: e.status,\n error: e.error,\n retryCount: e.retryCount,\n }));\n }\n\n getStatus(optimisticId: string): QueuedMessage | undefined {\n const entry = this.entries.find((e) => e.optimisticId === optimisticId);\n if (!entry) return undefined;\n return {\n optimisticId: entry.optimisticId,\n status: entry.status,\n error: entry.error,\n retryCount: entry.retryCount,\n };\n }\n\n enqueue(sendFn: SendFn, optimisticId: string): Promise<SendMessageResponse> {\n if (this.entries.length >= this.config.maxSize) {\n throw new QueueFullError(this.config.maxSize);\n }\n\n const abort = new AbortController();\n\n return new Promise<SendMessageResponse>((resolve, reject) => {\n const entry: QueueEntry = {\n optimisticId,\n sendFn,\n status: 'queued',\n retryCount: 0,\n abort,\n resolve,\n reject,\n };\n\n this.entries.push(entry);\n this.config.onStatusChange?.();\n this.persist();\n this.flush();\n });\n }\n\n cancel(optimisticId: string): void {\n const idx = this.entries.findIndex((e) => e.optimisticId === optimisticId);\n if (idx === -1) return;\n\n const entry = this.entries[idx]!;\n entry.abort.abort();\n entry.reject(new DOMException('Cancelled', 'AbortError'));\n this.entries.splice(idx, 1);\n this.config.onStatusChange?.();\n this.persist();\n }\n\n retry(optimisticId: string): void {\n const entry = this.entries.find((e) => e.optimisticId === optimisticId);\n if (!entry || entry.status !== 'failed') return;\n\n entry.status = 'queued';\n entry.error = undefined;\n entry.abort = new AbortController();\n this.config.onStatusChange?.();\n this.flush();\n }\n\n dispose(): void {\n this.unsubNetwork?.();\n this.unsubNetwork = null;\n\n for (const entry of this.entries) {\n entry.abort.abort();\n entry.reject(new DOMException('Queue disposed', 'AbortError'));\n }\n this.entries = [];\n }\n\n /**\n * Trigger a flush of queued messages. Returns immediately if a flush is\n * already in progress or the network is offline. The in-progress flush\n * will pick up any newly queued entries via its while loop.\n */\n async flush(): Promise<void> {\n if (this.flushing) return;\n if (!this.config.networkMonitor.isConnected()) return;\n\n this.flushing = true;\n let awaitingSession = false;\n\n try {\n while (this.entries.length > 0) {\n // Only pick up 'queued' entries — 'sending' means a previous flush is\n // mid-request. Server-side idempotency key prevents duplicates if the\n // prior request actually succeeded but we never got the response.\n const entry = this.entries.find((e) => e.status === 'queued');\n if (!entry) break;\n if (!this.config.networkMonitor.isConnected()) break;\n\n entry.status = 'sending';\n this.config.onStatusChange?.();\n\n try {\n const result = await withRetry(\n entry.sendFn,\n this.config.retryConfig,\n entry.abort.signal,\n );\n\n entry.resolve(result);\n const idx = this.entries.indexOf(entry);\n if (idx !== -1) this.entries.splice(idx, 1);\n this.config.onStatusChange?.();\n this.persist();\n } catch (err) {\n if (entry.abort.signal.aborted) continue; // cancelled, already removed\n\n // Session not yet reconnected — revert to 'queued' and stop flushing.\n // flush() will be called again when the session is re-established\n // via the explicit flush() call in use-chat.ts startSession().\n if (err instanceof ChatDisconnectedError) {\n entry.status = 'queued';\n this.config.onStatusChange?.();\n awaitingSession = true;\n break;\n }\n\n entry.status = 'failed';\n entry.error = err instanceof Error ? err : new Error(String(err));\n entry.retryCount++;\n entry.reject(entry.error);\n this.config.onStatusChange?.();\n this.persist();\n\n // Don't block the queue on a failed entry — skip to next\n }\n }\n } finally {\n this.flushing = false;\n // Re-check: entries may have been added while we were flushing.\n // Skip if we're waiting for session — startSession() will call flush().\n if (\n !awaitingSession &&\n this.entries.some((e) => e.status === 'queued') &&\n this.config.networkMonitor.isConnected()\n ) {\n queueMicrotask(() => this.flush());\n }\n }\n }\n\n private persist(): void {\n if (!this.config.storage) return;\n\n const data: PersistedEntry[] = this.entries.map((e) => ({\n optimisticId: e.optimisticId,\n retryCount: e.retryCount,\n }));\n\n const onErr = (err: unknown) => {\n this.config.onError?.(\n err instanceof Error ? err : new Error('Queue storage write failed'),\n );\n };\n\n if (data.length === 0) {\n this.config.storage.removeItem(this.storageKey).catch(onErr);\n } else {\n this.config.storage\n .setItem(this.storageKey, JSON.stringify(data))\n .catch(onErr);\n }\n }\n}\n","import { useEffect, useRef, useState, useCallback } from 'react';\nimport { AppState, Platform, type AppStateStatus } from 'react-native';\nimport type { UnreadCountResponse, SSEUnreadUpdateEvent } from '@pedi/chika-types';\nimport type { ChatConfig } from './types';\nimport { resolveServerUrl } from './resolve-url';\nimport { createSSEConnection, type SSEConnection } from './sse-connection';\nimport { createNetworkMonitor, type NetworkMonitor } from './network-monitor';\n\nconst DEFAULT_BACKGROUND_GRACE_MS = 2000;\nconst UNREAD_CUSTOM_EVENTS = ['unread_snapshot', 'unread_update', 'unread_clear'];\n\nexport interface UseUnreadOptions {\n config: ChatConfig;\n channelId: string;\n participantId: string;\n enabled?: boolean;\n}\n\nexport interface UseUnreadReturn {\n unreadCount: number;\n hasUnread: boolean;\n lastMessageAt: string | null;\n error: Error | null;\n}\n\nexport function useUnread(options: UseUnreadOptions): UseUnreadReturn {\n const { config, channelId, participantId, enabled = true } = options;\n\n const [unreadCount, setUnreadCount] = useState(0);\n const [lastMessageAt, setLastMessageAt] = useState<string | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n const connRef = useRef<SSEConnection | null>(null);\n const configRef = useRef(config);\n configRef.current = config;\n const appStateRef = useRef<AppStateStatus>(AppState.currentState);\n const backgroundTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const backgroundGraceMs =\n config.backgroundGraceMs ?? (Platform.OS === 'android' ? DEFAULT_BACKGROUND_GRACE_MS : 0);\n\n const [monitorReady, setMonitorReady] = useState(false);\n const monitorRef = useRef<NetworkMonitor | null>(null);\n const resilienceEnabled = config.resilience !== false;\n const injectedMonitor =\n typeof config.resilience === 'object' ? config.resilience.networkMonitor : undefined;\n\n useEffect(() => {\n if (!resilienceEnabled) {\n monitorRef.current = null;\n setMonitorReady(true);\n return;\n }\n if (injectedMonitor) {\n monitorRef.current = injectedMonitor;\n setMonitorReady(true);\n return;\n }\n try {\n const m = createNetworkMonitor();\n monitorRef.current = m;\n setMonitorReady(true);\n return () => {\n m.dispose();\n monitorRef.current = null;\n };\n } catch {\n monitorRef.current = null;\n setMonitorReady(true);\n }\n }, [resilienceEnabled, injectedMonitor]);\n\n const connect = useCallback(() => {\n connRef.current?.close();\n connRef.current = null;\n\n const serviceUrl = resolveServerUrl(configRef.current.manifest, channelId);\n const customHeaders = configRef.current.headers ?? {};\n const url = `${serviceUrl}/channels/${channelId}/unread?participant_id=${encodeURIComponent(participantId)}`;\n\n connRef.current = createSSEConnection(\n {\n url,\n headers: customHeaders,\n reconnectDelayMs: configRef.current.reconnectDelayMs,\n customEvents: UNREAD_CUSTOM_EVENTS,\n networkMonitor: monitorRef.current ?? undefined,\n },\n {\n onOpen: () => {\n setError(null);\n },\n onEvent: (eventType, data) => {\n try {\n if (eventType === 'unread_snapshot') {\n const snapshot: UnreadCountResponse = JSON.parse(data);\n setUnreadCount(snapshot.unread_count);\n setLastMessageAt(snapshot.last_message_at);\n } else if (eventType === 'unread_update') {\n const update: SSEUnreadUpdateEvent['data'] = JSON.parse(data);\n setUnreadCount((prev) => prev + 1);\n setLastMessageAt(update.created_at);\n } else if (eventType === 'unread_clear') {\n const clear: { channel_id: string; unread_count: number } = JSON.parse(data);\n setUnreadCount(clear.unread_count);\n }\n } catch {\n setError(new Error('Failed to parse unread SSE event'));\n }\n },\n onError: (err) => {\n setError(err);\n },\n onClosed: () => {\n connRef.current = null;\n },\n },\n );\n }, [channelId, participantId]);\n\n const disconnect = useCallback(() => {\n connRef.current?.close();\n connRef.current = null;\n }, []);\n\n useEffect(() => {\n setUnreadCount(0);\n setLastMessageAt(null);\n setError(null);\n\n if (!enabled) {\n disconnect();\n return;\n }\n\n // Don't connect until monitor initialization is complete\n if (!monitorReady) return;\n\n connect();\n\n return () => {\n disconnect();\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n }\n };\n }, [channelId, participantId, enabled, monitorReady, connect, disconnect]);\n\n useEffect(() => {\n if (!enabled) return;\n\n const subscription = AppState.addEventListener('change', (nextAppState) => {\n const prev = appStateRef.current;\n appStateRef.current = nextAppState;\n\n if (!connRef.current && nextAppState !== 'active') return;\n\n const shouldTeardown =\n nextAppState === 'background' ||\n (Platform.OS === 'ios' && nextAppState === 'inactive');\n\n if (nextAppState === 'active') {\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n return;\n }\n\n if (prev.match(/inactive|background/) && !connRef.current) {\n connect();\n }\n } else if (shouldTeardown) {\n if (backgroundTimerRef.current) return;\n\n if (backgroundGraceMs === 0) {\n disconnect();\n } else {\n backgroundTimerRef.current = setTimeout(() => {\n backgroundTimerRef.current = null;\n disconnect();\n }, backgroundGraceMs);\n }\n }\n });\n\n return () => {\n subscription.remove();\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n }\n };\n }, [enabled, backgroundGraceMs, connect, disconnect]);\n\n return { unreadCount, hasUnread: unreadCount > 0, lastMessageAt, error };\n}\n"],"mappings":";;;;;;;;AAAA,SAAS,WAAW,QAAQ,UAAU,mBAAmB;AACzD,SAAS,UAAU,gBAAqC;;;ACCjD,IAAM,wBAAN,cAAoC,MAAM;AAAA,EAC/C,YAA4B,QAAoB;AAC9C,UAAM,6BAA6B,MAAM,EAAE;AADjB;AAE1B,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,qBAAN,cAAiC,MAAM;AAAA,EAC5C,YAA4B,WAAmB;AAC7C,UAAM,WAAW,SAAS,YAAY;AADZ;AAE1B,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,YAAN,cAAwB,MAAM;AAAA,EACnC,YACkB,QACA,MACA,YAChB;AACA,UAAM,QAAQ,MAAM,KAAK,IAAI,EAAE;AAJf;AACA;AACA;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,sBAAN,cAAkC,MAAM;AAAA,EAC7C,YACkB,WACA,UACA,WAChB;AACA,UAAM,GAAG,SAAS,iBAAiB,QAAQ,cAAc,UAAU,OAAO,EAAE;AAJ5D;AACA;AACA;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,iBAAN,cAA6B,MAAM;AAAA,EACxC,YAA4B,SAAiB;AAC3C,UAAM,2BAA2B,OAAO,GAAG;AADjB;AAE1B,SAAK,OAAO;AAAA,EACd;AACF;;;AC5BO,IAAM,uBAAoC;AAAA,EAC/C,aAAa;AAAA,EACb,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,cAAc;AAChB;AAEO,SAAS,iBAAiB,SAAiB,QAA6B;AAC7E,QAAM,QAAQ,KAAK,IAAI,OAAO,cAAc,KAAK,SAAS,OAAO,UAAU;AAC3E,QAAM,SAAS,KAAK,KAAK,OAAO,IAAI,IAAI,KAAK,OAAO;AACpD,SAAO,KAAK,MAAM,QAAQ,MAAM;AAClC;AAEO,SAAS,iBAAiB,OAAyB;AACxD,MAAI,iBAAiB,mBAAoB,QAAO;AAChD,MAAI,iBAAiB,sBAAuB,QAAO;AACnD,MAAI,iBAAiB,eAAgB,QAAO;AAC5C,MAAI,iBAAiB,gBAAgB,MAAM,SAAS,aAAc,QAAO;AAEzE,MAAI,iBAAiB,WAAW;AAC9B,UAAM,EAAE,OAAO,IAAI;AACnB,QAAI,WAAW,OAAO,WAAW,IAAK,QAAO;AAC7C,QAAI,UAAU,IAAK,QAAO;AAC1B,WAAO;AAAA,EACT;AAGA,MAAI,iBAAiB,UAAW,QAAO;AAEvC,SAAO;AACT;AAEO,SAAS,MAAM,IAAY,QAAqC;AACrE,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI,QAAQ,SAAS;AACnB,aAAO,OAAO,UAAU,IAAI,aAAa,WAAW,YAAY,CAAC;AACjE;AAAA,IACF;AAEA,UAAM,QAAQ,WAAW,SAAS,EAAE;AAEpC,YAAQ;AAAA,MACN;AAAA,MACA,MAAM;AACJ,qBAAa,KAAK;AAClB,eAAO,OAAO,UAAU,IAAI,aAAa,WAAW,YAAY,CAAC;AAAA,MACnE;AAAA,MACA,EAAE,MAAM,KAAK;AAAA,IACf;AAAA,EACF,CAAC;AACH;AAEO,SAAS,mBACd,YACoB;AACpB,MAAI,eAAe,MAAO,QAAO;AACjC,MAAI,CAAC,cAAc,WAAW,UAAU,OAAW,QAAO;AAC1D,MAAI,WAAW,UAAU,MAAO,QAAO;AACvC,SAAO,EAAE,GAAG,sBAAsB,GAAG,WAAW,MAAM;AACxD;AAEA,eAAsB,UACpB,IACA,SAAsB,sBACtB,QACY;AACZ,MAAI;AAEJ,WAAS,UAAU,GAAG,UAAU,OAAO,aAAa,WAAW;AAC7D,QAAI,QAAQ,SAAS;AACnB,YAAM,OAAO,UAAU,IAAI,aAAa,WAAW,YAAY;AAAA,IACjE;AAEA,QAAI;AACF,aAAO,MAAM,GAAG;AAAA,IAClB,SAAS,KAAK;AACZ,kBAAY,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAE9D,UAAI,CAAC,iBAAiB,GAAG,EAAG,OAAM;AAElC,UAAI,UAAU,OAAO,cAAc,GAAG;AAEpC,cAAM,UACJ,eAAe,aAAa,IAAI,cAAc,OAC1C,IAAI,aAAa,MACjB,iBAAiB,SAAS,MAAM;AAEtC,cAAM,MAAM,SAAS,MAAM;AAAA,MAC7B;AAAA,IACF;AAAA,EACF;AAEA,QAAM,IAAI;AAAA,IACR;AAAA,IACA,OAAO;AAAA,IACP;AAAA,EACF;AACF;;;ACtGO,SAAS,eAAe,WAAiC;AAC9D,SAAO,EAAE,SAAS,CAAC,EAAE,OAAO,WAAW,OAAO,CAAC,GAAG,EAAE,GAAG,YAAY,UAAU,CAAC,EAAE;AAClF;AAEO,SAAS,iBAAiB,UAAwB,WAA2B;AAClF,QAAM,OAAO,CAAC,GAAG,SAAS,EAAE,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,WAAW,CAAC,GAAG,CAAC,IAAI;AAC3E,QAAM,SAAS,SAAS,QAAQ;AAAA,IAC9B,CAAC,MAAM,QAAQ,EAAE,MAAM,CAAC,KAAK,QAAQ,EAAE,MAAM,CAAC;AAAA,EAChD;AACA,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,2BAA2B,IAAI,EAAE;AAC9D,SAAO,OAAO;AAChB;;;ACrBA,OAAO,iBAAiB;AAIxB,IAAM,6BAA6B;AAwB5B,SAAS,oBACd,QACA,WACe;AACf,QAAM,YAAY,OAAO,oBAAoB;AAC7C,QAAM,eAAe,OAAO,gBAAgB,CAAC;AAC7C,QAAM,UAAU,OAAO;AAEvB,QAAM,gBAA6B;AAAA,IACjC,aAAa;AAAA,IACb,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,cAAc;AAAA,EAChB;AAEA,MAAI,qBAAqB,OAAO;AAChC,MAAI,KAAiC;AACrC,MAAI,WAAW;AACf,MAAI,iBAAuD;AAC3D,MAAI,UAAU;AACd,MAAI,YAAoC;AAExC,WAAS,UAAgB;AACvB,QAAI,IAAI;AACN,SAAG,wBAAwB;AAC3B,SAAG,MAAM;AACT,WAAK;AAAA,IACP;AACA,QAAI,gBAAgB;AAClB,mBAAa,cAAc;AAC3B,uBAAiB;AAAA,IACnB;AACA,QAAI,WAAW;AACb,gBAAU,MAAM;AAChB,kBAAY;AAAA,IACd;AAAA,EACF;AAEA,iBAAe,oBAAmC;AAChD,QAAI,YAAY,kBAAkB,UAAW;AAC7C,cAAU,iBAAiB;AAC3B,YAAQ;AAGR,QAAI,WAAW,CAAC,QAAQ,YAAY,GAAG;AACrC,kBAAY,IAAI,gBAAgB;AAChC,UAAI;AACF,cAAM,QAAQ,cAAc,UAAU,MAAM;AAAA,MAC9C,QAAQ;AACN;AAAA,MACF;AACA,kBAAY;AACZ,UAAI,SAAU;AAAA,IAChB;AAEA,UAAM,QAAQ,iBAAiB,WAAW,aAAa;AACvD,qBAAiB,WAAW,MAAM;AAChC,uBAAiB;AACjB,cAAQ;AAAA,IACV,GAAG,KAAK;AAAA,EACV;AAEA,WAAS,UAAgB;AACvB,QAAI,SAAU;AAEd,SAAK,IAAI,YAAoB,OAAO,KAAK;AAAA,MACvC,SAAS;AAAA,QACP,GAAG,OAAO;AAAA,QACV,GAAI,sBAAsB,EAAE,iBAAiB,mBAAmB;AAAA,MAClE;AAAA,MACA,iBAAiB;AAAA,IACnB,CAAC;AAED,OAAG,iBAAiB,QAAQ,MAAM;AAChC,UAAI,SAAU;AACd,gBAAU;AACV,gBAAU,SAAS;AAAA,IACrB,CAAC;AAED,OAAG,iBAAiB,WAAW,CAAC,UAAU;AACxC,UAAI,YAAY,CAAC,MAAM,KAAM;AAC7B,UAAI,MAAM,aAAa;AACrB,6BAAqB,MAAM;AAAA,MAC7B;AACA,gBAAU,QAAQ,WAAW,MAAM,MAAM,MAAM,eAAe,MAAS;AAAA,IACzE,CAAC;AAED,eAAW,aAAa,cAAc;AACpC,SAAG,iBAAiB,WAAW,CAAC,UAAU;AACxC,YAAI,SAAU;AACd,kBAAU,QAAQ,WAAW,MAAM,QAAQ,IAAI,MAAS;AAAA,MAC1D,CAAC;AAAA,IACH;AAEA,OAAG,iBAAiB,SAAS,CAAC,UAAU;AACtC,UAAI,SAAU;AAEd,YAAM,MAAM,aAAa,QAAQ,OAAO,MAAM,OAAO,IAAI;AAEzD,UAAI,IAAI,SAAS,mBAAmB,KAAK,IAAI,SAAS,KAAK,GAAG;AAC5D,kBAAU,WAAW;AACrB,gBAAQ;AACR,mBAAW;AACX;AAAA,MACF;AAEA,UAAI,IAAK,WAAU,UAAU,IAAI,MAAM,GAAG,CAAC;AAE3C,wBAAkB;AAAA,IACpB,CAAC;AAED,OAAG,iBAAiB,SAAS,MAAM;AACjC,UAAI,SAAU;AACd,wBAAkB;AAAA,IACpB,CAAC;AAAA,EACH;AAEA,UAAQ;AAER,SAAO;AAAA,IACL,OAAO,MAAM;AACX,iBAAW;AACX,cAAQ;AAAA,IACV;AAAA,IAEA,oBAAoB,MAAM;AACxB,UAAI,SAAU;AACd,cAAQ;AACR,gBAAU;AACV,cAAQ;AAAA,IACV;AAAA,EACF;AACF;;;AC/IA,IAAMA,8BAA6B;AACnC,IAAM,eAAe;AAErB,IAAM,yBAAsC;AAAA,EAC1C,aAAa;AAAA,EACb,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,cAAc;AAChB;AAyBA,SAAS,gBAAgB,KAAmC;AAC1D,QAAM,SAAS,IAAI,QAAQ,IAAI,aAAa;AAC5C,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,UAAU,OAAO,MAAM;AAC7B,SAAO,OAAO,SAAS,OAAO,IAAI,UAAU;AAC9C;AAEA,eAAe,eAAe,KAA+B;AAC3D,QAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,QAAM,IAAI,UAAU,IAAI,QAAQ,MAAM,gBAAgB,GAAG,CAAC;AAC5D;AAEA,eAAsB,kBACpB,QACA,WACA,SACA,WACA,gBACyB;AACzB,QAAM,aAAa,iBAAiB,OAAO,UAAU,SAAS;AAC9D,QAAM,gBAAgB,OAAO,WAAW,CAAC;AACzC,QAAM,iBAAiB,OAAO,oBAAoBA;AAClD,QAAM,cAAc,mBAAmB,OAAO,UAAU;AAExD,QAAM,eAAe,IAAI,gBAAgB;AAEzC,YAAU,eAAe,YAAY;AAErC,QAAM,SAAS,YAAsC;AACnD,UAAM,UAAU,MAAM,MAAM,GAAG,UAAU,aAAa,SAAS,SAAS;AAAA,MACtE,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,oBAAoB,GAAG,cAAc;AAAA,MAChE,MAAM,KAAK,UAAU,OAAO;AAAA,MAC5B,QAAQ,aAAa;AAAA,IACvB,CAAC;AAED,QAAI,QAAQ,WAAW,KAAK;AAC1B,YAAM,IAAI,mBAAmB,SAAS;AAAA,IACxC;AAEA,QAAI,CAAC,QAAQ,IAAI;AACf,YAAM,eAAe,OAAO;AAAA,IAC9B;AAEA,WAAO,QAAQ,KAAK;AAAA,EACtB;AAEA,QAAM,WAAW,cACb,MAAM,UAAU,QAAQ,aAAa,aAAa,MAAM,IACxD,MAAM,OAAO;AAEjB,QAAM,EAAE,UAAU,cAAc,UAAU,IAAqB;AAE/D,MAAI,cACF,SAAS,SAAS,IAAI,SAAS,SAAS,SAAS,CAAC,EAAG,KAAK;AAE5D,QAAM,WAAW;AAEjB,QAAM,iBAAiB,IAAI,IAAY,SAAS,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AAEhE,MAAI,UAAgC;AACpC,MAAI,WAAW;AAEf,QAAM,iBAAiB,eAAe;AAEtC,WAAS,cAAoB;AAC3B,QAAI,eAAe,QAAQ,eAAgB;AAC3C,UAAM,MAAM,CAAC,GAAG,cAAc;AAC9B,mBAAe,MAAM;AACrB,eAAW,MAAM,IAAI,MAAM,CAAC,YAAY,GAAG;AACzC,qBAAe,IAAI,EAAE;AAAA,IACvB;AAAA,EACF;AAEA,WAAS,UAAgB;AACvB,QAAI,SAAU;AAEd,UAAM,YAAY,cACd,GAAG,UAAU,aAAa,SAAS,YACnC,GAAG,UAAU,aAAa,SAAS,sBAAsB,mBAAmB,QAAQ,CAAC;AAEzF,cAAU;AAAA,MACR;AAAA,QACE,KAAK;AAAA,QACL,SAAS;AAAA,QACT,kBAAkB;AAAA,QAClB;AAAA,QACA,cAAc,CAAC,QAAQ;AAAA,QACvB;AAAA,MACF;AAAA,MACA;AAAA,QACE,QAAQ,MAAM;AACZ,cAAI,CAAC,SAAU,WAAU,eAAe,WAAW;AAAA,QACrD;AAAA,QACA,SAAS,CAAC,WAAW,MAAM,YAAY;AACrC,cAAI,SAAU;AAEd,cAAI,cAAc,WAAW;AAC3B,gBAAI;AACJ,gBAAI;AACF,wBAAU,KAAK,MAAM,IAAI;AAAA,YAC3B,QAAQ;AACN,wBAAU,QAAQ,IAAI,MAAM,6BAA6B,CAAC;AAC1D;AAAA,YACF;AAEA,gBAAI,SAAS;AACX,4BAAc;AAAA,YAChB;AAEA,gBAAI,eAAe,IAAI,QAAQ,EAAE,EAAG;AACpC,2BAAe,IAAI,QAAQ,EAAE;AAC7B,wBAAY;AAEZ,sBAAU,UAAU,OAAO;AAAA,UAC7B,WAAW,cAAc,UAAU;AAIjC,sBAAU,SAAS;AAAA,UACrB;AAAA,QACF;AAAA,QACA,SAAS,CAAC,QAAQ;AAChB,cAAI,CAAC,SAAU,WAAU,QAAQ,GAAG;AAAA,QACtC;AAAA,QACA,UAAU,MAAM;AACd,oBAAU,eAAe,QAAQ;AACjC,qBAAW;AAAA,QACb;AAAA,QACA,gBAAgB,MAAM;AACpB,cAAI,CAAC,SAAU,WAAU,eAAe,cAAc;AAAA,QACxD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,UAAQ;AAER,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,qBAAqB;AAAA,IACrB,iBAAiB;AAAA,IACjB,gBAAgB,kBAAkB;AAAA,IAElC,aAAa,OAAO,MAAM,MAAM,YAAY,mBAAmB;AAC7D,UAAI,SAAU,OAAM,IAAI,sBAAsB,cAAc;AAE5D,YAAM,UAAiC;AAAA,QACrC,WAAW,QAAQ;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,QACA,GAAI,iBAAiB,EAAE,iBAAiB,eAAe,IAAI,CAAC;AAAA,MAC9D;AAEA,YAAM,SAAS,YAA0C;AACvD,cAAM,MAAM,MAAM;AAAA,UAChB,GAAG,UAAU,aAAa,SAAS;AAAA,UACnC;AAAA,YACE,QAAQ;AAAA,YACR,SAAS,EAAE,gBAAgB,oBAAoB,GAAG,cAAc;AAAA,YAChE,MAAM,KAAK,UAAU,OAAO;AAAA,YAC5B,QAAQ,aAAa;AAAA,UACvB;AAAA,QACF;AAEA,YAAI,CAAC,IAAI,IAAI;AACX,gBAAM,eAAe,GAAG;AAAA,QAC1B;AAEA,eAAO,IAAI,KAAK;AAAA,MAClB;AAEA,YAAM,WAAW,cACb,MAAM,UAAU,QAAQ,aAAa,aAAa,MAAM,IACxD,MAAM,OAAO;AAEjB,qBAAe,IAAI,SAAS,EAAE;AAC9B,aAAO;AAAA,IACT;AAAA,IAEA,YAAY,OAAO,cAAsB;AAGvC,YAAM,SAAS,YAA2B;AACxC,cAAM,MAAM,MAAM,MAAM,GAAG,UAAU,aAAa,SAAS,SAAS;AAAA,UAClE,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,oBAAoB,GAAG,cAAc;AAAA,UAChE,MAAM,KAAK,UAAU;AAAA,YACnB,gBAAgB,QAAQ;AAAA,YACxB,YAAY;AAAA,UACd,CAAC;AAAA,QACH,CAAC;AACD,YAAI,CAAC,IAAI,IAAI;AACX,gBAAM,eAAe,GAAG;AAAA,QAC1B;AAAA,MACF;AAEA,UAAI,aAAa;AACf,YAAI;AACF,gBAAM,UAAU,QAAQ,sBAAsB;AAAA,QAChD,SAAS,KAAK;AAEZ,cAAI,eAAe,WAAW;AAC5B,sBAAU,QAAQ,GAAG;AAAA,UACvB;AAAA,QAEF;AAAA,MACF,OAAO;AACL,cAAM,OAAO;AAAA,MACf;AAAA,IACF;AAAA,IAEA,YAAY,MAAM;AAChB,iBAAW;AACX,mBAAa,MAAM;AACnB,eAAS,MAAM;AACf,gBAAU;AACV,gBAAU,eAAe,cAAc;AAAA,IACzC;AAAA,EACF;AACF;;;ACzQA,SAAS,oBAAoC;AAC3C,SAAO;AAAA,IACL,aAAa,MAAM;AAAA,IACnB,WAAW,MAAM,MAAM;AAAA,IAAC;AAAA,IACxB,eAAe,MAAM,QAAQ,QAAQ;AAAA,IACrC,SAAS,MAAM;AAAA,IAAC;AAAA,EAClB;AACF;AAEA,IAAI,kBAAuB;AAC3B,IAAI,kBAAkB;AAEtB,SAAS,aAAkB;AACzB,MAAI,gBAAiB,QAAO;AAC5B,oBAAkB;AAClB,MAAI;AACF,sBAAkB,UAAQ,iCAAiC;AAAA,EAC7D,QAAQ;AACN,sBAAkB;AAAA,EACpB;AACA,SAAO;AACT;AAEO,SAAS,uBAAuC;AACrD,QAAM,UAAU,WAAW;AAC3B,MAAI,CAAC,QAAS,QAAO,kBAAkB;AAEvC,QAAM,gBAAgB,QAAQ,WAAW;AAEzC,MAAI,YAAY;AAChB,QAAM,YAAY,oBAAI,IAAkC;AACxD,MAAI,qBAA0C;AAE9C,uBAAqB,cAAc;AAAA,IACjC,CAAC,UAA2C;AAC1C,YAAM,OAAO,MAAM,gBAAgB;AACnC,UAAI,SAAS,UAAW;AACxB,kBAAY;AACZ,iBAAW,MAAM,WAAW;AAC1B,WAAG,SAAS;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,aAAa,MAAM;AAAA,IAEnB,WAAW,CAAC,OAAO;AACjB,gBAAU,IAAI,EAAE;AAChB,aAAO,MAAM;AACX,kBAAU,OAAO,EAAE;AAAA,MACrB;AAAA,IACF;AAAA,IAEA,eAAe,CAAC,WAAY;AAC1B,UAAI,UAAW,QAAO,QAAQ,QAAQ;AAEtC,aAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,YAAI,QAAQ,SAAS;AACnB,iBAAO,OAAO,UAAU,IAAI,aAAa,WAAW,YAAY,CAAC;AACjE;AAAA,QACF;AAEA,cAAM,QAAQ,MAAY;AACxB,oBAAU,OAAO,OAAO;AACxB,kBAAQ,oBAAoB,SAAS,OAAO;AAAA,QAC9C;AAEA,cAAM,UAAU,CAAC,aAA4B;AAC3C,cAAI,UAAU;AACZ,kBAAM;AACN,oBAAQ;AAAA,UACV;AAAA,QACF;AAEA,cAAM,UAAU,MAAY;AAC1B,gBAAM;AACN,iBAAO,OAAQ,UAAU,IAAI,aAAa,WAAW,YAAY,CAAC;AAAA,QACpE;AAEA,kBAAU,IAAI,OAAO;AACrB,gBAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAAA,MAC3D,CAAC;AAAA,IACH;AAAA,IAEA,SAAS,MAAM;AACb,gBAAU,MAAM;AAChB,2BAAqB;AACrB,2BAAqB;AAAA,IACvB;AAAA,EACF;AACF;;;ACvFA,IAAI;AAEJ,SAAS,WAAW,MAAmB;AACrC,MAAI;AACF,WAAO,UAAQ,IAAI;AAAA,EACrB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,kBAAkB,KAAwB;AACjD,QAAM,OAAO,IAAI,QAAQ,IAAI,SAAS,QAAQ;AAC9C,QAAM,WAAW,IAAI,KAAK,EAAE,IAAI,cAAc,CAAC;AAC/C,SAAO;AAAA,IACL,SAAS,CAAC,QAAQ,QAAQ,QAAQ,SAAS,UAAU,GAAG,KAAK,IAAI;AAAA,IACjE,SAAS,CAAC,KAAK,UAAU;AAAE,eAAS,IAAI,KAAK,KAAK;AAAG,aAAO,QAAQ,QAAQ;AAAA,IAAG;AAAA,IAC/E,YAAY,CAAC,QAAQ;AAAE,eAAS,OAAO,GAAG;AAAG,aAAO,QAAQ,QAAQ;AAAA,IAAG;AAAA,EACzE;AACF;AAEA,SAAS,8BAA8B,KAAwB;AAC7D,QAAM,UAAU,IAAI,WAAW;AAC/B,SAAO;AAAA,IACL,SAAS,CAAC,QAAQ,QAAQ,QAAQ,GAAG;AAAA,IACrC,SAAS,CAAC,KAAK,UAAU,QAAQ,QAAQ,KAAK,KAAK;AAAA,IACnD,YAAY,CAAC,QAAQ,QAAQ,WAAW,GAAG;AAAA,EAC7C;AACF;AAkBO,SAAS,qBAA0C;AACxD,MAAI,oBAAoB,OAAW,QAAO,iBAAiB,WAAW;AAGtE,QAAM,OAAO,WAAW,mBAAmB;AAC3C,MAAI,MAAM;AACR,QAAI;AACF,YAAM,UAAU,kBAAkB,IAAI;AACtC,wBAAkB,EAAE,MAAM,QAAQ,QAAQ;AAC1C,aAAO;AAAA,IACT,QAAQ;AAAA,IAER;AAAA,EACF;AAGA,QAAM,eAAe,WAAW,2CAA2C;AAC3E,MAAI,cAAc;AAChB,UAAM,UAAU,8BAA8B,YAAY;AAC1D,sBAAkB,EAAE,MAAM,iBAAiB,QAAQ;AACnD,WAAO;AAAA,EACT;AAEA,oBAAkB;AAClB,SAAO;AACT;AAMO,SAAS,4BAAiD;AAC/D,QAAM,MAAM,WAAW,2CAA2C;AAClE,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,8BAA8B,GAAG;AAC1C;AAwCO,IAAM,eAAN,MAAmB;AAAA,EAMxB,YAA6B,QAA4B;AAA5B;AAC3B,SAAK,aAAa,eAAe,OAAO,SAAS;AAEjD,SAAK,eAAe,OAAO,eAAe,UAAU,CAAC,cAAc;AACjE,UAAI,UAAW,MAAK,MAAM;AAAA,IAC5B,CAAC;AAAA,EACH;AAAA,EAXQ,UAAwB,CAAC;AAAA,EACzB,WAAW;AAAA,EACX,eAAoC;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBjB,MAAM,QACJ,eACe;AACf,QAAI,CAAC,KAAK,OAAO,QAAS;AAE1B,QAAI;AACF,YAAM,MAAM,MAAM,KAAK,OAAO,QAAQ,QAAQ,KAAK,UAAU;AAC7D,UAAI,CAAC,IAAK;AAEV,YAAM,YAA8B,KAAK,MAAM,GAAG;AAClD,iBAAW,SAAS,WAAW;AAC7B,cAAM,SAAS,cAAc,MAAM,YAAY;AAC/C,YAAI,CAAC,OAAQ;AAEb,cAAM,QAAQ,IAAI,gBAAgB;AAClC,aAAK,QAAQ,KAAK;AAAA,UAChB,cAAc,MAAM;AAAA,UACpB;AAAA,UACA,QAAQ;AAAA,UACR,YAAY,MAAM;AAAA,UAClB;AAAA;AAAA,UAEA,SAAS,MAAM;AAAA,UAAC;AAAA,UAChB,QAAQ,MAAM;AAAA,UAAC;AAAA,QACjB,CAAC;AAAA,MACH;AAEA,UAAI,KAAK,QAAQ,SAAS,GAAG;AAC3B,aAAK,OAAO,iBAAiB;AAC7B,aAAK,MAAM;AAAA,MACb;AAAA,IACF,QAAQ;AACN,WAAK,OAAO,UAAU,IAAI,MAAM,8CAA8C,CAAC;AAAA,IACjF;AAAA,EACF;AAAA,EAEA,IAAI,eAAuB;AACzB,WAAO,KAAK,QAAQ;AAAA,EACtB;AAAA,EAEA,SAA0B;AACxB,WAAO,KAAK,QAAQ,IAAI,CAAC,OAAO;AAAA,MAC9B,cAAc,EAAE;AAAA,MAChB,QAAQ,EAAE;AAAA,MACV,OAAO,EAAE;AAAA,MACT,YAAY,EAAE;AAAA,IAChB,EAAE;AAAA,EACJ;AAAA,EAEA,UAAU,cAAiD;AACzD,UAAM,QAAQ,KAAK,QAAQ,KAAK,CAAC,MAAM,EAAE,iBAAiB,YAAY;AACtE,QAAI,CAAC,MAAO,QAAO;AACnB,WAAO;AAAA,MACL,cAAc,MAAM;AAAA,MACpB,QAAQ,MAAM;AAAA,MACd,OAAO,MAAM;AAAA,MACb,YAAY,MAAM;AAAA,IACpB;AAAA,EACF;AAAA,EAEA,QAAQ,QAAgB,cAAoD;AAC1E,QAAI,KAAK,QAAQ,UAAU,KAAK,OAAO,SAAS;AAC9C,YAAM,IAAI,eAAe,KAAK,OAAO,OAAO;AAAA,IAC9C;AAEA,UAAM,QAAQ,IAAI,gBAAgB;AAElC,WAAO,IAAI,QAA6B,CAAC,SAAS,WAAW;AAC3D,YAAM,QAAoB;AAAA,QACxB;AAAA,QACA;AAAA,QACA,QAAQ;AAAA,QACR,YAAY;AAAA,QACZ;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,WAAK,QAAQ,KAAK,KAAK;AACvB,WAAK,OAAO,iBAAiB;AAC7B,WAAK,QAAQ;AACb,WAAK,MAAM;AAAA,IACb,CAAC;AAAA,EACH;AAAA,EAEA,OAAO,cAA4B;AACjC,UAAM,MAAM,KAAK,QAAQ,UAAU,CAAC,MAAM,EAAE,iBAAiB,YAAY;AACzE,QAAI,QAAQ,GAAI;AAEhB,UAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,UAAM,MAAM,MAAM;AAClB,UAAM,OAAO,IAAI,aAAa,aAAa,YAAY,CAAC;AACxD,SAAK,QAAQ,OAAO,KAAK,CAAC;AAC1B,SAAK,OAAO,iBAAiB;AAC7B,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,MAAM,cAA4B;AAChC,UAAM,QAAQ,KAAK,QAAQ,KAAK,CAAC,MAAM,EAAE,iBAAiB,YAAY;AACtE,QAAI,CAAC,SAAS,MAAM,WAAW,SAAU;AAEzC,UAAM,SAAS;AACf,UAAM,QAAQ;AACd,UAAM,QAAQ,IAAI,gBAAgB;AAClC,SAAK,OAAO,iBAAiB;AAC7B,SAAK,MAAM;AAAA,EACb;AAAA,EAEA,UAAgB;AACd,SAAK,eAAe;AACpB,SAAK,eAAe;AAEpB,eAAW,SAAS,KAAK,SAAS;AAChC,YAAM,MAAM,MAAM;AAClB,YAAM,OAAO,IAAI,aAAa,kBAAkB,YAAY,CAAC;AAAA,IAC/D;AACA,SAAK,UAAU,CAAC;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,QAAuB;AAC3B,QAAI,KAAK,SAAU;AACnB,QAAI,CAAC,KAAK,OAAO,eAAe,YAAY,EAAG;AAE/C,SAAK,WAAW;AAChB,QAAI,kBAAkB;AAEtB,QAAI;AACF,aAAO,KAAK,QAAQ,SAAS,GAAG;AAI9B,cAAM,QAAQ,KAAK,QAAQ,KAAK,CAAC,MAAM,EAAE,WAAW,QAAQ;AAC5D,YAAI,CAAC,MAAO;AACZ,YAAI,CAAC,KAAK,OAAO,eAAe,YAAY,EAAG;AAE/C,cAAM,SAAS;AACf,aAAK,OAAO,iBAAiB;AAE7B,YAAI;AACF,gBAAM,SAAS,MAAM;AAAA,YACnB,MAAM;AAAA,YACN,KAAK,OAAO;AAAA,YACZ,MAAM,MAAM;AAAA,UACd;AAEA,gBAAM,QAAQ,MAAM;AACpB,gBAAM,MAAM,KAAK,QAAQ,QAAQ,KAAK;AACtC,cAAI,QAAQ,GAAI,MAAK,QAAQ,OAAO,KAAK,CAAC;AAC1C,eAAK,OAAO,iBAAiB;AAC7B,eAAK,QAAQ;AAAA,QACf,SAAS,KAAK;AACZ,cAAI,MAAM,MAAM,OAAO,QAAS;AAKhC,cAAI,eAAe,uBAAuB;AACxC,kBAAM,SAAS;AACf,iBAAK,OAAO,iBAAiB;AAC7B,8BAAkB;AAClB;AAAA,UACF;AAEA,gBAAM,SAAS;AACf,gBAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAChE,gBAAM;AACN,gBAAM,OAAO,MAAM,KAAK;AACxB,eAAK,OAAO,iBAAiB;AAC7B,eAAK,QAAQ;AAAA,QAGf;AAAA,MACF;AAAA,IACF,UAAE;AACA,WAAK,WAAW;AAGhB,UACE,CAAC,mBACD,KAAK,QAAQ,KAAK,CAAC,MAAM,EAAE,WAAW,QAAQ,KAC9C,KAAK,OAAO,eAAe,YAAY,GACvC;AACA,uBAAe,MAAM,KAAK,MAAM,CAAC;AAAA,MACnC;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,UAAgB;AACtB,QAAI,CAAC,KAAK,OAAO,QAAS;AAE1B,UAAM,OAAyB,KAAK,QAAQ,IAAI,CAAC,OAAO;AAAA,MACtD,cAAc,EAAE;AAAA,MAChB,YAAY,EAAE;AAAA,IAChB,EAAE;AAEF,UAAM,QAAQ,CAAC,QAAiB;AAC9B,WAAK,OAAO;AAAA,QACV,eAAe,QAAQ,MAAM,IAAI,MAAM,4BAA4B;AAAA,MACrE;AAAA,IACF;AAEA,QAAI,KAAK,WAAW,GAAG;AACrB,WAAK,OAAO,QAAQ,WAAW,KAAK,UAAU,EAAE,MAAM,KAAK;AAAA,IAC7D,OAAO;AACL,WAAK,OAAO,QACT,QAAQ,KAAK,YAAY,KAAK,UAAU,IAAI,CAAC,EAC7C,MAAM,KAAK;AAAA,IAChB;AAAA,EACF;AACF;;;AP5VA,IAAM,8BAA8B;AACpC,IAAM,yBAAyB;AAG/B,IAAM,gBAAgB,oBAAI,IAAuD;AAS1E,SAAS,QACd,EAAE,QAAQ,WAAW,SAAS,UAAU,GACtB;AAClB,QAAM,CAAC,UAAU,WAAW,IAAI,SAAuB,CAAC,CAAC;AACzD,QAAM,CAAC,cAAc,eAAe,IAAI,SAA2B,CAAC,CAAC;AACrE,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAqB,YAAY;AAC7D,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAuB,IAAI;AACrD,QAAM,CAAC,iBAAiB,kBAAkB,IAAI,SAA0B,CAAC,CAAC;AAE1E,QAAM,aAAa,OAA8B,IAAI;AACrD,QAAM,cAAc,OAAO,KAAK;AAChC,QAAM,cAAc,OAAO,QAAQ;AACnC,cAAY,UAAU;AACtB,QAAM,YAAY,OAAO,MAAM;AAC/B,YAAU,UAAU;AACpB,QAAM,cAAc,OAAuB,SAAS,YAAY;AAChE,QAAM,qBAAqB,OAA6C,IAAI;AAC5E,QAAM,aAAa,OAAO,OAAO;AACjC,aAAW,UAAU;AACrB,QAAM,YAAY,OAAO,MAAM;AAC/B,YAAU,UAAU;AACpB,QAAM,eAAe,OAAO,SAAS;AACrC,eAAa,UAAU;AACvB,QAAM,cAAc,OAAO,KAAK;AAChC,QAAM,uBAAuB,OAAO,oBAAI,IAAY,CAAC;AACrD,QAAM,CAAC,SAAS,UAAU,IAAI,SAAgC,IAAI;AAClE,QAAM,CAAC,cAAc,eAAe,IAAI,SAAS,KAAK;AACtD,QAAM,aAAa,OAA8B,IAAI;AACrD,aAAW,UAAU;AACrB,QAAM,WAAW,OAA4B,IAAI;AAEjD,QAAM,oBAAoB,OAAO,eAAe;AAChD,QAAM,eACJ,sBACC,OAAO,OAAO,eAAe,WAAW,OAAO,WAAW,iBAAiB,QAAQ;AACtF,QAAM,cAAc,mBAAmB,OAAO,UAAU;AACxD,QAAM,gBACH,qBAAqB,OAAO,cAAc,OAAO,OAAO,eAAe,WACpE,OAAO,WAAW,eAClB,WAAc;AAEpB,QAAM,oBACJ,OAAO,sBAAsB,SAAS,OAAO,YAAY,8BAA8B;AAGzF,QAAM,kBACJ,OAAO,OAAO,eAAe,WAAW,OAAO,WAAW,iBAAiB;AAI7E,YAAU,MAAM;AACd,QAAI,CAAC,mBAAmB;AACtB,iBAAW,IAAI;AACf,sBAAgB,IAAI;AACpB;AAAA,IACF;AACA,QAAI,iBAAiB;AACnB,iBAAW,eAAe;AAC1B,sBAAgB,IAAI;AACpB;AAAA,IACF;AACA,QAAI;AACF,YAAM,IAAI,qBAAqB;AAC/B,iBAAW,CAAC;AACZ,sBAAgB,IAAI;AACpB,aAAO,MAAM;AACX,UAAE,QAAQ;AACV,mBAAW,IAAI;AAAA,MACjB;AAAA,IACF,QAAQ;AAGN,iBAAW,IAAI;AACf,sBAAgB,IAAI;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,mBAAmB,eAAe,CAAC;AAEvC,QAAM,YAAiC;AAAA,IACrC,WAAW,CAAC,YAAY;AACtB,UAAI,YAAY,QAAS;AACzB,UAAI,sBAAqC;AACzC,kBAAY,CAAC,SAAuB;AAClC,YAAI,qBAAqB,QAAQ,SAAS,GAAG;AAC3C,iBAAO,CAAC,GAAG,MAAM,OAAO;AAAA,QAC1B;AAEA,cAAM,gBAAgB,KAAK;AAAA,UACzB,CAAC,MACC,qBAAqB,QAAQ,IAAI,EAAE,EAAE,KACrC,EAAE,cAAc,QAAQ,aACxB,EAAE,SAAS,QAAQ,QACnB,EAAE,SAAS,QAAQ;AAAA,QACvB;AACA,YAAI,kBAAkB,IAAI;AACxB,gCAAsB,KAAK,aAAa,EAAG;AAC3C,+BAAqB,QAAQ,OAAO,mBAAmB;AACvD,gBAAM,OAAO,CAAC,GAAG,IAAI;AACrB,eAAK,aAAa,IAAI;AACtB,iBAAO;AAAA,QACT;AACA,eAAO,CAAC,GAAG,MAAM,OAAO;AAAA,MAC1B,CAAC;AAGD,UAAI,qBAAqB;AACvB,cAAM,IAAI,SAAS,SAAS,UAAU,mBAAmB;AACzD,YAAI,KAAK,EAAE,WAAW,WAAW;AAC/B,mBAAS,SAAS,OAAO,mBAAmB;AAAA,QAC9C;AAAA,MACF;AACA,mBAAa,UAAU,OAAO;AAAA,IAChC;AAAA,IACA,gBAAgB,CAAC,eAAe;AAC9B,UAAI,YAAY,QAAS;AACzB,gBAAU,UAAU;AACpB,UAAI,eAAe,aAAa;AAC9B,iBAAS,IAAI;AAAA,MACf;AAAA,IACF;AAAA,IACA,SAAS,CAAC,QAAQ;AAChB,UAAI,YAAY,QAAS;AACzB,eAAS,GAAG;AAAA,IACd;AAAA,IACA,UAAU,MAAM;AACd,UAAI,YAAY,QAAS;AACzB,mBAAa;AAAA,IACf;AAAA,EACF;AAEA,iBAAe,eAA8B;AAC3C,QAAI,YAAY,QAAS;AACzB,gBAAY,UAAU;AAEtB,UAAM,WAAW,WAAW;AAC5B,QAAI,UAAU;AACZ,eAAS,WAAW;AACpB,iBAAW,UAAU;AAAA,IACvB;AAEA,QAAI;AACF,YAAM,UAAU,MAAM;AAAA,QACpB,UAAU;AAAA,QACV;AAAA,QACA,WAAW;AAAA,QACX;AAAA,QACA,WAAW,WAAW;AAAA,MACxB;AAEA,UAAI,YAAY,SAAS;AACvB,gBAAQ,WAAW;AACnB;AAAA,MACF;AAEA,iBAAW,UAAU;AACrB,sBAAgB,QAAQ,mBAAmB;AAG3C,eAAS,SAAS,MAAM;AAGxB,UAAI,qBAAqB,QAAQ,OAAO,GAAG;AACzC,cAAM,aAAa,qBAAqB;AACxC,oBAAY,CAAC,SAAS;AACpB,gBAAM,cAAc,KAAK,OAAO,CAAC,MAAM,WAAW,IAAI,EAAE,EAAE,CAAC;AAC3D,iBAAO,CAAC,GAAG,QAAQ,iBAAiB,GAAG,WAAW;AAAA,QACpD,CAAC;AAAA,MACH,OAAO;AACL,oBAAY,QAAQ,eAAe;AAAA,MACrC;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,YAAY,QAAS;AAEzB,UAAI,eAAe,oBAAoB;AACrC,kBAAU,QAAQ;AAClB,iBAAS,GAAG;AACZ;AAAA,MACF;AAEA,gBAAU,OAAO;AACjB,eAAS,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AAAA,IAC9D,UAAE;AACA,kBAAY,UAAU;AAAA,IACxB;AAAA,EACF;AAGA,YAAU,MAAM;AACd,gBAAY,UAAU;AAEtB,QAAI,CAAC,aAAc;AACnB,iBAAa;AAEb,WAAO,MAAM;AACX,kBAAY,UAAU;AACtB,UAAI,UAAU,YAAY,eAAe,WAAW,SAAS;AAC3D,cAAM,UAAU,YAAY,QAAQ,YAAY,QAAQ,SAAS,CAAC;AAClE,YAAI,SAAS;AACX,qBAAW,QAAQ,WAAW,QAAQ,EAAE,EAAE,MAAM,MAAM;AAAA,UAAC,CAAC;AAAA,QAC1D;AAAA,MACF;AACA,UAAI,mBAAmB,SAAS;AAC9B,qBAAa,mBAAmB,OAAO;AACvC,2BAAmB,UAAU;AAAA,MAC/B;AACA,iBAAW,SAAS,WAAW;AAC/B,iBAAW,UAAU;AAAA,IACvB;AAAA,EACF,GAAG,CAAC,WAAW,YAAY,CAAC;AAG5B,YAAU,MAAM;AACd,QAAI,CAAC,gBAAgB,CAAC,WAAW,CAAC,YAAa;AAE/C,QAAI,QAAQ,cAAc,IAAI,SAAS;AACvC,QAAI,CAAC,OAAO;AACV,YAAM,QAAQ,IAAI,aAAa;AAAA,QAC7B;AAAA,QACA,SAAS;AAAA,QACT;AAAA,QACA,gBAAgB;AAAA,QAChB,SACE,OAAO,OAAO,eAAe,WACzB,OAAO,WAAW,eAClB;AAAA,QACN,SAAS,CAAC,QAAQ;AAChB,cAAI,CAAC,YAAY,QAAS,UAAS,GAAG;AAAA,QACxC;AAAA,QACA,gBAAgB,MAAM;AACpB,cAAI,CAAC,YAAY,SAAS;AACxB,+BAAmB,SAAS,SAAS,OAAO,KAAK,CAAC,CAAC;AAAA,UACrD;AAAA,QACF;AAAA,MACF,CAAC;AACD,cAAQ,EAAE,OAAO,UAAU,EAAE;AAC7B,oBAAc,IAAI,WAAW,KAAK;AAAA,IACpC;AACA,UAAM;AACN,aAAS,UAAU,MAAM;AAEzB,WAAO,MAAM;AACX,YAAM,IAAI,cAAc,IAAI,SAAS;AACrC,UAAI,GAAG;AACL,UAAE;AACF,YAAI,EAAE,YAAY,GAAG;AACnB,YAAE,MAAM,QAAQ;AAChB,wBAAc,OAAO,SAAS;AAAA,QAChC;AAAA,MACF;AACA,eAAS,UAAU;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,WAAW,cAAc,OAAO,CAAC;AAGrC,YAAU,MAAM;AACd,QAAI,CAAC,WAAW,CAAC,kBAAmB;AAEpC,UAAM,QAAQ,QAAQ,UAAU,CAAC,cAAuB;AACtD,UAAI,aAAa,UAAU,YAAY,WAAW,CAAC,YAAY,SAAS;AACtE,qBAAa;AAAA,MACf;AAAA,IACF,CAAC;AAED,WAAO;AAAA,EACT,GAAG,CAAC,WAAW,mBAAmB,OAAO,CAAC;AAG1C,YAAU,MAAM;AACd,aAAS,kBAAwB;AAC/B,iBAAW,SAAS,WAAW;AAC/B,iBAAW,UAAU;AACrB,gBAAU,cAAc;AAAA,IAC1B;AAEA,UAAM,eAAe,SAAS,iBAAiB,UAAU,CAAC,iBAAiB;AACzE,YAAM,OAAO,YAAY;AACzB,kBAAY,UAAU;AAEtB,UAAI,CAAC,WAAW,WAAW,iBAAiB,SAAU;AAEtD,YAAM,iBACJ,iBAAiB,gBAChB,SAAS,OAAO,SAAS,iBAAiB;AAE7C,UAAI,iBAAiB,UAAU;AAC7B,YAAI,mBAAmB,SAAS;AAC9B,uBAAa,mBAAmB,OAAO;AACvC,6BAAmB,UAAU;AAC7B;AAAA,QACF;AAEA,YAAI,KAAK,MAAM,qBAAqB,KAAK,CAAC,WAAW,SAAS;AAC5D,uBAAa;AAAA,QACf;AAAA,MACF,WAAW,gBAAgB;AACzB,YAAI,mBAAmB,QAAS;AAEhC,YAAI,sBAAsB,GAAG;AAC3B,0BAAgB;AAAA,QAClB,OAAO;AACL,6BAAmB,UAAU,WAAW,MAAM;AAC5C,+BAAmB,UAAU;AAC7B,4BAAgB;AAAA,UAClB,GAAG,iBAAiB;AAAA,QACtB;AAAA,MACF;AAAA,IACF,CAAC;AAED,WAAO,MAAM;AACX,mBAAa,OAAO;AACpB,UAAI,mBAAmB,SAAS;AAC9B,qBAAa,mBAAmB,OAAO;AACvC,2BAAmB,UAAU;AAAA,MAC/B;AAAA,IACF;AAAA,EACF,GAAG,CAAC,WAAW,iBAAiB,CAAC;AAEjC,QAAM,cAAc;AAAA,IAClB,OAAO,MAAwB,MAAc,eAAoE;AAC/G,YAAM,UAAU,WAAW;AAC3B,UAAI,CAAC,QAAS,OAAM,IAAI,sBAAsB,UAAU,OAAO;AAE/D,YAAM,aAAa,UAAU,QAAQ,mBAAmB;AAExD,YAAM,aAAa,cAAc,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAErF,UAAI,YAAY;AACd,6BAAqB,QAAQ,IAAI,UAAU;AAC3C,cAAM,iBAA6B;AAAA,UACjC,IAAI;AAAA,UACJ,YAAY;AAAA,UACZ,WAAW,WAAW,QAAQ;AAAA,UAC9B,aAAa,WAAW,QAAQ;AAAA,UAChC;AAAA,UACA;AAAA,UACA,YAAa,cAAc,CAAC;AAAA,UAC5B,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,QACrC;AACA,oBAAY,CAAC,SAAS,CAAC,GAAG,MAAM,cAAc,CAAC;AAAA,MACjD;AAEA,YAAM,SAAS,MAAM;AACnB,cAAM,IAAI,WAAW;AACrB,YAAI,CAAC,EAAG,OAAM,IAAI,sBAAsB,UAAU,OAAO;AACzD,eAAO,EAAE,YAAY,MAAM,MAAM,YAAY,UAAU;AAAA,MACzD;AAEA,YAAM,gBAAgB,CAAC,aAAwC;AAC7D,YAAI,YAAY;AACd,+BAAqB,QAAQ,OAAO,UAAU;AAC9C,sBAAY,CAAC,SAAS;AACpB,kBAAM,eAAe,KAAK,KAAK,CAAC,MAAM,EAAE,OAAO,UAAU;AACzD,gBAAI,CAAC,aAAc,QAAO;AAC1B,mBAAO,KAAK;AAAA,cAAI,CAAC,MACf,EAAE,OAAO,aACL,EAAE,GAAG,GAAG,IAAI,SAAS,IAAI,YAAY,SAAS,WAAW,IACzD;AAAA,YACN;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAEA,YAAM,cAAc,CAAC,SAAwB;AAC3C,YAAI,YAAY;AACd,+BAAqB,QAAQ,OAAO,UAAU;AAC9C,sBAAY,CAAC,SAAS,KAAK,OAAO,CAAC,MAAM,EAAE,OAAO,UAAU,CAAC;AAAA,QAC/D;AAAA,MACF;AAGA,UAAI,SAAS,SAAS;AACpB,YAAI;AACF,gBAAM,WAAW,MAAM,SAAS,QAAQ,QAAQ,QAAQ,UAAU;AAClE,wBAAc,QAAQ;AACtB,iBAAO;AAAA,QACT,SAAS,KAAK;AACZ,cAAI,eAAe,gBAAgB;AACjC,wBAAY,GAAG;AACf,kBAAM;AAAA,UACR;AACA,cAAI,eAAe,uBAAuB,CAAC,iBAAiB,GAAG,GAAG;AAGhE,gBAAI,cAAc,eAAe,qBAAqB;AAAA,YAKtD,OAAO;AACL,0BAAY,GAAG;AAAA,YACjB;AACA,kBAAM;AAAA,UACR;AACA,sBAAY,GAAG;AACf,gBAAM;AAAA,QACR;AAAA,MACF;AAGA,UAAI;AACF,cAAM,WAAW,MAAM,OAAO;AAC9B,sBAAc,QAAQ;AACtB,eAAO;AAAA,MACT,SAAS,KAAK;AACZ,oBAAY,GAAG;AACf,cAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,CAAC,SAAS;AAAA,EACZ;AAEA,QAAM,gBAAgB;AAAA,IACpB,CAAC,iBAAyB;AACxB,eAAS,SAAS,OAAO,YAAY;AACrC,2BAAqB,QAAQ,OAAO,YAAY;AAChD,kBAAY,CAAC,SAAS,KAAK,OAAO,CAAC,MAAM,EAAE,OAAO,YAAY,CAAC;AAAA,IACjE;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,eAAe;AAAA,IACnB,CAAC,iBAAyB;AACxB,eAAS,SAAS,MAAM,YAAY;AAAA,IACtC;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,aAAa,YAAY,MAAM;AACnC,eAAW,SAAS,WAAW;AAC/B,eAAW,UAAU;AACrB,cAAU,cAAc;AAAA,EAC1B,GAAG,CAAC,CAAC;AAEL,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AQzdA,SAAS,aAAAC,YAAW,UAAAC,SAAQ,YAAAC,WAAU,eAAAC,oBAAmB;AACzD,SAAS,YAAAC,WAAU,YAAAC,iBAAqC;AAOxD,IAAMC,+BAA8B;AACpC,IAAM,uBAAuB,CAAC,mBAAmB,iBAAiB,cAAc;AAgBzE,SAAS,UAAU,SAA4C;AACpE,QAAM,EAAE,QAAQ,WAAW,eAAe,UAAU,KAAK,IAAI;AAE7D,QAAM,CAAC,aAAa,cAAc,IAAIC,UAAS,CAAC;AAChD,QAAM,CAAC,eAAe,gBAAgB,IAAIA,UAAwB,IAAI;AACtE,QAAM,CAAC,OAAO,QAAQ,IAAIA,UAAuB,IAAI;AAErD,QAAM,UAAUC,QAA6B,IAAI;AACjD,QAAM,YAAYA,QAAO,MAAM;AAC/B,YAAU,UAAU;AACpB,QAAM,cAAcA,QAAuBC,UAAS,YAAY;AAChE,QAAM,qBAAqBD,QAA6C,IAAI;AAE5E,QAAM,oBACJ,OAAO,sBAAsBE,UAAS,OAAO,YAAYJ,+BAA8B;AAEzF,QAAM,CAAC,cAAc,eAAe,IAAIC,UAAS,KAAK;AACtD,QAAM,aAAaC,QAA8B,IAAI;AACrD,QAAM,oBAAoB,OAAO,eAAe;AAChD,QAAM,kBACJ,OAAO,OAAO,eAAe,WAAW,OAAO,WAAW,iBAAiB;AAE7E,EAAAG,WAAU,MAAM;AACd,QAAI,CAAC,mBAAmB;AACtB,iBAAW,UAAU;AACrB,sBAAgB,IAAI;AACpB;AAAA,IACF;AACA,QAAI,iBAAiB;AACnB,iBAAW,UAAU;AACrB,sBAAgB,IAAI;AACpB;AAAA,IACF;AACA,QAAI;AACF,YAAM,IAAI,qBAAqB;AAC/B,iBAAW,UAAU;AACrB,sBAAgB,IAAI;AACpB,aAAO,MAAM;AACX,UAAE,QAAQ;AACV,mBAAW,UAAU;AAAA,MACvB;AAAA,IACF,QAAQ;AACN,iBAAW,UAAU;AACrB,sBAAgB,IAAI;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,mBAAmB,eAAe,CAAC;AAEvC,QAAM,UAAUC,aAAY,MAAM;AAChC,YAAQ,SAAS,MAAM;AACvB,YAAQ,UAAU;AAElB,UAAM,aAAa,iBAAiB,UAAU,QAAQ,UAAU,SAAS;AACzE,UAAM,gBAAgB,UAAU,QAAQ,WAAW,CAAC;AACpD,UAAM,MAAM,GAAG,UAAU,aAAa,SAAS,0BAA0B,mBAAmB,aAAa,CAAC;AAE1G,YAAQ,UAAU;AAAA,MAChB;AAAA,QACE;AAAA,QACA,SAAS;AAAA,QACT,kBAAkB,UAAU,QAAQ;AAAA,QACpC,cAAc;AAAA,QACd,gBAAgB,WAAW,WAAW;AAAA,MACxC;AAAA,MACA;AAAA,QACE,QAAQ,MAAM;AACZ,mBAAS,IAAI;AAAA,QACf;AAAA,QACA,SAAS,CAAC,WAAW,SAAS;AAC5B,cAAI;AACF,gBAAI,cAAc,mBAAmB;AACnC,oBAAM,WAAgC,KAAK,MAAM,IAAI;AACrD,6BAAe,SAAS,YAAY;AACpC,+BAAiB,SAAS,eAAe;AAAA,YAC3C,WAAW,cAAc,iBAAiB;AACxC,oBAAM,SAAuC,KAAK,MAAM,IAAI;AAC5D,6BAAe,CAAC,SAAS,OAAO,CAAC;AACjC,+BAAiB,OAAO,UAAU;AAAA,YACpC,WAAW,cAAc,gBAAgB;AACvC,oBAAM,QAAsD,KAAK,MAAM,IAAI;AAC3E,6BAAe,MAAM,YAAY;AAAA,YACnC;AAAA,UACF,QAAQ;AACN,qBAAS,IAAI,MAAM,kCAAkC,CAAC;AAAA,UACxD;AAAA,QACF;AAAA,QACA,SAAS,CAAC,QAAQ;AAChB,mBAAS,GAAG;AAAA,QACd;AAAA,QACA,UAAU,MAAM;AACd,kBAAQ,UAAU;AAAA,QACpB;AAAA,MACF;AAAA,IACF;AAAA,EACF,GAAG,CAAC,WAAW,aAAa,CAAC;AAE7B,QAAM,aAAaA,aAAY,MAAM;AACnC,YAAQ,SAAS,MAAM;AACvB,YAAQ,UAAU;AAAA,EACpB,GAAG,CAAC,CAAC;AAEL,EAAAD,WAAU,MAAM;AACd,mBAAe,CAAC;AAChB,qBAAiB,IAAI;AACrB,aAAS,IAAI;AAEb,QAAI,CAAC,SAAS;AACZ,iBAAW;AACX;AAAA,IACF;AAGA,QAAI,CAAC,aAAc;AAEnB,YAAQ;AAER,WAAO,MAAM;AACX,iBAAW;AACX,UAAI,mBAAmB,SAAS;AAC9B,qBAAa,mBAAmB,OAAO;AACvC,2BAAmB,UAAU;AAAA,MAC/B;AAAA,IACF;AAAA,EACF,GAAG,CAAC,WAAW,eAAe,SAAS,cAAc,SAAS,UAAU,CAAC;AAEzE,EAAAA,WAAU,MAAM;AACd,QAAI,CAAC,QAAS;AAEd,UAAM,eAAeF,UAAS,iBAAiB,UAAU,CAAC,iBAAiB;AACzE,YAAM,OAAO,YAAY;AACzB,kBAAY,UAAU;AAEtB,UAAI,CAAC,QAAQ,WAAW,iBAAiB,SAAU;AAEnD,YAAM,iBACJ,iBAAiB,gBAChBC,UAAS,OAAO,SAAS,iBAAiB;AAE7C,UAAI,iBAAiB,UAAU;AAC7B,YAAI,mBAAmB,SAAS;AAC9B,uBAAa,mBAAmB,OAAO;AACvC,6BAAmB,UAAU;AAC7B;AAAA,QACF;AAEA,YAAI,KAAK,MAAM,qBAAqB,KAAK,CAAC,QAAQ,SAAS;AACzD,kBAAQ;AAAA,QACV;AAAA,MACF,WAAW,gBAAgB;AACzB,YAAI,mBAAmB,QAAS;AAEhC,YAAI,sBAAsB,GAAG;AAC3B,qBAAW;AAAA,QACb,OAAO;AACL,6BAAmB,UAAU,WAAW,MAAM;AAC5C,+BAAmB,UAAU;AAC7B,uBAAW;AAAA,UACb,GAAG,iBAAiB;AAAA,QACtB;AAAA,MACF;AAAA,IACF,CAAC;AAED,WAAO,MAAM;AACX,mBAAa,OAAO;AACpB,UAAI,mBAAmB,SAAS;AAC9B,qBAAa,mBAAmB,OAAO;AACvC,2BAAmB,UAAU;AAAA,MAC/B;AAAA,IACF;AAAA,EACF,GAAG,CAAC,SAAS,mBAAmB,SAAS,UAAU,CAAC;AAEpD,SAAO,EAAE,aAAa,WAAW,cAAc,GAAG,eAAe,MAAM;AACzE;","names":["DEFAULT_RECONNECT_DELAY_MS","useEffect","useRef","useState","useCallback","AppState","Platform","DEFAULT_BACKGROUND_GRACE_MS","useState","useRef","AppState","Platform","useEffect","useCallback"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pedi/chika-sdk",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "React Native SDK for Pedi Chika chat service",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -33,7 +33,7 @@
33
33
  "typecheck": "tsc --noEmit"
34
34
  },
35
35
  "dependencies": {
36
- "@pedi/chika-types": "^1.0.7",
36
+ "@pedi/chika-types": "^1.0.8",
37
37
  "react-native-sse": "^1.2.1"
38
38
  },
39
39
  "peerDependencies": {