@pedi/chika-sdk 1.0.7 → 1.0.9

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.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
  }
@@ -780,7 +780,9 @@ function useChat({ config, channelId, profile, onMessage }) {
780
780
  onMessageRef.current = onMessage;
781
781
  const startingRef = useRef(false);
782
782
  const pendingOptimisticIds = useRef(/* @__PURE__ */ new Set());
783
+ const markReadTimerRef = useRef(null);
783
784
  const [monitor, setMonitor] = useState(null);
785
+ const [monitorReady, setMonitorReady] = useState(false);
784
786
  const monitorRef = useRef(null);
785
787
  monitorRef.current = monitor;
786
788
  const queueRef = useRef(null);
@@ -793,19 +795,50 @@ function useChat({ config, channelId, profile, onMessage }) {
793
795
  useEffect(() => {
794
796
  if (!resilienceEnabled) {
795
797
  setMonitor(null);
798
+ setMonitorReady(true);
796
799
  return;
797
800
  }
798
801
  if (injectedMonitor) {
799
802
  setMonitor(injectedMonitor);
803
+ setMonitorReady(true);
800
804
  return;
801
805
  }
802
- const m = createNetworkMonitor();
803
- setMonitor(m);
804
- return () => {
805
- m.dispose();
806
+ try {
807
+ const m = createNetworkMonitor();
808
+ setMonitor(m);
809
+ setMonitorReady(true);
810
+ return () => {
811
+ m.dispose();
812
+ setMonitor(null);
813
+ };
814
+ } catch {
806
815
  setMonitor(null);
807
- };
816
+ setMonitorReady(true);
817
+ }
808
818
  }, [resilienceEnabled, injectedMonitor]);
819
+ function scheduleMarkAsRead(messageId) {
820
+ if (markReadTimerRef.current) clearTimeout(markReadTimerRef.current);
821
+ markReadTimerRef.current = setTimeout(() => {
822
+ markReadTimerRef.current = null;
823
+ sessionRef.current?.markAsRead(messageId).catch(() => {
824
+ });
825
+ }, 500);
826
+ }
827
+ function flushMarkReadAndDisconnect() {
828
+ if (markReadTimerRef.current) {
829
+ clearTimeout(markReadTimerRef.current);
830
+ markReadTimerRef.current = null;
831
+ }
832
+ if (sessionRef.current) {
833
+ const lastMsg = messagesRef.current[messagesRef.current.length - 1];
834
+ if (lastMsg) {
835
+ sessionRef.current.markAsRead(lastMsg.id).catch(() => {
836
+ });
837
+ }
838
+ }
839
+ sessionRef.current?.disconnect();
840
+ sessionRef.current = null;
841
+ }
809
842
  const callbacks = {
810
843
  onMessage: (message) => {
811
844
  if (disposedRef.current) return;
@@ -832,6 +865,9 @@ function useChat({ config, channelId, profile, onMessage }) {
832
865
  queueRef.current?.cancel(matchedOptimisticId);
833
866
  }
834
867
  }
868
+ if (message.sender_id !== profileRef.current.id) {
869
+ scheduleMarkAsRead(message.id);
870
+ }
835
871
  onMessageRef.current?.(message);
836
872
  },
837
873
  onStatusChange: (nextStatus) => {
@@ -882,6 +918,10 @@ function useChat({ config, channelId, profile, onMessage }) {
882
918
  } else {
883
919
  setMessages(session.initialMessages);
884
920
  }
921
+ const lastInitMsg = session.initialMessages[session.initialMessages.length - 1];
922
+ if (lastInitMsg) {
923
+ scheduleMarkAsRead(lastInitMsg.id);
924
+ }
885
925
  } catch (err) {
886
926
  if (disposedRef.current) return;
887
927
  if (err instanceof ChannelClosedError) {
@@ -897,25 +937,17 @@ function useChat({ config, channelId, profile, onMessage }) {
897
937
  }
898
938
  useEffect(() => {
899
939
  disposedRef.current = false;
900
- if (resilienceEnabled && !monitor) return;
940
+ if (!monitorReady) return;
901
941
  startSession();
902
942
  return () => {
903
943
  disposedRef.current = true;
904
- if (statusRef.current === "connected" && sessionRef.current) {
905
- const lastMsg = messagesRef.current[messagesRef.current.length - 1];
906
- if (lastMsg) {
907
- sessionRef.current.markAsRead(lastMsg.id).catch(() => {
908
- });
909
- }
910
- }
944
+ flushMarkReadAndDisconnect();
911
945
  if (backgroundTimerRef.current) {
912
946
  clearTimeout(backgroundTimerRef.current);
913
947
  backgroundTimerRef.current = null;
914
948
  }
915
- sessionRef.current?.disconnect();
916
- sessionRef.current = null;
917
949
  };
918
- }, [channelId, monitor]);
950
+ }, [channelId, monitorReady]);
919
951
  useEffect(() => {
920
952
  if (!queueEnabled || !monitor || !retryConfig) return;
921
953
  let entry = queueRegistry.get(channelId);
@@ -963,8 +995,7 @@ function useChat({ config, channelId, profile, onMessage }) {
963
995
  }, [channelId, resilienceEnabled, monitor]);
964
996
  useEffect(() => {
965
997
  function teardownSession() {
966
- sessionRef.current?.disconnect();
967
- sessionRef.current = null;
998
+ flushMarkReadAndDisconnect();
968
999
  setStatus("disconnected");
969
1000
  }
970
1001
  const subscription = AppState.addEventListener("change", (nextAppState) => {
@@ -1091,8 +1122,7 @@ function useChat({ config, channelId, profile, onMessage }) {
1091
1122
  []
1092
1123
  );
1093
1124
  const disconnect = useCallback(() => {
1094
- sessionRef.current?.disconnect();
1095
- sessionRef.current = null;
1125
+ flushMarkReadAndDisconnect();
1096
1126
  setStatus("disconnected");
1097
1127
  }, []);
1098
1128
  return {
@@ -1124,24 +1154,33 @@ function useUnread(options) {
1124
1154
  const appStateRef = useRef2(AppState2.currentState);
1125
1155
  const backgroundTimerRef = useRef2(null);
1126
1156
  const backgroundGraceMs = config.backgroundGraceMs ?? (Platform2.OS === "android" ? DEFAULT_BACKGROUND_GRACE_MS2 : 0);
1127
- const [monitor, setMonitor] = useState2(null);
1157
+ const [monitorReady, setMonitorReady] = useState2(false);
1158
+ const monitorRef = useRef2(null);
1128
1159
  const resilienceEnabled = config.resilience !== false;
1129
1160
  const injectedMonitor = typeof config.resilience === "object" ? config.resilience.networkMonitor : void 0;
1130
1161
  useEffect2(() => {
1131
1162
  if (!resilienceEnabled) {
1132
- setMonitor(null);
1163
+ monitorRef.current = null;
1164
+ setMonitorReady(true);
1133
1165
  return;
1134
1166
  }
1135
1167
  if (injectedMonitor) {
1136
- setMonitor(injectedMonitor);
1168
+ monitorRef.current = injectedMonitor;
1169
+ setMonitorReady(true);
1137
1170
  return;
1138
1171
  }
1139
- const m = createNetworkMonitor();
1140
- setMonitor(m);
1141
- return () => {
1142
- m.dispose();
1143
- setMonitor(null);
1144
- };
1172
+ try {
1173
+ const m = createNetworkMonitor();
1174
+ monitorRef.current = m;
1175
+ setMonitorReady(true);
1176
+ return () => {
1177
+ m.dispose();
1178
+ monitorRef.current = null;
1179
+ };
1180
+ } catch {
1181
+ monitorRef.current = null;
1182
+ setMonitorReady(true);
1183
+ }
1145
1184
  }, [resilienceEnabled, injectedMonitor]);
1146
1185
  const connect = useCallback2(() => {
1147
1186
  connRef.current?.close();
@@ -1155,7 +1194,7 @@ function useUnread(options) {
1155
1194
  headers: customHeaders,
1156
1195
  reconnectDelayMs: configRef.current.reconnectDelayMs,
1157
1196
  customEvents: UNREAD_CUSTOM_EVENTS,
1158
- networkMonitor: monitor ?? void 0
1197
+ networkMonitor: monitorRef.current ?? void 0
1159
1198
  },
1160
1199
  {
1161
1200
  onOpen: () => {
@@ -1187,7 +1226,7 @@ function useUnread(options) {
1187
1226
  }
1188
1227
  }
1189
1228
  );
1190
- }, [channelId, participantId, monitor]);
1229
+ }, [channelId, participantId]);
1191
1230
  const disconnect = useCallback2(() => {
1192
1231
  connRef.current?.close();
1193
1232
  connRef.current = null;
@@ -1200,7 +1239,7 @@ function useUnread(options) {
1200
1239
  disconnect();
1201
1240
  return;
1202
1241
  }
1203
- if (resilienceEnabled && !monitor) return;
1242
+ if (!monitorReady) return;
1204
1243
  connect();
1205
1244
  return () => {
1206
1245
  disconnect();
@@ -1209,7 +1248,7 @@ function useUnread(options) {
1209
1248
  backgroundTimerRef.current = null;
1210
1249
  }
1211
1250
  };
1212
- }, [channelId, participantId, enabled, connect, disconnect]);
1251
+ }, [channelId, participantId, enabled, monitorReady, connect, disconnect]);
1213
1252
  useEffect2(() => {
1214
1253
  if (!enabled) return;
1215
1254
  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 markReadTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\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 // Debounced markAsRead: batches rapid incoming messages into a single POST.\n // Keeps unread count in sync while the user is viewing the chat.\n function scheduleMarkAsRead(messageId: string): void {\n if (markReadTimerRef.current) clearTimeout(markReadTimerRef.current);\n markReadTimerRef.current = setTimeout(() => {\n markReadTimerRef.current = null;\n sessionRef.current?.markAsRead(messageId).catch(() => {});\n }, 500);\n }\n\n // Flush pending debounced markAsRead and send a final one with the latest message.\n // Used by all teardown paths (unmount, background, manual disconnect).\n function flushMarkReadAndDisconnect(): void {\n if (markReadTimerRef.current) {\n clearTimeout(markReadTimerRef.current);\n markReadTimerRef.current = null;\n }\n if (sessionRef.current) {\n const lastMsg = messagesRef.current[messagesRef.current.length - 1];\n if (lastMsg) {\n sessionRef.current.markAsRead(lastMsg.id).catch(() => {});\n }\n }\n sessionRef.current?.disconnect();\n sessionRef.current = null;\n }\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 // Debounced markAsRead keeps unread count in sync while viewing chat.\n // Only mark for messages from others (not our own SSE echo).\n if (message.sender_id !== profileRef.current.id) {\n scheduleMarkAsRead(message.id);\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\n // Mark latest message as read so the server broadcasts unread_clear.\n // Uses scheduleMarkAsRead (debounced) so that if SSE delivers additional\n // messages right after join, they're covered by the same batched call.\n const lastInitMsg = session.initialMessages[session.initialMessages.length - 1];\n if (lastInitMsg) {\n scheduleMarkAsRead(lastInitMsg.id);\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 flushMarkReadAndDisconnect();\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n }\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 flushMarkReadAndDisconnect();\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 flushMarkReadAndDisconnect();\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":["DEFAULT_RECONNECT_DELAY_MS","useEffect","useRef","useState","useCallback","AppState","Platform","DEFAULT_BACKGROUND_GRACE_MS","useState","useRef","AppState","Platform","useEffect","useCallback"]}