@pedi/chika-sdk 1.0.6 → 1.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -691,7 +691,7 @@ var MessageQueue = class {
691
691
  const entry = {
692
692
  optimisticId,
693
693
  sendFn,
694
- status: this.config.networkMonitor.isConnected() ? "sending" : "queued",
694
+ status: "queued",
695
695
  retryCount: 0,
696
696
  abort,
697
697
  resolve,
@@ -731,10 +731,16 @@ var MessageQueue = class {
731
731
  }
732
732
  this.entries = [];
733
733
  }
734
+ /**
735
+ * Trigger a flush of queued messages. Returns immediately if a flush is
736
+ * already in progress or the network is offline. The in-progress flush
737
+ * will pick up any newly queued entries via its while loop.
738
+ */
734
739
  async flush() {
735
740
  if (this.flushing) return;
736
741
  if (!this.config.networkMonitor.isConnected()) return;
737
742
  this.flushing = true;
743
+ let awaitingSession = false;
738
744
  try {
739
745
  while (this.entries.length > 0) {
740
746
  const entry = this.entries.find((e) => e.status === "queued");
@@ -755,6 +761,12 @@ var MessageQueue = class {
755
761
  this.persist();
756
762
  } catch (err) {
757
763
  if (entry.abort.signal.aborted) continue;
764
+ if (err instanceof ChatDisconnectedError) {
765
+ entry.status = "queued";
766
+ this.config.onStatusChange?.();
767
+ awaitingSession = true;
768
+ break;
769
+ }
758
770
  entry.status = "failed";
759
771
  entry.error = err instanceof Error ? err : new Error(String(err));
760
772
  entry.retryCount++;
@@ -764,6 +776,9 @@ var MessageQueue = class {
764
776
  }
765
777
  } finally {
766
778
  this.flushing = false;
779
+ if (!awaitingSession && this.entries.some((e) => e.status === "queued") && this.config.networkMonitor.isConnected()) {
780
+ queueMicrotask(() => this.flush());
781
+ }
767
782
  }
768
783
  }
769
784
  persist() {
@@ -840,6 +855,7 @@ function useChat({ config, channelId, profile, onMessage }) {
840
855
  const callbacks = {
841
856
  onMessage: (message) => {
842
857
  if (disposedRef.current) return;
858
+ let matchedOptimisticId = null;
843
859
  setMessages((prev) => {
844
860
  if (pendingOptimisticIds.current.size === 0) {
845
861
  return [...prev, message];
@@ -848,14 +864,20 @@ function useChat({ config, channelId, profile, onMessage }) {
848
864
  (m) => pendingOptimisticIds.current.has(m.id) && m.sender_id === message.sender_id && m.body === message.body && m.type === message.type
849
865
  );
850
866
  if (optimisticIdx !== -1) {
851
- const optimisticId = prev[optimisticIdx].id;
852
- pendingOptimisticIds.current.delete(optimisticId);
867
+ matchedOptimisticId = prev[optimisticIdx].id;
868
+ pendingOptimisticIds.current.delete(matchedOptimisticId);
853
869
  const next = [...prev];
854
870
  next[optimisticIdx] = message;
855
871
  return next;
856
872
  }
857
873
  return [...prev, message];
858
874
  });
875
+ if (matchedOptimisticId) {
876
+ const s = queueRef.current?.getStatus(matchedOptimisticId);
877
+ if (s && s.status !== "sending") {
878
+ queueRef.current?.cancel(matchedOptimisticId);
879
+ }
880
+ }
859
881
  onMessageRef.current?.(message);
860
882
  },
861
883
  onStatusChange: (nextStatus) => {
@@ -896,6 +918,7 @@ function useChat({ config, channelId, profile, onMessage }) {
896
918
  }
897
919
  sessionRef.current = session;
898
920
  setParticipants(session.initialParticipants);
921
+ queueRef.current?.flush();
899
922
  if (pendingOptimisticIds.current.size > 0) {
900
923
  const pendingIds = pendingOptimisticIds.current;
901
924
  setMessages((prev) => {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/use-chat.ts","../src/errors.ts","../src/retry.ts","../src/resolve-url.ts","../src/sse-connection.ts","../src/session.ts","../src/network-monitor.ts","../src/message-queue.ts","../src/use-unread.ts"],"sourcesContent":["export { useChat } from './use-chat';\nexport { useUnread } from './use-unread';\nexport { createChatSession } from './session';\nexport { resolveServerUrl, createManifest } from './resolve-url';\nexport { createSSEConnection } from './sse-connection';\nexport { ChatDisconnectedError, ChannelClosedError, HttpError, RetryExhaustedError, QueueFullError } from './errors';\nexport { withRetry, isRetryableError, calculateBackoff, resolveRetryConfig } from './retry';\nexport { createNetworkMonitor } from './network-monitor';\nexport { createQueueStorage, createAsyncStorageAdapter } from './message-queue';\n\nexport type { ChatConfig, ChatStatus, UseChatOptions, UseChatReturn, ResilienceConfig } from './types';\nexport type { ChatSession, SessionCallbacks } from './session';\nexport type { UseUnreadOptions, UseUnreadReturn } from './use-unread';\nexport type { SSEConnection, SSEConnectionConfig, SSEConnectionCallbacks } from './sse-connection';\nexport type { RetryConfig } from './retry';\nexport type { NetworkMonitor } from './network-monitor';\nexport type { MessageSendStatus, QueuedMessage, QueueStorage } from './message-queue';\n\nexport type {\n ChatDomain,\n DefaultDomain,\n Message,\n Participant,\n MessageAttributes,\n SendMessageResponse,\n ChatManifest,\n ChatBucket,\n UnreadCountResponse,\n MarkReadRequest,\n SSEUnreadUpdateEvent,\n SSEUnreadClearEvent,\n SSEUnreadEvent,\n PediChat,\n PediRole,\n PediVehicle,\n PediLocation,\n PediParticipantMeta,\n PediMessageType,\n PediMessageAttributes,\n} from '@pedi/chika-types';\n","import { useEffect, useRef, useState, useCallback } from 'react';\nimport { AppState, Platform, type AppStateStatus } from 'react-native';\nimport type {\n ChatDomain,\n DefaultDomain,\n Message,\n Participant,\n MessageAttributes,\n SendMessageResponse,\n} from '@pedi/chika-types';\nimport type { UseChatOptions, UseChatReturn, ChatStatus } from './types';\nimport { ChatDisconnectedError, ChannelClosedError, QueueFullError, RetryExhaustedError } from './errors';\nimport { isRetryableError, resolveRetryConfig } from './retry';\nimport { createChatSession, type ChatSession, type SessionCallbacks } from './session';\nimport { createNetworkMonitor, type NetworkMonitor } from './network-monitor';\nimport { MessageQueue, type QueuedMessage } from './message-queue';\n\nconst DEFAULT_BACKGROUND_GRACE_MS = 2000;\nconst DEFAULT_MAX_QUEUE_SIZE = 50;\n\n// Module-scope queue registry, keyed by channelId. Survives component remounts.\nconst queueRegistry = new Map<string, { queue: MessageQueue; refCount: number }>();\n\n/**\n * React hook for real-time chat over SSE.\n * Manages connection lifecycle, AppState transitions, message deduplication, reconnection,\n * and optional network resilience (retry, offline queue, network monitoring).\n *\n * @template D - Chat domain type for role/message type narrowing. Defaults to DefaultDomain.\n */\nexport function useChat<D extends ChatDomain = DefaultDomain>(\n { config, channelId, profile, onMessage }: UseChatOptions<D>,\n): UseChatReturn<D> {\n const [messages, setMessages] = useState<Message<D>[]>([]);\n const [participants, setParticipants] = useState<Participant<D>[]>([]);\n const [status, setStatus] = useState<ChatStatus>('connecting');\n const [error, setError] = useState<Error | null>(null);\n const [pendingMessages, setPendingMessages] = useState<QueuedMessage[]>([]);\n\n const sessionRef = useRef<ChatSession<D> | null>(null);\n const disposedRef = useRef(false);\n const messagesRef = useRef(messages);\n messagesRef.current = messages;\n const statusRef = useRef(status);\n statusRef.current = status;\n const appStateRef = useRef<AppStateStatus>(AppState.currentState);\n const backgroundTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const profileRef = useRef(profile);\n profileRef.current = profile;\n const configRef = useRef(config);\n configRef.current = config;\n const onMessageRef = useRef(onMessage);\n onMessageRef.current = onMessage;\n const startingRef = useRef(false);\n const pendingOptimisticIds = useRef(new Set<string>());\n const [monitor, setMonitor] = useState<NetworkMonitor | null>(null);\n const monitorRef = useRef<NetworkMonitor | null>(null);\n monitorRef.current = monitor;\n const queueRef = useRef<MessageQueue | null>(null);\n\n const resilienceEnabled = config.resilience !== false;\n const queueEnabled =\n resilienceEnabled &&\n (typeof config.resilience === 'object' ? config.resilience.offlineQueue !== false : true);\n const retryConfig = resolveRetryConfig(config.resilience);\n const maxQueueSize =\n (resilienceEnabled && config.resilience && typeof config.resilience === 'object'\n ? config.resilience.maxQueueSize\n : undefined) ?? DEFAULT_MAX_QUEUE_SIZE;\n\n const backgroundGraceMs =\n config.backgroundGraceMs ?? (Platform.OS === 'android' ? DEFAULT_BACKGROUND_GRACE_MS : 0);\n\n // Resolve user-injected monitor (stable reference, no side effect)\n const injectedMonitor =\n typeof config.resilience === 'object' ? config.resilience.networkMonitor : undefined;\n\n // Create monitor in useEffect to avoid side effects during render.\n // Uses state (not ref) so the queue effect re-runs when monitor is ready.\n useEffect(() => {\n if (!resilienceEnabled) {\n setMonitor(null);\n return;\n }\n if (injectedMonitor) {\n setMonitor(injectedMonitor);\n return; // user owns lifecycle\n }\n const m = createNetworkMonitor();\n setMonitor(m);\n return () => {\n m.dispose();\n setMonitor(null);\n };\n }, [resilienceEnabled, injectedMonitor]);\n\n const callbacks: SessionCallbacks<D> = {\n onMessage: (message) => {\n if (disposedRef.current) return;\n 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 const optimisticId = prev[optimisticIdx]!.id;\n pendingOptimisticIds.current.delete(optimisticId);\n const next = [...prev];\n next[optimisticIdx] = message;\n return next;\n }\n return [...prev, message];\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 // 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 } 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: this.config.networkMonitor.isConnected() ? 'sending' : '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 private async flush(): Promise<void> {\n if (this.flushing) return;\n if (!this.config.networkMonitor.isConnected()) return;\n\n this.flushing = true;\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 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 }\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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,mBAAyD;AACzD,0BAAwD;;;ACCjD,IAAM,wBAAN,cAAoC,MAAM;AAAA,EAC/C,YAA4B,QAAoB;AAC9C,UAAM,6BAA6B,MAAM,EAAE;AADjB;AAE1B,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,qBAAN,cAAiC,MAAM;AAAA,EAC5C,YAA4B,WAAmB;AAC7C,UAAM,WAAW,SAAS,YAAY;AADZ;AAE1B,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,YAAN,cAAwB,MAAM;AAAA,EACnC,YACkB,QACA,MACA,YAChB;AACA,UAAM,QAAQ,MAAM,KAAK,IAAI,EAAE;AAJf;AACA;AACA;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,sBAAN,cAAkC,MAAM;AAAA,EAC7C,YACkB,WACA,UACA,WAChB;AACA,UAAM,GAAG,SAAS,iBAAiB,QAAQ,cAAc,UAAU,OAAO,EAAE;AAJ5D;AACA;AACA;AAGhB,SAAK,OAAO;AAAA,EACd;AACF;AAEO,IAAM,iBAAN,cAA6B,MAAM;AAAA,EACxC,YAA4B,SAAiB;AAC3C,UAAM,2BAA2B,OAAO,GAAG;AADjB;AAE1B,SAAK,OAAO;AAAA,EACd;AACF;;;AC5BO,IAAM,uBAAoC;AAAA,EAC/C,aAAa;AAAA,EACb,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,cAAc;AAChB;AAEO,SAAS,iBAAiB,SAAiB,QAA6B;AAC7E,QAAM,QAAQ,KAAK,IAAI,OAAO,cAAc,KAAK,SAAS,OAAO,UAAU;AAC3E,QAAM,SAAS,KAAK,KAAK,OAAO,IAAI,IAAI,KAAK,OAAO;AACpD,SAAO,KAAK,MAAM,QAAQ,MAAM;AAClC;AAEO,SAAS,iBAAiB,OAAyB;AACxD,MAAI,iBAAiB,mBAAoB,QAAO;AAChD,MAAI,iBAAiB,sBAAuB,QAAO;AACnD,MAAI,iBAAiB,eAAgB,QAAO;AAC5C,MAAI,iBAAiB,gBAAgB,MAAM,SAAS,aAAc,QAAO;AAEzE,MAAI,iBAAiB,WAAW;AAC9B,UAAM,EAAE,OAAO,IAAI;AACnB,QAAI,WAAW,OAAO,WAAW,IAAK,QAAO;AAC7C,QAAI,UAAU,IAAK,QAAO;AAC1B,WAAO;AAAA,EACT;AAGA,MAAI,iBAAiB,UAAW,QAAO;AAEvC,SAAO;AACT;AAEO,SAAS,MAAM,IAAY,QAAqC;AACrE,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI,QAAQ,SAAS;AACnB,aAAO,OAAO,UAAU,IAAI,aAAa,WAAW,YAAY,CAAC;AACjE;AAAA,IACF;AAEA,UAAM,QAAQ,WAAW,SAAS,EAAE;AAEpC,YAAQ;AAAA,MACN;AAAA,MACA,MAAM;AACJ,qBAAa,KAAK;AAClB,eAAO,OAAO,UAAU,IAAI,aAAa,WAAW,YAAY,CAAC;AAAA,MACnE;AAAA,MACA,EAAE,MAAM,KAAK;AAAA,IACf;AAAA,EACF,CAAC;AACH;AAEO,SAAS,mBACd,YACoB;AACpB,MAAI,eAAe,MAAO,QAAO;AACjC,MAAI,CAAC,cAAc,WAAW,UAAU,OAAW,QAAO;AAC1D,MAAI,WAAW,UAAU,MAAO,QAAO;AACvC,SAAO,EAAE,GAAG,sBAAsB,GAAG,WAAW,MAAM;AACxD;AAEA,eAAsB,UACpB,IACA,SAAsB,sBACtB,QACY;AACZ,MAAI;AAEJ,WAAS,UAAU,GAAG,UAAU,OAAO,aAAa,WAAW;AAC7D,QAAI,QAAQ,SAAS;AACnB,YAAM,OAAO,UAAU,IAAI,aAAa,WAAW,YAAY;AAAA,IACjE;AAEA,QAAI;AACF,aAAO,MAAM,GAAG;AAAA,IAClB,SAAS,KAAK;AACZ,kBAAY,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAE9D,UAAI,CAAC,iBAAiB,GAAG,EAAG,OAAM;AAElC,UAAI,UAAU,OAAO,cAAc,GAAG;AAEpC,cAAM,UACJ,eAAe,aAAa,IAAI,cAAc,OAC1C,IAAI,aAAa,MACjB,iBAAiB,SAAS,MAAM;AAEtC,cAAM,MAAM,SAAS,MAAM;AAAA,MAC7B;AAAA,IACF;AAAA,EACF;AAEA,QAAM,IAAI;AAAA,IACR;AAAA,IACA,OAAO;AAAA,IACP;AAAA,EACF;AACF;;;ACtGO,SAAS,eAAe,WAAiC;AAC9D,SAAO,EAAE,SAAS,CAAC,EAAE,OAAO,WAAW,OAAO,CAAC,GAAG,EAAE,GAAG,YAAY,UAAU,CAAC,EAAE;AAClF;AAEO,SAAS,iBAAiB,UAAwB,WAA2B;AAClF,QAAM,OAAO,CAAC,GAAG,SAAS,EAAE,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,WAAW,CAAC,GAAG,CAAC,IAAI;AAC3E,QAAM,SAAS,SAAS,QAAQ;AAAA,IAC9B,CAAC,MAAM,QAAQ,EAAE,MAAM,CAAC,KAAK,QAAQ,EAAE,MAAM,CAAC;AAAA,EAChD;AACA,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,2BAA2B,IAAI,EAAE;AAC9D,SAAO,OAAO;AAChB;;;ACrBA,8BAAwB;AAIxB,IAAM,6BAA6B;AAwB5B,SAAS,oBACd,QACA,WACe;AACf,QAAM,YAAY,OAAO,oBAAoB;AAC7C,QAAM,eAAe,OAAO,gBAAgB,CAAC;AAC7C,QAAM,UAAU,OAAO;AAEvB,QAAM,gBAA6B;AAAA,IACjC,aAAa;AAAA,IACb,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,cAAc;AAAA,EAChB;AAEA,MAAI,qBAAqB,OAAO;AAChC,MAAI,KAAiC;AACrC,MAAI,WAAW;AACf,MAAI,iBAAuD;AAC3D,MAAI,UAAU;AACd,MAAI,YAAoC;AAExC,WAAS,UAAgB;AACvB,QAAI,IAAI;AACN,SAAG,wBAAwB;AAC3B,SAAG,MAAM;AACT,WAAK;AAAA,IACP;AACA,QAAI,gBAAgB;AAClB,mBAAa,cAAc;AAC3B,uBAAiB;AAAA,IACnB;AACA,QAAI,WAAW;AACb,gBAAU,MAAM;AAChB,kBAAY;AAAA,IACd;AAAA,EACF;AAEA,iBAAe,oBAAmC;AAChD,QAAI,YAAY,kBAAkB,UAAW;AAC7C,cAAU,iBAAiB;AAC3B,YAAQ;AAGR,QAAI,WAAW,CAAC,QAAQ,YAAY,GAAG;AACrC,kBAAY,IAAI,gBAAgB;AAChC,UAAI;AACF,cAAM,QAAQ,cAAc,UAAU,MAAM;AAAA,MAC9C,QAAQ;AACN;AAAA,MACF;AACA,kBAAY;AACZ,UAAI,SAAU;AAAA,IAChB;AAEA,UAAM,QAAQ,iBAAiB,WAAW,aAAa;AACvD,qBAAiB,WAAW,MAAM;AAChC,uBAAiB;AACjB,cAAQ;AAAA,IACV,GAAG,KAAK;AAAA,EACV;AAEA,WAAS,UAAgB;AACvB,QAAI,SAAU;AAEd,SAAK,IAAI,wBAAAA,QAAoB,OAAO,KAAK;AAAA,MACvC,SAAS;AAAA,QACP,GAAG,OAAO;AAAA,QACV,GAAI,sBAAsB,EAAE,iBAAiB,mBAAmB;AAAA,MAClE;AAAA,MACA,iBAAiB;AAAA,IACnB,CAAC;AAED,OAAG,iBAAiB,QAAQ,MAAM;AAChC,UAAI,SAAU;AACd,gBAAU;AACV,gBAAU,SAAS;AAAA,IACrB,CAAC;AAED,OAAG,iBAAiB,WAAW,CAAC,UAAU;AACxC,UAAI,YAAY,CAAC,MAAM,KAAM;AAC7B,UAAI,MAAM,aAAa;AACrB,6BAAqB,MAAM;AAAA,MAC7B;AACA,gBAAU,QAAQ,WAAW,MAAM,MAAM,MAAM,eAAe,MAAS;AAAA,IACzE,CAAC;AAED,eAAW,aAAa,cAAc;AACpC,SAAG,iBAAiB,WAAW,CAAC,UAAU;AACxC,YAAI,SAAU;AACd,kBAAU,QAAQ,WAAW,MAAM,QAAQ,IAAI,MAAS;AAAA,MAC1D,CAAC;AAAA,IACH;AAEA,OAAG,iBAAiB,SAAS,CAAC,UAAU;AACtC,UAAI,SAAU;AAEd,YAAM,MAAM,aAAa,QAAQ,OAAO,MAAM,OAAO,IAAI;AAEzD,UAAI,IAAI,SAAS,mBAAmB,KAAK,IAAI,SAAS,KAAK,GAAG;AAC5D,kBAAU,WAAW;AACrB,gBAAQ;AACR,mBAAW;AACX;AAAA,MACF;AAEA,UAAI,IAAK,WAAU,UAAU,IAAI,MAAM,GAAG,CAAC;AAE3C,wBAAkB;AAAA,IACpB,CAAC;AAED,OAAG,iBAAiB,SAAS,MAAM;AACjC,UAAI,SAAU;AACd,wBAAkB;AAAA,IACpB,CAAC;AAAA,EACH;AAEA,UAAQ;AAER,SAAO;AAAA,IACL,OAAO,MAAM;AACX,iBAAW;AACX,cAAQ;AAAA,IACV;AAAA,IAEA,oBAAoB,MAAM;AACxB,UAAI,SAAU;AACd,cAAQ;AACR,gBAAU;AACV,cAAQ;AAAA,IACV;AAAA,EACF;AACF;;;AC/IA,IAAMC,8BAA6B;AACnC,IAAM,eAAe;AAErB,IAAM,yBAAsC;AAAA,EAC1C,aAAa;AAAA,EACb,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,cAAc;AAChB;AAyBA,SAAS,gBAAgB,KAAmC;AAC1D,QAAM,SAAS,IAAI,QAAQ,IAAI,aAAa;AAC5C,MAAI,CAAC,OAAQ,QAAO;AACpB,QAAM,UAAU,OAAO,MAAM;AAC7B,SAAO,OAAO,SAAS,OAAO,IAAI,UAAU;AAC9C;AAEA,eAAe,eAAe,KAA+B;AAC3D,QAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,MAAM,EAAE;AAC5C,QAAM,IAAI,UAAU,IAAI,QAAQ,MAAM,gBAAgB,GAAG,CAAC;AAC5D;AAEA,eAAsB,kBACpB,QACA,WACA,SACA,WACA,gBACyB;AACzB,QAAM,aAAa,iBAAiB,OAAO,UAAU,SAAS;AAC9D,QAAM,gBAAgB,OAAO,WAAW,CAAC;AACzC,QAAM,iBAAiB,OAAO,oBAAoBA;AAClD,QAAM,cAAc,mBAAmB,OAAO,UAAU;AAExD,QAAM,eAAe,IAAI,gBAAgB;AAEzC,YAAU,eAAe,YAAY;AAErC,QAAM,SAAS,YAAsC;AACnD,UAAM,UAAU,MAAM,MAAM,GAAG,UAAU,aAAa,SAAS,SAAS;AAAA,MACtE,QAAQ;AAAA,MACR,SAAS,EAAE,gBAAgB,oBAAoB,GAAG,cAAc;AAAA,MAChE,MAAM,KAAK,UAAU,OAAO;AAAA,MAC5B,QAAQ,aAAa;AAAA,IACvB,CAAC;AAED,QAAI,QAAQ,WAAW,KAAK;AAC1B,YAAM,IAAI,mBAAmB,SAAS;AAAA,IACxC;AAEA,QAAI,CAAC,QAAQ,IAAI;AACf,YAAM,eAAe,OAAO;AAAA,IAC9B;AAEA,WAAO,QAAQ,KAAK;AAAA,EACtB;AAEA,QAAM,WAAW,cACb,MAAM,UAAU,QAAQ,aAAa,aAAa,MAAM,IACxD,MAAM,OAAO;AAEjB,QAAM,EAAE,UAAU,cAAc,UAAU,IAAqB;AAE/D,MAAI,cACF,SAAS,SAAS,IAAI,SAAS,SAAS,SAAS,CAAC,EAAG,KAAK;AAE5D,QAAM,WAAW;AAEjB,QAAM,iBAAiB,IAAI,IAAY,SAAS,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;AAEhE,MAAI,UAAgC;AACpC,MAAI,WAAW;AAEf,QAAM,iBAAiB,eAAe;AAEtC,WAAS,cAAoB;AAC3B,QAAI,eAAe,QAAQ,eAAgB;AAC3C,UAAM,MAAM,CAAC,GAAG,cAAc;AAC9B,mBAAe,MAAM;AACrB,eAAW,MAAM,IAAI,MAAM,CAAC,YAAY,GAAG;AACzC,qBAAe,IAAI,EAAE;AAAA,IACvB;AAAA,EACF;AAEA,WAAS,UAAgB;AACvB,QAAI,SAAU;AAEd,UAAM,YAAY,cACd,GAAG,UAAU,aAAa,SAAS,YACnC,GAAG,UAAU,aAAa,SAAS,sBAAsB,mBAAmB,QAAQ,CAAC;AAEzF,cAAU;AAAA,MACR;AAAA,QACE,KAAK;AAAA,QACL,SAAS;AAAA,QACT,kBAAkB;AAAA,QAClB;AAAA,QACA,cAAc,CAAC,QAAQ;AAAA,QACvB;AAAA,MACF;AAAA,MACA;AAAA,QACE,QAAQ,MAAM;AACZ,cAAI,CAAC,SAAU,WAAU,eAAe,WAAW;AAAA,QACrD;AAAA,QACA,SAAS,CAAC,WAAW,MAAM,YAAY;AACrC,cAAI,SAAU;AAEd,cAAI,cAAc,WAAW;AAC3B,gBAAI;AACJ,gBAAI;AACF,wBAAU,KAAK,MAAM,IAAI;AAAA,YAC3B,QAAQ;AACN,wBAAU,QAAQ,IAAI,MAAM,6BAA6B,CAAC;AAC1D;AAAA,YACF;AAEA,gBAAI,SAAS;AACX,4BAAc;AAAA,YAChB;AAEA,gBAAI,eAAe,IAAI,QAAQ,EAAE,EAAG;AACpC,2BAAe,IAAI,QAAQ,EAAE;AAC7B,wBAAY;AAEZ,sBAAU,UAAU,OAAO;AAAA,UAC7B,WAAW,cAAc,UAAU;AAIjC,sBAAU,SAAS;AAAA,UACrB;AAAA,QACF;AAAA,QACA,SAAS,CAAC,QAAQ;AAChB,cAAI,CAAC,SAAU,WAAU,QAAQ,GAAG;AAAA,QACtC;AAAA,QACA,UAAU,MAAM;AACd,oBAAU,eAAe,QAAQ;AACjC,qBAAW;AAAA,QACb;AAAA,QACA,gBAAgB,MAAM;AACpB,cAAI,CAAC,SAAU,WAAU,eAAe,cAAc;AAAA,QACxD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,UAAQ;AAER,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,qBAAqB;AAAA,IACrB,iBAAiB;AAAA,IACjB,gBAAgB,kBAAkB;AAAA,IAElC,aAAa,OAAO,MAAM,MAAM,YAAY,mBAAmB;AAC7D,UAAI,SAAU,OAAM,IAAI,sBAAsB,cAAc;AAE5D,YAAM,UAAiC;AAAA,QACrC,WAAW,QAAQ;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,QACA,GAAI,iBAAiB,EAAE,iBAAiB,eAAe,IAAI,CAAC;AAAA,MAC9D;AAEA,YAAM,SAAS,YAA0C;AACvD,cAAM,MAAM,MAAM;AAAA,UAChB,GAAG,UAAU,aAAa,SAAS;AAAA,UACnC;AAAA,YACE,QAAQ;AAAA,YACR,SAAS,EAAE,gBAAgB,oBAAoB,GAAG,cAAc;AAAA,YAChE,MAAM,KAAK,UAAU,OAAO;AAAA,YAC5B,QAAQ,aAAa;AAAA,UACvB;AAAA,QACF;AAEA,YAAI,CAAC,IAAI,IAAI;AACX,gBAAM,eAAe,GAAG;AAAA,QAC1B;AAEA,eAAO,IAAI,KAAK;AAAA,MAClB;AAEA,YAAM,WAAW,cACb,MAAM,UAAU,QAAQ,aAAa,aAAa,MAAM,IACxD,MAAM,OAAO;AAEjB,qBAAe,IAAI,SAAS,EAAE;AAC9B,aAAO;AAAA,IACT;AAAA,IAEA,YAAY,OAAO,cAAsB;AACvC,YAAM,SAAS,YAA2B;AACxC,cAAM,MAAM,MAAM,MAAM,GAAG,UAAU,aAAa,SAAS,SAAS;AAAA,UAClE,QAAQ;AAAA,UACR,SAAS,EAAE,gBAAgB,oBAAoB,GAAG,cAAc;AAAA,UAChE,MAAM,KAAK,UAAU;AAAA,YACnB,gBAAgB,QAAQ;AAAA,YACxB,YAAY;AAAA,UACd,CAAC;AAAA,UACD,QAAQ,aAAa;AAAA,QACvB,CAAC;AACD,YAAI,CAAC,IAAI,IAAI;AACX,gBAAM,eAAe,GAAG;AAAA,QAC1B;AAAA,MACF;AAEA,UAAI,aAAa;AACf,YAAI;AACF,gBAAM,UAAU,QAAQ,wBAAwB,aAAa,MAAM;AAAA,QACrE,SAAS,KAAK;AAEZ,cAAI,eAAe,WAAW;AAC5B,sBAAU,QAAQ,GAAG;AAAA,UACvB;AAAA,QAEF;AAAA,MACF,OAAO;AACL,cAAM,OAAO;AAAA,MACf;AAAA,IACF;AAAA,IAEA,YAAY,MAAM;AAChB,iBAAW;AACX,mBAAa,MAAM;AACnB,eAAS,MAAM;AACf,gBAAU;AACV,gBAAU,eAAe,cAAc;AAAA,IACzC;AAAA,EACF;AACF;;;ACxQA,SAAS,oBAAoC;AAC3C,SAAO;AAAA,IACL,aAAa,MAAM;AAAA,IACnB,WAAW,MAAM,MAAM;AAAA,IAAC;AAAA,IACxB,eAAe,MAAM,QAAQ,QAAQ;AAAA,IACrC,SAAS,MAAM;AAAA,IAAC;AAAA,EAClB;AACF;AAEA,IAAI,kBAAuB;AAC3B,IAAI,kBAAkB;AAEtB,SAAS,aAAkB;AACzB,MAAI,gBAAiB,QAAO;AAC5B,oBAAkB;AAClB,MAAI;AACF,sBAAkB,QAAQ,iCAAiC;AAAA,EAC7D,QAAQ;AACN,sBAAkB;AAAA,EACpB;AACA,SAAO;AACT;AAEO,SAAS,uBAAuC;AACrD,QAAM,UAAU,WAAW;AAC3B,MAAI,CAAC,QAAS,QAAO,kBAAkB;AAEvC,QAAM,gBAAgB,QAAQ,WAAW;AAEzC,MAAI,YAAY;AAChB,QAAM,YAAY,oBAAI,IAAkC;AACxD,MAAI,qBAA0C;AAE9C,uBAAqB,cAAc;AAAA,IACjC,CAAC,UAA2C;AAC1C,YAAM,OAAO,MAAM,gBAAgB;AACnC,UAAI,SAAS,UAAW;AACxB,kBAAY;AACZ,iBAAW,MAAM,WAAW;AAC1B,WAAG,SAAS;AAAA,MACd;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL,aAAa,MAAM;AAAA,IAEnB,WAAW,CAAC,OAAO;AACjB,gBAAU,IAAI,EAAE;AAChB,aAAO,MAAM;AACX,kBAAU,OAAO,EAAE;AAAA,MACrB;AAAA,IACF;AAAA,IAEA,eAAe,CAAC,WAAY;AAC1B,UAAI,UAAW,QAAO,QAAQ,QAAQ;AAEtC,aAAO,IAAI,QAAc,CAAC,SAAS,WAAW;AAC5C,YAAI,QAAQ,SAAS;AACnB,iBAAO,OAAO,UAAU,IAAI,aAAa,WAAW,YAAY,CAAC;AACjE;AAAA,QACF;AAEA,cAAM,QAAQ,MAAY;AACxB,oBAAU,OAAO,OAAO;AACxB,kBAAQ,oBAAoB,SAAS,OAAO;AAAA,QAC9C;AAEA,cAAM,UAAU,CAAC,aAA4B;AAC3C,cAAI,UAAU;AACZ,kBAAM;AACN,oBAAQ;AAAA,UACV;AAAA,QACF;AAEA,cAAM,UAAU,MAAY;AAC1B,gBAAM;AACN,iBAAO,OAAQ,UAAU,IAAI,aAAa,WAAW,YAAY,CAAC;AAAA,QACpE;AAEA,kBAAU,IAAI,OAAO;AACrB,gBAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAAA,MAC3D,CAAC;AAAA,IACH;AAAA,IAEA,SAAS,MAAM;AACb,gBAAU,MAAM;AAChB,2BAAqB;AACrB,2BAAqB;AAAA,IACvB;AAAA,EACF;AACF;;;ACvFA,IAAI;AAEJ,SAAS,WAAW,MAAmB;AACrC,MAAI;AACF,WAAO,QAAQ,IAAI;AAAA,EACrB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,SAAS,kBAAkB,KAAwB;AACjD,QAAM,OAAO,IAAI,QAAQ,IAAI,SAAS,QAAQ;AAC9C,QAAM,WAAW,IAAI,KAAK,EAAE,IAAI,cAAc,CAAC;AAC/C,SAAO;AAAA,IACL,SAAS,CAAC,QAAQ,QAAQ,QAAQ,SAAS,UAAU,GAAG,KAAK,IAAI;AAAA,IACjE,SAAS,CAAC,KAAK,UAAU;AAAE,eAAS,IAAI,KAAK,KAAK;AAAG,aAAO,QAAQ,QAAQ;AAAA,IAAG;AAAA,IAC/E,YAAY,CAAC,QAAQ;AAAE,eAAS,OAAO,GAAG;AAAG,aAAO,QAAQ,QAAQ;AAAA,IAAG;AAAA,EACzE;AACF;AAEA,SAAS,8BAA8B,KAAwB;AAC7D,QAAM,UAAU,IAAI,WAAW;AAC/B,SAAO;AAAA,IACL,SAAS,CAAC,QAAQ,QAAQ,QAAQ,GAAG;AAAA,IACrC,SAAS,CAAC,KAAK,UAAU,QAAQ,QAAQ,KAAK,KAAK;AAAA,IACnD,YAAY,CAAC,QAAQ,QAAQ,WAAW,GAAG;AAAA,EAC7C;AACF;AAkBO,SAAS,qBAA0C;AACxD,MAAI,oBAAoB,OAAW,QAAO,iBAAiB,WAAW;AAGtE,QAAM,OAAO,WAAW,mBAAmB;AAC3C,MAAI,MAAM;AACR,QAAI;AACF,YAAM,UAAU,kBAAkB,IAAI;AACtC,wBAAkB,EAAE,MAAM,QAAQ,QAAQ;AAC1C,aAAO;AAAA,IACT,QAAQ;AAAA,IAER;AAAA,EACF;AAGA,QAAM,eAAe,WAAW,2CAA2C;AAC3E,MAAI,cAAc;AAChB,UAAM,UAAU,8BAA8B,YAAY;AAC1D,sBAAkB,EAAE,MAAM,iBAAiB,QAAQ;AACnD,WAAO;AAAA,EACT;AAEA,oBAAkB;AAClB,SAAO;AACT;AAMO,SAAS,4BAAiD;AAC/D,QAAM,MAAM,WAAW,2CAA2C;AAClE,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,8BAA8B,GAAG;AAC1C;AAwCO,IAAM,eAAN,MAAmB;AAAA,EAMxB,YAA6B,QAA4B;AAA5B;AAC3B,SAAK,aAAa,eAAe,OAAO,SAAS;AAEjD,SAAK,eAAe,OAAO,eAAe,UAAU,CAAC,cAAc;AACjE,UAAI,UAAW,MAAK,MAAM;AAAA,IAC5B,CAAC;AAAA,EACH;AAAA,EAXQ,UAAwB,CAAC;AAAA,EACzB,WAAW;AAAA,EACX,eAAoC;AAAA,EAC3B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBjB,MAAM,QACJ,eACe;AACf,QAAI,CAAC,KAAK,OAAO,QAAS;AAE1B,QAAI;AACF,YAAM,MAAM,MAAM,KAAK,OAAO,QAAQ,QAAQ,KAAK,UAAU;AAC7D,UAAI,CAAC,IAAK;AAEV,YAAM,YAA8B,KAAK,MAAM,GAAG;AAClD,iBAAW,SAAS,WAAW;AAC7B,cAAM,SAAS,cAAc,MAAM,YAAY;AAC/C,YAAI,CAAC,OAAQ;AAEb,cAAM,QAAQ,IAAI,gBAAgB;AAClC,aAAK,QAAQ,KAAK;AAAA,UAChB,cAAc,MAAM;AAAA,UACpB;AAAA,UACA,QAAQ;AAAA,UACR,YAAY,MAAM;AAAA,UAClB;AAAA;AAAA,UAEA,SAAS,MAAM;AAAA,UAAC;AAAA,UAChB,QAAQ,MAAM;AAAA,UAAC;AAAA,QACjB,CAAC;AAAA,MACH;AAEA,UAAI,KAAK,QAAQ,SAAS,GAAG;AAC3B,aAAK,OAAO,iBAAiB;AAC7B,aAAK,MAAM;AAAA,MACb;AAAA,IACF,QAAQ;AACN,WAAK,OAAO,UAAU,IAAI,MAAM,8CAA8C,CAAC;AAAA,IACjF;AAAA,EACF;AAAA,EAEA,IAAI,eAAuB;AACzB,WAAO,KAAK,QAAQ;AAAA,EACtB;AAAA,EAEA,SAA0B;AACxB,WAAO,KAAK,QAAQ,IAAI,CAAC,OAAO;AAAA,MAC9B,cAAc,EAAE;AAAA,MAChB,QAAQ,EAAE;AAAA,MACV,OAAO,EAAE;AAAA,MACT,YAAY,EAAE;AAAA,IAChB,EAAE;AAAA,EACJ;AAAA,EAEA,UAAU,cAAiD;AACzD,UAAM,QAAQ,KAAK,QAAQ,KAAK,CAAC,MAAM,EAAE,iBAAiB,YAAY;AACtE,QAAI,CAAC,MAAO,QAAO;AACnB,WAAO;AAAA,MACL,cAAc,MAAM;AAAA,MACpB,QAAQ,MAAM;AAAA,MACd,OAAO,MAAM;AAAA,MACb,YAAY,MAAM;AAAA,IACpB;AAAA,EACF;AAAA,EAEA,QAAQ,QAAgB,cAAoD;AAC1E,QAAI,KAAK,QAAQ,UAAU,KAAK,OAAO,SAAS;AAC9C,YAAM,IAAI,eAAe,KAAK,OAAO,OAAO;AAAA,IAC9C;AAEA,UAAM,QAAQ,IAAI,gBAAgB;AAElC,WAAO,IAAI,QAA6B,CAAC,SAAS,WAAW;AAC3D,YAAM,QAAoB;AAAA,QACxB;AAAA,QACA;AAAA,QACA,QAAQ,KAAK,OAAO,eAAe,YAAY,IAAI,YAAY;AAAA,QAC/D,YAAY;AAAA,QACZ;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAEA,WAAK,QAAQ,KAAK,KAAK;AACvB,WAAK,OAAO,iBAAiB;AAC7B,WAAK,QAAQ;AACb,WAAK,MAAM;AAAA,IACb,CAAC;AAAA,EACH;AAAA,EAEA,OAAO,cAA4B;AACjC,UAAM,MAAM,KAAK,QAAQ,UAAU,CAAC,MAAM,EAAE,iBAAiB,YAAY;AACzE,QAAI,QAAQ,GAAI;AAEhB,UAAM,QAAQ,KAAK,QAAQ,GAAG;AAC9B,UAAM,MAAM,MAAM;AAClB,UAAM,OAAO,IAAI,aAAa,aAAa,YAAY,CAAC;AACxD,SAAK,QAAQ,OAAO,KAAK,CAAC;AAC1B,SAAK,OAAO,iBAAiB;AAC7B,SAAK,QAAQ;AAAA,EACf;AAAA,EAEA,MAAM,cAA4B;AAChC,UAAM,QAAQ,KAAK,QAAQ,KAAK,CAAC,MAAM,EAAE,iBAAiB,YAAY;AACtE,QAAI,CAAC,SAAS,MAAM,WAAW,SAAU;AAEzC,UAAM,SAAS;AACf,UAAM,QAAQ;AACd,UAAM,QAAQ,IAAI,gBAAgB;AAClC,SAAK,OAAO,iBAAiB;AAC7B,SAAK,MAAM;AAAA,EACb;AAAA,EAEA,UAAgB;AACd,SAAK,eAAe;AACpB,SAAK,eAAe;AAEpB,eAAW,SAAS,KAAK,SAAS;AAChC,YAAM,MAAM,MAAM;AAClB,YAAM,OAAO,IAAI,aAAa,kBAAkB,YAAY,CAAC;AAAA,IAC/D;AACA,SAAK,UAAU,CAAC;AAAA,EAClB;AAAA,EAEA,MAAc,QAAuB;AACnC,QAAI,KAAK,SAAU;AACnB,QAAI,CAAC,KAAK,OAAO,eAAe,YAAY,EAAG;AAE/C,SAAK,WAAW;AAEhB,QAAI;AACF,aAAO,KAAK,QAAQ,SAAS,GAAG;AAI9B,cAAM,QAAQ,KAAK,QAAQ,KAAK,CAAC,MAAM,EAAE,WAAW,QAAQ;AAC5D,YAAI,CAAC,MAAO;AACZ,YAAI,CAAC,KAAK,OAAO,eAAe,YAAY,EAAG;AAE/C,cAAM,SAAS;AACf,aAAK,OAAO,iBAAiB;AAE7B,YAAI;AACF,gBAAM,SAAS,MAAM;AAAA,YACnB,MAAM;AAAA,YACN,KAAK,OAAO;AAAA,YACZ,MAAM,MAAM;AAAA,UACd;AAEA,gBAAM,QAAQ,MAAM;AACpB,gBAAM,MAAM,KAAK,QAAQ,QAAQ,KAAK;AACtC,cAAI,QAAQ,GAAI,MAAK,QAAQ,OAAO,KAAK,CAAC;AAC1C,eAAK,OAAO,iBAAiB;AAC7B,eAAK,QAAQ;AAAA,QACf,SAAS,KAAK;AACZ,cAAI,MAAM,MAAM,OAAO,QAAS;AAEhC,gBAAM,SAAS;AACf,gBAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;AAChE,gBAAM;AACN,eAAK,OAAO,iBAAiB;AAC7B,eAAK,QAAQ;AAAA,QAGf;AAAA,MACF;AAAA,IACF,UAAE;AACA,WAAK,WAAW;AAAA,IAClB;AAAA,EACF;AAAA,EAEQ,UAAgB;AACtB,QAAI,CAAC,KAAK,OAAO,QAAS;AAE1B,UAAM,OAAyB,KAAK,QAAQ,IAAI,CAAC,OAAO;AAAA,MACtD,cAAc,EAAE;AAAA,MAChB,YAAY,EAAE;AAAA,IAChB,EAAE;AAEF,UAAM,QAAQ,CAAC,QAAiB;AAC9B,WAAK,OAAO;AAAA,QACV,eAAe,QAAQ,MAAM,IAAI,MAAM,4BAA4B;AAAA,MACrE;AAAA,IACF;AAEA,QAAI,KAAK,WAAW,GAAG;AACrB,WAAK,OAAO,QAAQ,WAAW,KAAK,UAAU,EAAE,MAAM,KAAK;AAAA,IAC7D,OAAO;AACL,WAAK,OAAO,QACT,QAAQ,KAAK,YAAY,KAAK,UAAU,IAAI,CAAC,EAC7C,MAAM,KAAK;AAAA,IAChB;AAAA,EACF;AACF;;;APlUA,IAAM,8BAA8B;AACpC,IAAM,yBAAyB;AAG/B,IAAM,gBAAgB,oBAAI,IAAuD;AAS1E,SAAS,QACd,EAAE,QAAQ,WAAW,SAAS,UAAU,GACtB;AAClB,QAAM,CAAC,UAAU,WAAW,QAAI,uBAAuB,CAAC,CAAC;AACzD,QAAM,CAAC,cAAc,eAAe,QAAI,uBAA2B,CAAC,CAAC;AACrE,QAAM,CAAC,QAAQ,SAAS,QAAI,uBAAqB,YAAY;AAC7D,QAAM,CAAC,OAAO,QAAQ,QAAI,uBAAuB,IAAI;AACrD,QAAM,CAAC,iBAAiB,kBAAkB,QAAI,uBAA0B,CAAC,CAAC;AAE1E,QAAM,iBAAa,qBAA8B,IAAI;AACrD,QAAM,kBAAc,qBAAO,KAAK;AAChC,QAAM,kBAAc,qBAAO,QAAQ;AACnC,cAAY,UAAU;AACtB,QAAM,gBAAY,qBAAO,MAAM;AAC/B,YAAU,UAAU;AACpB,QAAM,kBAAc,qBAAuB,6BAAS,YAAY;AAChE,QAAM,yBAAqB,qBAA6C,IAAI;AAC5E,QAAM,iBAAa,qBAAO,OAAO;AACjC,aAAW,UAAU;AACrB,QAAM,gBAAY,qBAAO,MAAM;AAC/B,YAAU,UAAU;AACpB,QAAM,mBAAe,qBAAO,SAAS;AACrC,eAAa,UAAU;AACvB,QAAM,kBAAc,qBAAO,KAAK;AAChC,QAAM,2BAAuB,qBAAO,oBAAI,IAAY,CAAC;AACrD,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAgC,IAAI;AAClE,QAAM,iBAAa,qBAA8B,IAAI;AACrD,aAAW,UAAU;AACrB,QAAM,eAAW,qBAA4B,IAAI;AAEjD,QAAM,oBAAoB,OAAO,eAAe;AAChD,QAAM,eACJ,sBACC,OAAO,OAAO,eAAe,WAAW,OAAO,WAAW,iBAAiB,QAAQ;AACtF,QAAM,cAAc,mBAAmB,OAAO,UAAU;AACxD,QAAM,gBACH,qBAAqB,OAAO,cAAc,OAAO,OAAO,eAAe,WACpE,OAAO,WAAW,eAClB,WAAc;AAEpB,QAAM,oBACJ,OAAO,sBAAsB,6BAAS,OAAO,YAAY,8BAA8B;AAGzF,QAAM,kBACJ,OAAO,OAAO,eAAe,WAAW,OAAO,WAAW,iBAAiB;AAI7E,8BAAU,MAAM;AACd,QAAI,CAAC,mBAAmB;AACtB,iBAAW,IAAI;AACf;AAAA,IACF;AACA,QAAI,iBAAiB;AACnB,iBAAW,eAAe;AAC1B;AAAA,IACF;AACA,UAAM,IAAI,qBAAqB;AAC/B,eAAW,CAAC;AACZ,WAAO,MAAM;AACX,QAAE,QAAQ;AACV,iBAAW,IAAI;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,mBAAmB,eAAe,CAAC;AAEvC,QAAM,YAAiC;AAAA,IACrC,WAAW,CAAC,YAAY;AACtB,UAAI,YAAY,QAAS;AACzB,kBAAY,CAAC,SAAuB;AAClC,YAAI,qBAAqB,QAAQ,SAAS,GAAG;AAC3C,iBAAO,CAAC,GAAG,MAAM,OAAO;AAAA,QAC1B;AAEA,cAAM,gBAAgB,KAAK;AAAA,UACzB,CAAC,MACC,qBAAqB,QAAQ,IAAI,EAAE,EAAE,KACrC,EAAE,cAAc,QAAQ,aACxB,EAAE,SAAS,QAAQ,QACnB,EAAE,SAAS,QAAQ;AAAA,QACvB;AACA,YAAI,kBAAkB,IAAI;AACxB,gBAAM,eAAe,KAAK,aAAa,EAAG;AAC1C,+BAAqB,QAAQ,OAAO,YAAY;AAChD,gBAAM,OAAO,CAAC,GAAG,IAAI;AACrB,eAAK,aAAa,IAAI;AACtB,iBAAO;AAAA,QACT;AACA,eAAO,CAAC,GAAG,MAAM,OAAO;AAAA,MAC1B,CAAC;AACD,mBAAa,UAAU,OAAO;AAAA,IAChC;AAAA,IACA,gBAAgB,CAAC,eAAe;AAC9B,UAAI,YAAY,QAAS;AACzB,gBAAU,UAAU;AACpB,UAAI,eAAe,aAAa;AAC9B,iBAAS,IAAI;AAAA,MACf;AAAA,IACF;AAAA,IACA,SAAS,CAAC,QAAQ;AAChB,UAAI,YAAY,QAAS;AACzB,eAAS,GAAG;AAAA,IACd;AAAA,IACA,UAAU,MAAM;AACd,UAAI,YAAY,QAAS;AACzB,mBAAa;AAAA,IACf;AAAA,EACF;AAEA,iBAAe,eAA8B;AAC3C,QAAI,YAAY,QAAS;AACzB,gBAAY,UAAU;AAEtB,UAAM,WAAW,WAAW;AAC5B,QAAI,UAAU;AACZ,eAAS,WAAW;AACpB,iBAAW,UAAU;AAAA,IACvB;AAEA,QAAI;AACF,YAAM,UAAU,MAAM;AAAA,QACpB,UAAU;AAAA,QACV;AAAA,QACA,WAAW;AAAA,QACX;AAAA,QACA,WAAW,WAAW;AAAA,MACxB;AAEA,UAAI,YAAY,SAAS;AACvB,gBAAQ,WAAW;AACnB;AAAA,MACF;AAEA,iBAAW,UAAU;AACrB,sBAAgB,QAAQ,mBAAmB;AAG3C,UAAI,qBAAqB,QAAQ,OAAO,GAAG;AACzC,cAAM,aAAa,qBAAqB;AACxC,oBAAY,CAAC,SAAS;AACpB,gBAAM,cAAc,KAAK,OAAO,CAAC,MAAM,WAAW,IAAI,EAAE,EAAE,CAAC;AAC3D,iBAAO,CAAC,GAAG,QAAQ,iBAAiB,GAAG,WAAW;AAAA,QACpD,CAAC;AAAA,MACH,OAAO;AACL,oBAAY,QAAQ,eAAe;AAAA,MACrC;AAAA,IACF,SAAS,KAAK;AACZ,UAAI,YAAY,QAAS;AAEzB,UAAI,eAAe,oBAAoB;AACrC,kBAAU,QAAQ;AAClB,iBAAS,GAAG;AACZ;AAAA,MACF;AAEA,gBAAU,OAAO;AACjB,eAAS,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,CAAC;AAAA,IAC9D,UAAE;AACA,kBAAY,UAAU;AAAA,IACxB;AAAA,EACF;AAGA,8BAAU,MAAM;AACd,gBAAY,UAAU;AAEtB,QAAI,qBAAqB,CAAC,QAAS;AACnC,iBAAa;AAEb,WAAO,MAAM;AACX,kBAAY,UAAU;AACtB,UAAI,UAAU,YAAY,eAAe,WAAW,SAAS;AAC3D,cAAM,UAAU,YAAY,QAAQ,YAAY,QAAQ,SAAS,CAAC;AAClE,YAAI,SAAS;AACX,qBAAW,QAAQ,WAAW,QAAQ,EAAE,EAAE,MAAM,MAAM;AAAA,UAAC,CAAC;AAAA,QAC1D;AAAA,MACF;AACA,UAAI,mBAAmB,SAAS;AAC9B,qBAAa,mBAAmB,OAAO;AACvC,2BAAmB,UAAU;AAAA,MAC/B;AACA,iBAAW,SAAS,WAAW;AAC/B,iBAAW,UAAU;AAAA,IACvB;AAAA,EACF,GAAG,CAAC,WAAW,OAAO,CAAC;AAGvB,8BAAU,MAAM;AACd,QAAI,CAAC,gBAAgB,CAAC,WAAW,CAAC,YAAa;AAE/C,QAAI,QAAQ,cAAc,IAAI,SAAS;AACvC,QAAI,CAAC,OAAO;AACV,YAAM,QAAQ,IAAI,aAAa;AAAA,QAC7B;AAAA,QACA,SAAS;AAAA,QACT;AAAA,QACA,gBAAgB;AAAA,QAChB,SACE,OAAO,OAAO,eAAe,WACzB,OAAO,WAAW,eAClB;AAAA,QACN,SAAS,CAAC,QAAQ;AAChB,cAAI,CAAC,YAAY,QAAS,UAAS,GAAG;AAAA,QACxC;AAAA,QACA,gBAAgB,MAAM;AACpB,cAAI,CAAC,YAAY,SAAS;AACxB,+BAAmB,SAAS,SAAS,OAAO,KAAK,CAAC,CAAC;AAAA,UACrD;AAAA,QACF;AAAA,MACF,CAAC;AACD,cAAQ,EAAE,OAAO,UAAU,EAAE;AAC7B,oBAAc,IAAI,WAAW,KAAK;AAAA,IACpC;AACA,UAAM;AACN,aAAS,UAAU,MAAM;AAEzB,WAAO,MAAM;AACX,YAAM,IAAI,cAAc,IAAI,SAAS;AACrC,UAAI,GAAG;AACL,UAAE;AACF,YAAI,EAAE,YAAY,GAAG;AACnB,YAAE,MAAM,QAAQ;AAChB,wBAAc,OAAO,SAAS;AAAA,QAChC;AAAA,MACF;AACA,eAAS,UAAU;AAAA,IACrB;AAAA,EACF,GAAG,CAAC,WAAW,cAAc,OAAO,CAAC;AAGrC,8BAAU,MAAM;AACd,QAAI,CAAC,WAAW,CAAC,kBAAmB;AAEpC,UAAM,QAAQ,QAAQ,UAAU,CAAC,cAAuB;AACtD,UAAI,aAAa,UAAU,YAAY,WAAW,CAAC,YAAY,SAAS;AACtE,qBAAa;AAAA,MACf;AAAA,IACF,CAAC;AAED,WAAO;AAAA,EACT,GAAG,CAAC,WAAW,mBAAmB,OAAO,CAAC;AAG1C,8BAAU,MAAM;AACd,aAAS,kBAAwB;AAC/B,iBAAW,SAAS,WAAW;AAC/B,iBAAW,UAAU;AACrB,gBAAU,cAAc;AAAA,IAC1B;AAEA,UAAM,eAAe,6BAAS,iBAAiB,UAAU,CAAC,iBAAiB;AACzE,YAAM,OAAO,YAAY;AACzB,kBAAY,UAAU;AAEtB,UAAI,CAAC,WAAW,WAAW,iBAAiB,SAAU;AAEtD,YAAM,iBACJ,iBAAiB,gBAChB,6BAAS,OAAO,SAAS,iBAAiB;AAE7C,UAAI,iBAAiB,UAAU;AAC7B,YAAI,mBAAmB,SAAS;AAC9B,uBAAa,mBAAmB,OAAO;AACvC,6BAAmB,UAAU;AAC7B;AAAA,QACF;AAEA,YAAI,KAAK,MAAM,qBAAqB,KAAK,CAAC,WAAW,SAAS;AAC5D,uBAAa;AAAA,QACf;AAAA,MACF,WAAW,gBAAgB;AACzB,YAAI,mBAAmB,QAAS;AAEhC,YAAI,sBAAsB,GAAG;AAC3B,0BAAgB;AAAA,QAClB,OAAO;AACL,6BAAmB,UAAU,WAAW,MAAM;AAC5C,+BAAmB,UAAU;AAC7B,4BAAgB;AAAA,UAClB,GAAG,iBAAiB;AAAA,QACtB;AAAA,MACF;AAAA,IACF,CAAC;AAED,WAAO,MAAM;AACX,mBAAa,OAAO;AACpB,UAAI,mBAAmB,SAAS;AAC9B,qBAAa,mBAAmB,OAAO;AACvC,2BAAmB,UAAU;AAAA,MAC/B;AAAA,IACF;AAAA,EACF,GAAG,CAAC,WAAW,iBAAiB,CAAC;AAEjC,QAAM,kBAAc;AAAA,IAClB,OAAO,MAAwB,MAAc,eAAoE;AAC/G,YAAM,UAAU,WAAW;AAC3B,UAAI,CAAC,QAAS,OAAM,IAAI,sBAAsB,UAAU,OAAO;AAE/D,YAAM,aAAa,UAAU,QAAQ,mBAAmB;AAExD,YAAM,aAAa,cAAc,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAErF,UAAI,YAAY;AACd,6BAAqB,QAAQ,IAAI,UAAU;AAC3C,cAAM,iBAA6B;AAAA,UACjC,IAAI;AAAA,UACJ,YAAY;AAAA,UACZ,WAAW,WAAW,QAAQ;AAAA,UAC9B,aAAa,WAAW,QAAQ;AAAA,UAChC;AAAA,UACA;AAAA,UACA,YAAa,cAAc,CAAC;AAAA,UAC5B,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,QACrC;AACA,oBAAY,CAAC,SAAS,CAAC,GAAG,MAAM,cAAc,CAAC;AAAA,MACjD;AAEA,YAAM,SAAS,MAAM;AACnB,cAAM,IAAI,WAAW;AACrB,YAAI,CAAC,EAAG,OAAM,IAAI,sBAAsB,UAAU,OAAO;AACzD,eAAO,EAAE,YAAY,MAAM,MAAM,YAAY,UAAU;AAAA,MACzD;AAEA,YAAM,gBAAgB,CAAC,aAAwC;AAC7D,YAAI,YAAY;AACd,+BAAqB,QAAQ,OAAO,UAAU;AAC9C,sBAAY,CAAC,SAAS;AACpB,kBAAM,eAAe,KAAK,KAAK,CAAC,MAAM,EAAE,OAAO,UAAU;AACzD,gBAAI,CAAC,aAAc,QAAO;AAC1B,mBAAO,KAAK;AAAA,cAAI,CAAC,MACf,EAAE,OAAO,aACL,EAAE,GAAG,GAAG,IAAI,SAAS,IAAI,YAAY,SAAS,WAAW,IACzD;AAAA,YACN;AAAA,UACF,CAAC;AAAA,QACH;AAAA,MACF;AAEA,YAAM,cAAc,CAAC,SAAwB;AAC3C,YAAI,YAAY;AACd,+BAAqB,QAAQ,OAAO,UAAU;AAC9C,sBAAY,CAAC,SAAS,KAAK,OAAO,CAAC,MAAM,EAAE,OAAO,UAAU,CAAC;AAAA,QAC/D;AAAA,MACF;AAGA,UAAI,SAAS,SAAS;AACpB,YAAI;AACF,gBAAM,WAAW,MAAM,SAAS,QAAQ,QAAQ,QAAQ,UAAU;AAClE,wBAAc,QAAQ;AACtB,iBAAO;AAAA,QACT,SAAS,KAAK;AACZ,cAAI,eAAe,gBAAgB;AACjC,wBAAY,GAAG;AACf,kBAAM;AAAA,UACR;AACA,cAAI,eAAe,uBAAuB,CAAC,iBAAiB,GAAG,GAAG;AAGhE,gBAAI,cAAc,eAAe,qBAAqB;AAAA,YAKtD,OAAO;AACL,0BAAY,GAAG;AAAA,YACjB;AACA,kBAAM;AAAA,UACR;AACA,sBAAY,GAAG;AACf,gBAAM;AAAA,QACR;AAAA,MACF;AAGA,UAAI;AACF,cAAM,WAAW,MAAM,OAAO;AAC9B,sBAAc,QAAQ;AACtB,eAAO;AAAA,MACT,SAAS,KAAK;AACZ,oBAAY,GAAG;AACf,cAAM;AAAA,MACR;AAAA,IACF;AAAA,IACA,CAAC,SAAS;AAAA,EACZ;AAEA,QAAM,oBAAgB;AAAA,IACpB,CAAC,iBAAyB;AACxB,eAAS,SAAS,OAAO,YAAY;AACrC,2BAAqB,QAAQ,OAAO,YAAY;AAChD,kBAAY,CAAC,SAAS,KAAK,OAAO,CAAC,MAAM,EAAE,OAAO,YAAY,CAAC;AAAA,IACjE;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,mBAAe;AAAA,IACnB,CAAC,iBAAyB;AACxB,eAAS,SAAS,MAAM,YAAY;AAAA,IACtC;AAAA,IACA,CAAC;AAAA,EACH;AAEA,QAAM,iBAAa,0BAAY,MAAM;AACnC,eAAW,SAAS,WAAW;AAC/B,eAAW,UAAU;AACrB,cAAU,cAAc;AAAA,EAC1B,GAAG,CAAC,CAAC;AAEL,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AQlcA,IAAAC,gBAAyD;AACzD,IAAAC,uBAAwD;AAOxD,IAAMC,+BAA8B;AACpC,IAAM,uBAAuB,CAAC,mBAAmB,iBAAiB,cAAc;AAgBzE,SAAS,UAAU,SAA4C;AACpE,QAAM,EAAE,QAAQ,WAAW,eAAe,UAAU,KAAK,IAAI;AAE7D,QAAM,CAAC,aAAa,cAAc,QAAI,wBAAS,CAAC;AAChD,QAAM,CAAC,eAAe,gBAAgB,QAAI,wBAAwB,IAAI;AACtE,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAuB,IAAI;AAErD,QAAM,cAAU,sBAA6B,IAAI;AACjD,QAAM,gBAAY,sBAAO,MAAM;AAC/B,YAAU,UAAU;AACpB,QAAM,kBAAc,sBAAuB,8BAAS,YAAY;AAChE,QAAM,yBAAqB,sBAA6C,IAAI;AAE5E,QAAM,oBACJ,OAAO,sBAAsB,8BAAS,OAAO,YAAYA,+BAA8B;AAEzF,QAAM,CAAC,SAAS,UAAU,QAAI,wBAAgC,IAAI;AAClE,QAAM,oBAAoB,OAAO,eAAe;AAChD,QAAM,kBACJ,OAAO,OAAO,eAAe,WAAW,OAAO,WAAW,iBAAiB;AAE7E,+BAAU,MAAM;AACd,QAAI,CAAC,mBAAmB;AACtB,iBAAW,IAAI;AACf;AAAA,IACF;AACA,QAAI,iBAAiB;AACnB,iBAAW,eAAe;AAC1B;AAAA,IACF;AACA,UAAM,IAAI,qBAAqB;AAC/B,eAAW,CAAC;AACZ,WAAO,MAAM;AACX,QAAE,QAAQ;AACV,iBAAW,IAAI;AAAA,IACjB;AAAA,EACF,GAAG,CAAC,mBAAmB,eAAe,CAAC;AAEvC,QAAM,cAAU,2BAAY,MAAM;AAChC,YAAQ,SAAS,MAAM;AACvB,YAAQ,UAAU;AAElB,UAAM,aAAa,iBAAiB,UAAU,QAAQ,UAAU,SAAS;AACzE,UAAM,gBAAgB,UAAU,QAAQ,WAAW,CAAC;AACpD,UAAM,MAAM,GAAG,UAAU,aAAa,SAAS,0BAA0B,mBAAmB,aAAa,CAAC;AAE1G,YAAQ,UAAU;AAAA,MAChB;AAAA,QACE;AAAA,QACA,SAAS;AAAA,QACT,kBAAkB,UAAU,QAAQ;AAAA,QACpC,cAAc;AAAA,QACd,gBAAgB,WAAW;AAAA,MAC7B;AAAA,MACA;AAAA,QACE,QAAQ,MAAM;AACZ,mBAAS,IAAI;AAAA,QACf;AAAA,QACA,SAAS,CAAC,WAAW,SAAS;AAC5B,cAAI;AACF,gBAAI,cAAc,mBAAmB;AACnC,oBAAM,WAAgC,KAAK,MAAM,IAAI;AACrD,6BAAe,SAAS,YAAY;AACpC,+BAAiB,SAAS,eAAe;AAAA,YAC3C,WAAW,cAAc,iBAAiB;AACxC,oBAAM,SAAuC,KAAK,MAAM,IAAI;AAC5D,6BAAe,CAAC,SAAS,OAAO,CAAC;AACjC,+BAAiB,OAAO,UAAU;AAAA,YACpC,WAAW,cAAc,gBAAgB;AACvC,oBAAM,QAAsD,KAAK,MAAM,IAAI;AAC3E,6BAAe,MAAM,YAAY;AAAA,YACnC;AAAA,UACF,QAAQ;AACN,qBAAS,IAAI,MAAM,kCAAkC,CAAC;AAAA,UACxD;AAAA,QACF;AAAA,QACA,SAAS,CAAC,QAAQ;AAChB,mBAAS,GAAG;AAAA,QACd;AAAA,QACA,UAAU,MAAM;AACd,kBAAQ,UAAU;AAAA,QACpB;AAAA,MACF;AAAA,IACF;AAAA,EACF,GAAG,CAAC,WAAW,eAAe,OAAO,CAAC;AAEtC,QAAM,iBAAa,2BAAY,MAAM;AACnC,YAAQ,SAAS,MAAM;AACvB,YAAQ,UAAU;AAAA,EACpB,GAAG,CAAC,CAAC;AAEL,+BAAU,MAAM;AACd,mBAAe,CAAC;AAChB,qBAAiB,IAAI;AACrB,aAAS,IAAI;AAEb,QAAI,CAAC,SAAS;AACZ,iBAAW;AACX;AAAA,IACF;AAGA,QAAI,qBAAqB,CAAC,QAAS;AAEnC,YAAQ;AAER,WAAO,MAAM;AACX,iBAAW;AACX,UAAI,mBAAmB,SAAS;AAC9B,qBAAa,mBAAmB,OAAO;AACvC,2BAAmB,UAAU;AAAA,MAC/B;AAAA,IACF;AAAA,EACF,GAAG,CAAC,WAAW,eAAe,SAAS,SAAS,UAAU,CAAC;AAE3D,+BAAU,MAAM;AACd,QAAI,CAAC,QAAS;AAEd,UAAM,eAAe,8BAAS,iBAAiB,UAAU,CAAC,iBAAiB;AACzE,YAAM,OAAO,YAAY;AACzB,kBAAY,UAAU;AAEtB,UAAI,CAAC,QAAQ,WAAW,iBAAiB,SAAU;AAEnD,YAAM,iBACJ,iBAAiB,gBAChB,8BAAS,OAAO,SAAS,iBAAiB;AAE7C,UAAI,iBAAiB,UAAU;AAC7B,YAAI,mBAAmB,SAAS;AAC9B,uBAAa,mBAAmB,OAAO;AACvC,6BAAmB,UAAU;AAC7B;AAAA,QACF;AAEA,YAAI,KAAK,MAAM,qBAAqB,KAAK,CAAC,QAAQ,SAAS;AACzD,kBAAQ;AAAA,QACV;AAAA,MACF,WAAW,gBAAgB;AACzB,YAAI,mBAAmB,QAAS;AAEhC,YAAI,sBAAsB,GAAG;AAC3B,qBAAW;AAAA,QACb,OAAO;AACL,6BAAmB,UAAU,WAAW,MAAM;AAC5C,+BAAmB,UAAU;AAC7B,uBAAW;AAAA,UACb,GAAG,iBAAiB;AAAA,QACtB;AAAA,MACF;AAAA,IACF,CAAC;AAED,WAAO,MAAM;AACX,mBAAa,OAAO;AACpB,UAAI,mBAAmB,SAAS;AAC9B,qBAAa,mBAAmB,OAAO;AACvC,2BAAmB,UAAU;AAAA,MAC/B;AAAA,IACF;AAAA,EACF,GAAG,CAAC,SAAS,mBAAmB,SAAS,UAAU,CAAC;AAEpD,SAAO,EAAE,aAAa,WAAW,cAAc,GAAG,eAAe,MAAM;AACzE;","names":["EventSource","DEFAULT_RECONNECT_DELAY_MS","import_react","import_react_native","DEFAULT_BACKGROUND_GRACE_MS"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/use-chat.ts","../src/errors.ts","../src/retry.ts","../src/resolve-url.ts","../src/sse-connection.ts","../src/session.ts","../src/network-monitor.ts","../src/message-queue.ts","../src/use-unread.ts"],"sourcesContent":["export { useChat } from './use-chat';\nexport { useUnread } from './use-unread';\nexport { createChatSession } from './session';\nexport { resolveServerUrl, createManifest } from './resolve-url';\nexport { createSSEConnection } from './sse-connection';\nexport { ChatDisconnectedError, ChannelClosedError, HttpError, RetryExhaustedError, QueueFullError } from './errors';\nexport { withRetry, isRetryableError, calculateBackoff, resolveRetryConfig } from './retry';\nexport { createNetworkMonitor } from './network-monitor';\nexport { createQueueStorage, createAsyncStorageAdapter } from './message-queue';\n\nexport type { ChatConfig, ChatStatus, UseChatOptions, UseChatReturn, ResilienceConfig } from './types';\nexport type { ChatSession, SessionCallbacks } from './session';\nexport type { UseUnreadOptions, UseUnreadReturn } from './use-unread';\nexport type { SSEConnection, SSEConnectionConfig, SSEConnectionCallbacks } from './sse-connection';\nexport type { RetryConfig } from './retry';\nexport type { NetworkMonitor } from './network-monitor';\nexport type { MessageSendStatus, QueuedMessage, QueueStorage } from './message-queue';\n\nexport type {\n ChatDomain,\n DefaultDomain,\n Message,\n Participant,\n MessageAttributes,\n SendMessageResponse,\n ChatManifest,\n ChatBucket,\n UnreadCountResponse,\n MarkReadRequest,\n SSEUnreadUpdateEvent,\n SSEUnreadClearEvent,\n SSEUnreadEvent,\n PediChat,\n PediRole,\n PediVehicle,\n PediLocation,\n PediParticipantMeta,\n PediMessageType,\n PediMessageAttributes,\n} from '@pedi/chika-types';\n","import { useEffect, useRef, useState, useCallback } from 'react';\nimport { AppState, Platform, type AppStateStatus } from 'react-native';\nimport type {\n ChatDomain,\n DefaultDomain,\n Message,\n Participant,\n MessageAttributes,\n SendMessageResponse,\n} from '@pedi/chika-types';\nimport type { UseChatOptions, UseChatReturn, ChatStatus } from './types';\nimport { ChatDisconnectedError, ChannelClosedError, QueueFullError, RetryExhaustedError } from './errors';\nimport { isRetryableError, resolveRetryConfig } from './retry';\nimport { createChatSession, type ChatSession, type SessionCallbacks } from './session';\nimport { createNetworkMonitor, type NetworkMonitor } from './network-monitor';\nimport { MessageQueue, type QueuedMessage } from './message-queue';\n\nconst DEFAULT_BACKGROUND_GRACE_MS = 2000;\nconst DEFAULT_MAX_QUEUE_SIZE = 50;\n\n// Module-scope queue registry, keyed by channelId. Survives component remounts.\nconst queueRegistry = new Map<string, { queue: MessageQueue; refCount: number }>();\n\n/**\n * React hook for real-time chat over SSE.\n * Manages connection lifecycle, AppState transitions, message deduplication, reconnection,\n * and optional network resilience (retry, offline queue, network monitoring).\n *\n * @template D - Chat domain type for role/message type narrowing. Defaults to DefaultDomain.\n */\nexport function useChat<D extends ChatDomain = DefaultDomain>(\n { config, channelId, profile, onMessage }: UseChatOptions<D>,\n): UseChatReturn<D> {\n const [messages, setMessages] = useState<Message<D>[]>([]);\n const [participants, setParticipants] = useState<Participant<D>[]>([]);\n const [status, setStatus] = useState<ChatStatus>('connecting');\n const [error, setError] = useState<Error | null>(null);\n const [pendingMessages, setPendingMessages] = useState<QueuedMessage[]>([]);\n\n const sessionRef = useRef<ChatSession<D> | null>(null);\n const disposedRef = useRef(false);\n const messagesRef = useRef(messages);\n messagesRef.current = messages;\n const statusRef = useRef(status);\n statusRef.current = status;\n const appStateRef = useRef<AppStateStatus>(AppState.currentState);\n const backgroundTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const profileRef = useRef(profile);\n profileRef.current = profile;\n const configRef = useRef(config);\n configRef.current = config;\n const onMessageRef = useRef(onMessage);\n onMessageRef.current = onMessage;\n const startingRef = useRef(false);\n const pendingOptimisticIds = useRef(new Set<string>());\n const [monitor, setMonitor] = useState<NetworkMonitor | null>(null);\n const monitorRef = useRef<NetworkMonitor | null>(null);\n monitorRef.current = monitor;\n const queueRef = useRef<MessageQueue | null>(null);\n\n const resilienceEnabled = config.resilience !== false;\n const queueEnabled =\n resilienceEnabled &&\n (typeof config.resilience === 'object' ? config.resilience.offlineQueue !== false : true);\n const retryConfig = resolveRetryConfig(config.resilience);\n const maxQueueSize =\n (resilienceEnabled && config.resilience && typeof config.resilience === 'object'\n ? config.resilience.maxQueueSize\n : undefined) ?? DEFAULT_MAX_QUEUE_SIZE;\n\n const backgroundGraceMs =\n config.backgroundGraceMs ?? (Platform.OS === 'android' ? DEFAULT_BACKGROUND_GRACE_MS : 0);\n\n // Resolve user-injected monitor (stable reference, no side effect)\n const injectedMonitor =\n typeof config.resilience === 'object' ? config.resilience.networkMonitor : undefined;\n\n // Create monitor in useEffect to avoid side effects during render.\n // Uses state (not ref) so the queue effect re-runs when monitor is ready.\n useEffect(() => {\n if (!resilienceEnabled) {\n setMonitor(null);\n return;\n }\n if (injectedMonitor) {\n setMonitor(injectedMonitor);\n return; // user owns lifecycle\n }\n const m = createNetworkMonitor();\n setMonitor(m);\n return () => {\n m.dispose();\n setMonitor(null);\n };\n }, [resilienceEnabled, injectedMonitor]);\n\n const callbacks: SessionCallbacks<D> = {\n onMessage: (message) => {\n if (disposedRef.current) return;\n let matchedOptimisticId: string | null = null;\n setMessages((prev: Message<D>[]) => {\n if (pendingOptimisticIds.current.size === 0) {\n return [...prev, message];\n }\n\n const optimisticIdx = prev.findIndex(\n (m) =>\n pendingOptimisticIds.current.has(m.id) &&\n m.sender_id === message.sender_id &&\n m.body === message.body &&\n m.type === message.type,\n );\n if (optimisticIdx !== -1) {\n matchedOptimisticId = prev[optimisticIdx]!.id;\n pendingOptimisticIds.current.delete(matchedOptimisticId);\n const next = [...prev];\n next[optimisticIdx] = message;\n return next;\n }\n return [...prev, message];\n });\n // Clean up any queued/failed queue entry — SSE confirmed delivery.\n // Done outside setMessages updater to avoid setState-in-setState.\n if (matchedOptimisticId) {\n const s = queueRef.current?.getStatus(matchedOptimisticId);\n if (s && s.status !== 'sending') {\n queueRef.current?.cancel(matchedOptimisticId);\n }\n }\n onMessageRef.current?.(message);\n },\n onStatusChange: (nextStatus) => {\n if (disposedRef.current) return;\n setStatus(nextStatus);\n if (nextStatus === 'connected') {\n setError(null);\n }\n },\n onError: (err) => {\n if (disposedRef.current) return;\n setError(err);\n },\n onResync: () => {\n if (disposedRef.current) return;\n startSession();\n },\n };\n\n async function startSession(): Promise<void> {\n if (startingRef.current) return;\n startingRef.current = true;\n\n const existing = sessionRef.current;\n if (existing) {\n existing.disconnect();\n sessionRef.current = null;\n }\n\n try {\n const session = await createChatSession<D>(\n configRef.current,\n channelId,\n profileRef.current,\n callbacks,\n monitorRef.current ?? undefined,\n );\n\n if (disposedRef.current) {\n session.disconnect();\n return;\n }\n\n sessionRef.current = session;\n setParticipants(session.initialParticipants);\n\n // Flush any messages queued while session was unavailable\n queueRef.current?.flush();\n\n // Re-merge any pending optimistic messages after resync\n if (pendingOptimisticIds.current.size > 0) {\n const pendingIds = pendingOptimisticIds.current;\n setMessages((prev) => {\n const pendingMsgs = prev.filter((m) => pendingIds.has(m.id));\n return [...session.initialMessages, ...pendingMsgs];\n });\n } else {\n setMessages(session.initialMessages);\n }\n } catch (err) {\n if (disposedRef.current) return;\n\n if (err instanceof ChannelClosedError) {\n setStatus('closed');\n setError(err);\n return;\n }\n\n setStatus('error');\n setError(err instanceof Error ? err : new Error(String(err)));\n } finally {\n startingRef.current = false;\n }\n }\n\n // Session lifecycle\n useEffect(() => {\n disposedRef.current = false;\n // Don't start until monitor is resolved (or resilience is off)\n if (resilienceEnabled && !monitor) return;\n startSession();\n\n return () => {\n disposedRef.current = true;\n if (statusRef.current === 'connected' && sessionRef.current) {\n const lastMsg = messagesRef.current[messagesRef.current.length - 1];\n if (lastMsg) {\n sessionRef.current.markAsRead(lastMsg.id).catch(() => {});\n }\n }\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n }\n sessionRef.current?.disconnect();\n sessionRef.current = null;\n };\n }, [channelId, monitor]);\n\n // Module-scope queue lifecycle (ref-counted, survives remounts)\n useEffect(() => {\n if (!queueEnabled || !monitor || !retryConfig) return;\n\n let entry = queueRegistry.get(channelId);\n if (!entry) {\n const queue = new MessageQueue({\n channelId,\n maxSize: maxQueueSize,\n retryConfig,\n networkMonitor: monitor,\n storage:\n typeof config.resilience === 'object'\n ? config.resilience.queueStorage\n : undefined,\n onError: (err) => {\n if (!disposedRef.current) setError(err);\n },\n onStatusChange: () => {\n if (!disposedRef.current) {\n setPendingMessages(queueRef.current?.getAll() ?? []);\n }\n },\n });\n entry = { queue, refCount: 0 };\n queueRegistry.set(channelId, entry);\n }\n entry.refCount++;\n queueRef.current = entry.queue;\n\n return () => {\n const e = queueRegistry.get(channelId);\n if (e) {\n e.refCount--;\n if (e.refCount <= 0) {\n e.queue.dispose();\n queueRegistry.delete(channelId);\n }\n }\n queueRef.current = null;\n };\n }, [channelId, queueEnabled, monitor]);\n\n // Network monitor: auto-rejoin on connectivity return when in error state\n useEffect(() => {\n if (!monitor || !resilienceEnabled) return;\n\n const unsub = monitor.subscribe((connected: boolean) => {\n if (connected && statusRef.current === 'error' && !startingRef.current) {\n startSession();\n }\n });\n\n return unsub;\n }, [channelId, resilienceEnabled, monitor]);\n\n // AppState lifecycle\n useEffect(() => {\n function teardownSession(): void {\n sessionRef.current?.disconnect();\n sessionRef.current = null;\n setStatus('disconnected');\n }\n\n const subscription = AppState.addEventListener('change', (nextAppState) => {\n const prev = appStateRef.current;\n appStateRef.current = nextAppState;\n\n if (!sessionRef.current && nextAppState !== 'active') return;\n\n const shouldTeardown =\n nextAppState === 'background' ||\n (Platform.OS === 'ios' && nextAppState === 'inactive');\n\n if (nextAppState === 'active') {\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n return;\n }\n\n if (prev.match(/inactive|background/) && !sessionRef.current) {\n startSession();\n }\n } else if (shouldTeardown) {\n if (backgroundTimerRef.current) return;\n\n if (backgroundGraceMs === 0) {\n teardownSession();\n } else {\n backgroundTimerRef.current = setTimeout(() => {\n backgroundTimerRef.current = null;\n teardownSession();\n }, backgroundGraceMs);\n }\n }\n });\n\n return () => {\n subscription.remove();\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n }\n };\n }, [channelId, backgroundGraceMs]);\n\n const sendMessage = useCallback(\n async (type: D['messageType'], body: string, attributes?: MessageAttributes<D>): Promise<SendMessageResponse> => {\n const session = sessionRef.current;\n if (!session) throw new ChatDisconnectedError(statusRef.current);\n\n const optimistic = configRef.current.optimisticSend !== false;\n // Serves as both the optimistic message ID and the server-side idempotency key\n const messageKey = `optimistic_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\n\n if (optimistic) {\n pendingOptimisticIds.current.add(messageKey);\n const provisionalMsg: Message<D> = {\n id: messageKey,\n channel_id: channelId,\n sender_id: profileRef.current.id,\n sender_role: profileRef.current.role,\n type,\n body,\n attributes: (attributes ?? {}) as MessageAttributes<D>,\n created_at: new Date().toISOString(),\n };\n setMessages((prev) => [...prev, provisionalMsg]);\n }\n\n const doSend = () => {\n const s = sessionRef.current;\n if (!s) throw new ChatDisconnectedError(statusRef.current);\n return s.sendMessage(type, body, attributes, messageKey);\n };\n\n const handleSuccess = (response: SendMessageResponse): void => {\n if (optimistic) {\n pendingOptimisticIds.current.delete(messageKey);\n setMessages((prev) => {\n const stillPending = prev.some((m) => m.id === messageKey);\n if (!stillPending) return prev;\n return prev.map((m) =>\n m.id === messageKey\n ? { ...m, id: response.id, created_at: response.created_at }\n : m,\n );\n });\n }\n };\n\n const handleError = (_err: unknown): void => {\n if (optimistic) {\n pendingOptimisticIds.current.delete(messageKey);\n setMessages((prev) => prev.filter((m) => m.id !== messageKey));\n }\n };\n\n // Queue path: enqueue and let queue handle retry + offline\n if (queueRef.current) {\n try {\n const response = await queueRef.current.enqueue(doSend, messageKey);\n handleSuccess(response);\n return response;\n } catch (err) {\n if (err instanceof QueueFullError) {\n handleError(err);\n throw err;\n }\n if (err instanceof RetryExhaustedError || !isRetryableError(err)) {\n // For failed messages: if optimistic, mark as failed (keep in UI)\n // The pendingMessages state shows the status\n if (optimistic && err instanceof RetryExhaustedError) {\n // Keep optimistic message visible — queue tracks status as 'failed'.\n // Keep messageKey in pendingOptimisticIds so SSE reconciliation can\n // still match if the server eventually received the message.\n // Removed on explicit cancelMessage() call.\n } else {\n handleError(err);\n }\n throw err;\n }\n handleError(err);\n throw err;\n }\n }\n\n // Non-queue path: direct send (session.ts handles retry if enabled)\n try {\n const response = await doSend();\n handleSuccess(response);\n return response;\n } catch (err) {\n handleError(err);\n throw err;\n }\n },\n [channelId],\n );\n\n const cancelMessage = useCallback(\n (optimisticId: string) => {\n queueRef.current?.cancel(optimisticId);\n pendingOptimisticIds.current.delete(optimisticId);\n setMessages((prev) => prev.filter((m) => m.id !== optimisticId));\n },\n [],\n );\n\n const retryMessage = useCallback(\n (optimisticId: string) => {\n queueRef.current?.retry(optimisticId);\n },\n [],\n );\n\n const disconnect = useCallback(() => {\n sessionRef.current?.disconnect();\n sessionRef.current = null;\n setStatus('disconnected');\n }, []);\n\n return {\n messages,\n participants,\n status,\n error,\n sendMessage,\n disconnect,\n pendingMessages,\n cancelMessage,\n retryMessage,\n };\n}\n","import type { ChatStatus } from './types';\n\nexport class ChatDisconnectedError extends Error {\n constructor(public readonly status: ChatStatus) {\n super(`Cannot send message while ${status}`);\n this.name = 'ChatDisconnectedError';\n }\n}\n\nexport class ChannelClosedError extends Error {\n constructor(public readonly channelId: string) {\n super(`Channel ${channelId} is closed`);\n this.name = 'ChannelClosedError';\n }\n}\n\nexport class HttpError extends Error {\n constructor(\n public readonly status: number,\n public readonly body: string,\n public readonly retryAfter?: number,\n ) {\n super(`HTTP ${status}: ${body}`);\n this.name = 'HttpError';\n }\n}\n\nexport class RetryExhaustedError extends Error {\n constructor(\n public readonly operation: string,\n public readonly attempts: number,\n public readonly lastError: Error,\n ) {\n super(`${operation} failed after ${attempts} attempts: ${lastError.message}`);\n this.name = 'RetryExhaustedError';\n }\n}\n\nexport class QueueFullError extends Error {\n constructor(public readonly maxSize: number) {\n super(`Message queue full (max ${maxSize})`);\n this.name = 'QueueFullError';\n }\n}\n","import {\n HttpError,\n RetryExhaustedError,\n ChannelClosedError,\n ChatDisconnectedError,\n QueueFullError,\n} from './errors';\n\nexport interface RetryConfig {\n maxAttempts: number;\n baseDelayMs: number;\n maxDelayMs: number;\n jitterFactor: number;\n}\n\nexport const DEFAULT_RETRY_CONFIG: RetryConfig = {\n maxAttempts: 3,\n baseDelayMs: 1000,\n maxDelayMs: 10000,\n jitterFactor: 0.3,\n};\n\nexport function calculateBackoff(attempt: number, config: RetryConfig): number {\n const delay = Math.min(config.baseDelayMs * 2 ** attempt, config.maxDelayMs);\n const jitter = 1 + (Math.random() * 2 - 1) * config.jitterFactor;\n return Math.round(delay * jitter);\n}\n\nexport function isRetryableError(error: unknown): boolean {\n if (error instanceof ChannelClosedError) return false;\n if (error instanceof ChatDisconnectedError) return false;\n if (error instanceof QueueFullError) return false;\n if (error instanceof DOMException && error.name === 'AbortError') return false;\n\n if (error instanceof HttpError) {\n const { status } = error;\n if (status === 408 || status === 429) return true;\n if (status >= 500) return true;\n return false;\n }\n\n // TypeError from fetch = network failure\n if (error instanceof TypeError) return true;\n\n return false;\n}\n\nexport function sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));\n return;\n }\n\n const timer = setTimeout(resolve, ms);\n\n signal?.addEventListener(\n 'abort',\n () => {\n clearTimeout(timer);\n reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));\n },\n { once: true },\n );\n });\n}\n\nexport function resolveRetryConfig(\n resilience: { retry?: Partial<RetryConfig> | false } | false | undefined,\n): RetryConfig | null {\n if (resilience === false) return null;\n if (!resilience || resilience.retry === undefined) return DEFAULT_RETRY_CONFIG;\n if (resilience.retry === false) return null;\n return { ...DEFAULT_RETRY_CONFIG, ...resilience.retry };\n}\n\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n config: RetryConfig = DEFAULT_RETRY_CONFIG,\n signal?: AbortSignal,\n): Promise<T> {\n let lastError: Error | undefined;\n\n for (let attempt = 0; attempt < config.maxAttempts; attempt++) {\n if (signal?.aborted) {\n throw signal.reason ?? new DOMException('Aborted', 'AbortError');\n }\n\n try {\n return await fn();\n } catch (err) {\n lastError = err instanceof Error ? err : new Error(String(err));\n\n if (!isRetryableError(err)) throw lastError;\n\n if (attempt < config.maxAttempts - 1) {\n // Respect Retry-After header for 429 responses\n const delayMs =\n err instanceof HttpError && err.retryAfter != null\n ? err.retryAfter * 1000\n : calculateBackoff(attempt, config);\n\n await sleep(delayMs, signal);\n }\n }\n }\n\n throw new RetryExhaustedError(\n 'operation',\n config.maxAttempts,\n lastError!,\n );\n}\n","import type { ChatManifest } from '@pedi/chika-types';\n\n/**\n * Creates a single-server manifest. Use this when all channels route to the same server.\n *\n * @example\n * ```ts\n * const config: ChatConfig = { manifest: createManifest('https://chat.example.com') };\n * ```\n */\nexport function createManifest(serverUrl: string): ChatManifest {\n return { buckets: [{ group: 'default', range: [0, 99], server_url: serverUrl }] };\n}\n\nexport function resolveServerUrl(manifest: ChatManifest, channelId: string): string {\n const hash = [...channelId].reduce((sum, c) => sum + c.charCodeAt(0), 0) % 100;\n const bucket = manifest.buckets.find(\n (b) => hash >= b.range[0] && hash <= b.range[1],\n );\n if (!bucket) throw new Error(`No chat bucket for hash ${hash}`);\n return bucket.server_url;\n}\n","import EventSource from 'react-native-sse';\nimport { calculateBackoff, type RetryConfig } from './retry';\nimport type { NetworkMonitor } from './network-monitor';\n\nconst DEFAULT_RECONNECT_DELAY_MS = 3000;\n\nexport interface SSEConnectionConfig {\n url: string;\n headers?: Record<string, string>;\n reconnectDelayMs?: number;\n lastEventId?: string;\n customEvents?: string[];\n networkMonitor?: NetworkMonitor;\n}\n\nexport interface SSEConnectionCallbacks {\n onOpen?: () => void;\n onEvent: (eventType: string, data: string, lastEventId?: string) => void;\n onError?: (error: Error) => void;\n onClosed?: () => void;\n onReconnecting?: () => void;\n}\n\nexport interface SSEConnection {\n close: () => void;\n reconnectImmediate: () => void;\n}\n\nexport function createSSEConnection(\n config: SSEConnectionConfig,\n callbacks: SSEConnectionCallbacks,\n): SSEConnection {\n const baseDelay = config.reconnectDelayMs ?? DEFAULT_RECONNECT_DELAY_MS;\n const customEvents = config.customEvents ?? [];\n const monitor = config.networkMonitor;\n\n const backoffConfig: RetryConfig = {\n maxAttempts: Infinity,\n baseDelayMs: baseDelay,\n maxDelayMs: 30000,\n jitterFactor: 0.3,\n };\n\n let currentLastEventId = config.lastEventId;\n let es: EventSource<string> | null = null;\n let disposed = false;\n let reconnectTimer: ReturnType<typeof setTimeout> | null = null;\n let attempt = 0;\n let waitAbort: AbortController | null = null;\n\n function cleanup(): void {\n if (es) {\n es.removeAllEventListeners();\n es.close();\n es = null;\n }\n if (reconnectTimer) {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n }\n if (waitAbort) {\n waitAbort.abort();\n waitAbort = null;\n }\n }\n\n async function scheduleReconnect(): Promise<void> {\n if (disposed || reconnectTimer || waitAbort) return;\n callbacks.onReconnecting?.();\n cleanup();\n\n // Wait for network if monitor available\n if (monitor && !monitor.isConnected()) {\n waitAbort = new AbortController();\n try {\n await monitor.waitForOnline(waitAbort.signal);\n } catch {\n return; // aborted via dispose\n }\n waitAbort = null;\n if (disposed) return;\n }\n\n const delay = calculateBackoff(attempt++, backoffConfig);\n reconnectTimer = setTimeout(() => {\n reconnectTimer = null;\n connect();\n }, delay);\n }\n\n function connect(): void {\n if (disposed) return;\n\n es = new EventSource<string>(config.url, {\n headers: {\n ...config.headers,\n ...(currentLastEventId && { 'Last-Event-ID': currentLastEventId }),\n },\n pollingInterval: 0,\n });\n\n es.addEventListener('open', () => {\n if (disposed) return;\n attempt = 0;\n callbacks.onOpen?.();\n });\n\n es.addEventListener('message', (event) => {\n if (disposed || !event.data) return;\n if (event.lastEventId) {\n currentLastEventId = event.lastEventId;\n }\n callbacks.onEvent('message', event.data, event.lastEventId ?? undefined);\n });\n\n for (const eventName of customEvents) {\n es.addEventListener(eventName, (event) => {\n if (disposed) return;\n callbacks.onEvent(eventName, event.data ?? '', undefined);\n });\n }\n\n es.addEventListener('error', (event) => {\n if (disposed) return;\n\n const msg = 'message' in event ? String(event.message) : '';\n\n if (msg.includes('Channel is closed') || msg.includes('410')) {\n callbacks.onClosed?.();\n cleanup();\n disposed = true;\n return;\n }\n\n if (msg) callbacks.onError?.(new Error(msg));\n\n scheduleReconnect();\n });\n\n es.addEventListener('close', () => {\n if (disposed) return;\n scheduleReconnect();\n });\n }\n\n connect();\n\n return {\n close: () => {\n disposed = true;\n cleanup();\n },\n\n reconnectImmediate: () => {\n if (disposed) return;\n cleanup(); // does NOT set disposed\n attempt = 0;\n connect();\n },\n };\n}\n","import type {\n ChatDomain,\n DefaultDomain,\n Participant,\n Message,\n JoinResponse,\n SendMessageRequest,\n SendMessageResponse,\n MessageAttributes,\n} from '@pedi/chika-types';\nimport type { ChatConfig, ChatStatus } from './types';\nimport { ChatDisconnectedError, ChannelClosedError, HttpError } from './errors';\nimport { resolveServerUrl } from './resolve-url';\nimport { createSSEConnection, type SSEConnection } from './sse-connection';\nimport { withRetry, resolveRetryConfig, type RetryConfig } from './retry';\nimport type { NetworkMonitor } from './network-monitor';\n\nconst DEFAULT_RECONNECT_DELAY_MS = 3000;\nconst MAX_SEEN_IDS = 500;\n\nconst MARK_READ_RETRY_CONFIG: RetryConfig = {\n maxAttempts: 2,\n baseDelayMs: 500,\n maxDelayMs: 2000,\n jitterFactor: 0.3,\n};\n\nexport interface SessionCallbacks<D extends ChatDomain = DefaultDomain> {\n onMessage: (message: Message<D>) => void;\n onStatusChange: (status: ChatStatus) => void;\n onError: (error: Error) => void;\n onResync: () => void;\n}\n\nexport interface ChatSession<D extends ChatDomain = DefaultDomain> {\n serviceUrl: string;\n channelId: string;\n initialParticipants: Participant<D>[];\n initialMessages: Message<D>[];\n networkMonitor: NetworkMonitor | null;\n sendMessage: (\n type: D['messageType'],\n body: string,\n attributes?: MessageAttributes<D>,\n idempotencyKey?: string,\n ) => Promise<SendMessageResponse>;\n markAsRead: (messageId: string) => Promise<void>;\n disconnect: () => void;\n}\n\nfunction parseRetryAfter(res: Response): number | undefined {\n const header = res.headers.get('Retry-After');\n if (!header) return undefined;\n const seconds = Number(header);\n return Number.isFinite(seconds) ? seconds : undefined;\n}\n\nasync function throwHttpError(res: Response): Promise<never> {\n const body = await res.text().catch(() => '');\n throw new HttpError(res.status, body, parseRetryAfter(res));\n}\n\nexport async function createChatSession<D extends ChatDomain = DefaultDomain>(\n config: ChatConfig,\n channelId: string,\n profile: Participant<D>,\n callbacks: SessionCallbacks<D>,\n networkMonitor?: NetworkMonitor,\n): Promise<ChatSession<D>> {\n const serviceUrl = resolveServerUrl(config.manifest, channelId);\n const customHeaders = config.headers ?? {};\n const reconnectDelay = config.reconnectDelayMs ?? DEFAULT_RECONNECT_DELAY_MS;\n const retryConfig = resolveRetryConfig(config.resilience);\n\n const sessionAbort = new AbortController();\n\n callbacks.onStatusChange('connecting');\n\n const joinFn = async (): Promise<JoinResponse<D>> => {\n const joinRes = await fetch(`${serviceUrl}/channels/${channelId}/join`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...customHeaders },\n body: JSON.stringify(profile),\n signal: sessionAbort.signal,\n });\n\n if (joinRes.status === 410) {\n throw new ChannelClosedError(channelId);\n }\n\n if (!joinRes.ok) {\n await throwHttpError(joinRes);\n }\n\n return joinRes.json();\n };\n\n const joinData = retryConfig\n ? await withRetry(joinFn, retryConfig, sessionAbort.signal)\n : await joinFn();\n\n const { messages, participants, joined_at }: JoinResponse<D> = joinData;\n\n let lastEventId =\n messages.length > 0 ? messages[messages.length - 1]!.id : undefined;\n\n const joinedAt = joined_at;\n\n const seenMessageIds = new Set<string>(messages.map((m) => m.id));\n\n let sseConn: SSEConnection | null = null;\n let disposed = false;\n\n const TRIM_THRESHOLD = MAX_SEEN_IDS * 1.5;\n\n function trimSeenIds(): void {\n if (seenMessageIds.size <= TRIM_THRESHOLD) return;\n const ids = [...seenMessageIds];\n seenMessageIds.clear();\n for (const id of ids.slice(-MAX_SEEN_IDS)) {\n seenMessageIds.add(id);\n }\n }\n\n function connect(): void {\n if (disposed) return;\n\n const streamUrl = lastEventId\n ? `${serviceUrl}/channels/${channelId}/stream`\n : `${serviceUrl}/channels/${channelId}/stream?since_time=${encodeURIComponent(joinedAt)}`;\n\n sseConn = createSSEConnection(\n {\n url: streamUrl,\n headers: customHeaders,\n reconnectDelayMs: reconnectDelay,\n lastEventId,\n customEvents: ['resync'],\n networkMonitor,\n },\n {\n onOpen: () => {\n if (!disposed) callbacks.onStatusChange('connected');\n },\n onEvent: (eventType, data, eventId) => {\n if (disposed) return;\n\n if (eventType === 'message') {\n let message: Message<D>;\n try {\n message = JSON.parse(data);\n } catch {\n callbacks.onError(new Error('Failed to parse SSE message'));\n return;\n }\n\n if (eventId) {\n lastEventId = eventId;\n }\n\n if (seenMessageIds.has(message.id)) return;\n seenMessageIds.add(message.id);\n trimSeenIds();\n\n callbacks.onMessage(message);\n } else if (eventType === 'resync') {\n // Let onResync → startSession handle full session recreation.\n // Don't call reconnectImmediate() — startSession disconnects this\n // session anyway, so the reconnect would be immediately thrown away.\n callbacks.onResync();\n }\n },\n onError: (err) => {\n if (!disposed) callbacks.onError(err);\n },\n onClosed: () => {\n callbacks.onStatusChange('closed');\n disposed = true;\n },\n onReconnecting: () => {\n if (!disposed) callbacks.onStatusChange('reconnecting');\n },\n },\n );\n }\n\n connect();\n\n return {\n serviceUrl,\n channelId,\n initialParticipants: participants,\n initialMessages: messages,\n networkMonitor: networkMonitor ?? null,\n\n sendMessage: async (type, body, attributes, idempotencyKey) => {\n if (disposed) throw new ChatDisconnectedError('disconnected');\n\n const payload: SendMessageRequest<D> = {\n sender_id: profile.id,\n type,\n body,\n attributes,\n ...(idempotencyKey ? { idempotency_key: idempotencyKey } : {}),\n };\n\n const sendFn = async (): Promise<SendMessageResponse> => {\n const res = await fetch(\n `${serviceUrl}/channels/${channelId}/messages`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...customHeaders },\n body: JSON.stringify(payload),\n signal: sessionAbort.signal,\n },\n );\n\n if (!res.ok) {\n await throwHttpError(res);\n }\n\n return res.json();\n };\n\n const response = retryConfig\n ? await withRetry(sendFn, retryConfig, sessionAbort.signal)\n : await sendFn();\n\n seenMessageIds.add(response.id);\n return response;\n },\n\n markAsRead: async (messageId: string) => {\n const readFn = async (): Promise<void> => {\n const res = await fetch(`${serviceUrl}/channels/${channelId}/read`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...customHeaders },\n body: JSON.stringify({\n participant_id: profile.id,\n message_id: messageId,\n }),\n signal: sessionAbort.signal,\n });\n if (!res.ok) {\n await throwHttpError(res);\n }\n };\n\n if (retryConfig) {\n try {\n await withRetry(readFn, MARK_READ_RETRY_CONFIG, sessionAbort.signal);\n } catch (err) {\n // Surface non-retryable errors (403, 404) for dev diagnostics\n if (err instanceof HttpError) {\n callbacks.onError(err);\n }\n // Swallow RetryExhaustedError — markAsRead is best-effort\n }\n } else {\n await readFn();\n }\n },\n\n disconnect: () => {\n disposed = true;\n sessionAbort.abort();\n sseConn?.close();\n sseConn = null;\n callbacks.onStatusChange('disconnected');\n },\n };\n}\n","export interface NetworkMonitor {\n isConnected(): boolean;\n subscribe(cb: (connected: boolean) => void): () => void;\n waitForOnline(signal?: AbortSignal): Promise<void>;\n dispose(): void;\n}\n\nfunction createStubMonitor(): NetworkMonitor {\n return {\n isConnected: () => true,\n subscribe: () => () => {},\n waitForOnline: () => Promise.resolve(),\n dispose: () => {},\n };\n}\n\nlet resolvedNetInfo: any = undefined;\nlet netInfoResolved = false;\n\nfunction getNetInfo(): any {\n if (netInfoResolved) return resolvedNetInfo;\n netInfoResolved = true;\n try {\n resolvedNetInfo = require('@react-native-community/netinfo');\n } catch {\n resolvedNetInfo = null;\n }\n return resolvedNetInfo;\n}\n\nexport function createNetworkMonitor(): NetworkMonitor {\n const NetInfo = getNetInfo();\n if (!NetInfo) return createStubMonitor();\n\n const netInfoModule = NetInfo.default ?? NetInfo;\n\n let connected = true;\n const listeners = new Set<(connected: boolean) => void>();\n let unsubscribeNetInfo: (() => void) | null = null;\n\n unsubscribeNetInfo = netInfoModule.addEventListener(\n (state: { isConnected: boolean | null }) => {\n const next = state.isConnected !== false;\n if (next === connected) return;\n connected = next;\n for (const cb of listeners) {\n cb(connected);\n }\n },\n );\n\n return {\n isConnected: () => connected,\n\n subscribe: (cb) => {\n listeners.add(cb);\n return () => {\n listeners.delete(cb);\n };\n },\n\n waitForOnline: (signal?) => {\n if (connected) return Promise.resolve();\n\n return new Promise<void>((resolve, reject) => {\n if (signal?.aborted) {\n reject(signal.reason ?? new DOMException('Aborted', 'AbortError'));\n return;\n }\n\n const unsub = (): void => {\n listeners.delete(handler);\n signal?.removeEventListener('abort', onAbort);\n };\n\n const handler = (isOnline: boolean): void => {\n if (isOnline) {\n unsub();\n resolve();\n }\n };\n\n const onAbort = (): void => {\n unsub();\n reject(signal!.reason ?? new DOMException('Aborted', 'AbortError'));\n };\n\n listeners.add(handler);\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n },\n\n dispose: () => {\n listeners.clear();\n unsubscribeNetInfo?.();\n unsubscribeNetInfo = null;\n },\n };\n}\n","import type { SendMessageResponse } from '@pedi/chika-types';\nimport { withRetry, type RetryConfig } from './retry';\nimport { QueueFullError, ChatDisconnectedError } from './errors';\nimport type { NetworkMonitor } from './network-monitor';\n\nexport interface QueueStorage {\n getItem(key: string): Promise<string | null>;\n setItem(key: string, value: string): Promise<void>;\n removeItem(key: string): Promise<void>;\n}\n\nlet resolvedStorage: { type: string; adapter: QueueStorage } | null | undefined;\n\nfunction tryRequire(name: string): any {\n try {\n return require(name);\n } catch {\n return null;\n }\n}\n\nfunction createMmkvAdapter(mod: any): QueueStorage {\n const MMKV = mod.MMKV ?? mod.default?.MMKV ?? mod;\n const instance = new MMKV({ id: 'chika-queue' });\n return {\n getItem: (key) => Promise.resolve(instance.getString(key) ?? null),\n setItem: (key, value) => { instance.set(key, value); return Promise.resolve(); },\n removeItem: (key) => { instance.delete(key); return Promise.resolve(); },\n };\n}\n\nfunction createAsyncStorageAdapterFrom(mod: any): QueueStorage {\n const storage = mod.default ?? mod;\n return {\n getItem: (key) => storage.getItem(key),\n setItem: (key, value) => storage.setItem(key, value),\n removeItem: (key) => storage.removeItem(key),\n };\n}\n\n/**\n * Auto-detect the best available storage for queue persistence.\n * Priority: react-native-mmkv (fastest, sync) > AsyncStorage > null.\n * Returns `null` if neither is installed — no hard dependencies.\n *\n * Usage:\n * ```ts\n * import { createQueueStorage } from '@pedi/chika-sdk';\n *\n * const config: ChatConfig = {\n * resilience: {\n * queueStorage: createQueueStorage() ?? undefined,\n * },\n * };\n * ```\n */\nexport function createQueueStorage(): QueueStorage | null {\n if (resolvedStorage !== undefined) return resolvedStorage?.adapter ?? null;\n\n // Priority 1: MMKV (synchronous, fastest)\n const mmkv = tryRequire('react-native-mmkv');\n if (mmkv) {\n try {\n const adapter = createMmkvAdapter(mmkv);\n resolvedStorage = { type: 'mmkv', adapter };\n return adapter;\n } catch {\n // MMKV instantiation can fail if native module isn't linked\n }\n }\n\n // Priority 2: AsyncStorage\n const asyncStorage = tryRequire('@react-native-async-storage/async-storage');\n if (asyncStorage) {\n const adapter = createAsyncStorageAdapterFrom(asyncStorage);\n resolvedStorage = { type: 'async-storage', adapter };\n return adapter;\n }\n\n resolvedStorage = null;\n return null;\n}\n\n/**\n * Creates a QueueStorage adapter backed by `@react-native-async-storage/async-storage`.\n * Returns `null` if the package is not installed.\n */\nexport function createAsyncStorageAdapter(): QueueStorage | null {\n const mod = tryRequire('@react-native-async-storage/async-storage');\n if (!mod) return null;\n return createAsyncStorageAdapterFrom(mod);\n}\n\nexport type MessageSendStatus = 'sending' | 'queued' | 'failed';\n\nexport interface QueuedMessage {\n optimisticId: string;\n status: MessageSendStatus;\n error?: Error;\n retryCount: number;\n}\n\ntype SendFn = () => Promise<SendMessageResponse>;\n\ninterface QueueEntry {\n optimisticId: string;\n sendFn: SendFn;\n status: MessageSendStatus;\n error?: Error;\n retryCount: number;\n abort: AbortController;\n resolve: (value: SendMessageResponse) => void;\n reject: (reason: Error) => void;\n}\n\n/** Serializable subset persisted to storage. */\ninterface PersistedEntry {\n optimisticId: string;\n retryCount: number;\n}\n\nexport interface MessageQueueConfig {\n channelId: string;\n maxSize: number;\n retryConfig: RetryConfig;\n networkMonitor: NetworkMonitor;\n storage?: QueueStorage;\n onError?: (error: Error) => void;\n onStatusChange?: () => void;\n}\n\nexport class MessageQueue {\n private entries: QueueEntry[] = [];\n private flushing = false;\n private unsubNetwork: (() => void) | null = null;\n private readonly storageKey: string;\n\n constructor(private readonly config: MessageQueueConfig) {\n this.storageKey = `chika_queue_${config.channelId}`;\n\n this.unsubNetwork = config.networkMonitor.subscribe((connected) => {\n if (connected) this.flush();\n });\n }\n\n /**\n * Restore queued messages from persistent storage on cold start.\n * Restored entries are fire-and-forget — there is no caller awaiting their\n * promise (the original enqueue() caller is gone after app restart).\n * Success/failure is reported via onStatusChange/onError callbacks.\n */\n async restore(\n rebuildSendFn: (optimisticId: string) => SendFn | null,\n ): Promise<void> {\n if (!this.config.storage) return;\n\n try {\n const raw = await this.config.storage.getItem(this.storageKey);\n if (!raw) return;\n\n const persisted: PersistedEntry[] = JSON.parse(raw);\n for (const entry of persisted) {\n const sendFn = rebuildSendFn(entry.optimisticId);\n if (!sendFn) continue;\n\n const abort = new AbortController();\n this.entries.push({\n optimisticId: entry.optimisticId,\n sendFn,\n status: 'queued',\n retryCount: entry.retryCount,\n abort,\n // No-op: restored entries have no caller awaiting the promise.\n resolve: () => {},\n reject: () => {},\n });\n }\n\n if (this.entries.length > 0) {\n this.config.onStatusChange?.();\n this.flush();\n }\n } catch {\n this.config.onError?.(new Error('Failed to restore message queue from storage'));\n }\n }\n\n get pendingCount(): number {\n return this.entries.length;\n }\n\n getAll(): QueuedMessage[] {\n return this.entries.map((e) => ({\n optimisticId: e.optimisticId,\n status: e.status,\n error: e.error,\n retryCount: e.retryCount,\n }));\n }\n\n getStatus(optimisticId: string): QueuedMessage | undefined {\n const entry = this.entries.find((e) => e.optimisticId === optimisticId);\n if (!entry) return undefined;\n return {\n optimisticId: entry.optimisticId,\n status: entry.status,\n error: entry.error,\n retryCount: entry.retryCount,\n };\n }\n\n enqueue(sendFn: SendFn, optimisticId: string): Promise<SendMessageResponse> {\n if (this.entries.length >= this.config.maxSize) {\n throw new QueueFullError(this.config.maxSize);\n }\n\n const abort = new AbortController();\n\n return new Promise<SendMessageResponse>((resolve, reject) => {\n const entry: QueueEntry = {\n optimisticId,\n sendFn,\n status: 'queued',\n retryCount: 0,\n abort,\n resolve,\n reject,\n };\n\n this.entries.push(entry);\n this.config.onStatusChange?.();\n this.persist();\n this.flush();\n });\n }\n\n cancel(optimisticId: string): void {\n const idx = this.entries.findIndex((e) => e.optimisticId === optimisticId);\n if (idx === -1) return;\n\n const entry = this.entries[idx]!;\n entry.abort.abort();\n entry.reject(new DOMException('Cancelled', 'AbortError'));\n this.entries.splice(idx, 1);\n this.config.onStatusChange?.();\n this.persist();\n }\n\n retry(optimisticId: string): void {\n const entry = this.entries.find((e) => e.optimisticId === optimisticId);\n if (!entry || entry.status !== 'failed') return;\n\n entry.status = 'queued';\n entry.error = undefined;\n entry.abort = new AbortController();\n this.config.onStatusChange?.();\n this.flush();\n }\n\n dispose(): void {\n this.unsubNetwork?.();\n this.unsubNetwork = null;\n\n for (const entry of this.entries) {\n entry.abort.abort();\n entry.reject(new DOMException('Queue disposed', 'AbortError'));\n }\n this.entries = [];\n }\n\n /**\n * Trigger a flush of queued messages. Returns immediately if a flush is\n * already in progress or the network is offline. The in-progress flush\n * will pick up any newly queued entries via its while loop.\n */\n async flush(): Promise<void> {\n if (this.flushing) return;\n if (!this.config.networkMonitor.isConnected()) return;\n\n this.flushing = true;\n let awaitingSession = false;\n\n try {\n while (this.entries.length > 0) {\n // Only pick up 'queued' entries — 'sending' means a previous flush is\n // mid-request. Server-side idempotency key prevents duplicates if the\n // prior request actually succeeded but we never got the response.\n const entry = this.entries.find((e) => e.status === 'queued');\n if (!entry) break;\n if (!this.config.networkMonitor.isConnected()) break;\n\n entry.status = 'sending';\n this.config.onStatusChange?.();\n\n try {\n const result = await withRetry(\n entry.sendFn,\n this.config.retryConfig,\n entry.abort.signal,\n );\n\n entry.resolve(result);\n const idx = this.entries.indexOf(entry);\n if (idx !== -1) this.entries.splice(idx, 1);\n this.config.onStatusChange?.();\n this.persist();\n } catch (err) {\n if (entry.abort.signal.aborted) continue; // cancelled, already removed\n\n // Session not yet reconnected — revert to 'queued' and stop flushing.\n // flush() will be called again when the session is re-established\n // via the explicit flush() call in use-chat.ts startSession().\n if (err instanceof ChatDisconnectedError) {\n entry.status = 'queued';\n this.config.onStatusChange?.();\n awaitingSession = true;\n break;\n }\n\n entry.status = 'failed';\n entry.error = err instanceof Error ? err : new Error(String(err));\n entry.retryCount++;\n this.config.onStatusChange?.();\n this.persist();\n\n // Don't block the queue on a failed entry — skip to next\n }\n }\n } finally {\n this.flushing = false;\n // Re-check: entries may have been added while we were flushing.\n // Skip if we're waiting for session — startSession() will call flush().\n if (\n !awaitingSession &&\n this.entries.some((e) => e.status === 'queued') &&\n this.config.networkMonitor.isConnected()\n ) {\n queueMicrotask(() => this.flush());\n }\n }\n }\n\n private persist(): void {\n if (!this.config.storage) return;\n\n const data: PersistedEntry[] = this.entries.map((e) => ({\n optimisticId: e.optimisticId,\n retryCount: e.retryCount,\n }));\n\n const onErr = (err: unknown) => {\n this.config.onError?.(\n err instanceof Error ? err : new Error('Queue storage write failed'),\n );\n };\n\n if (data.length === 0) {\n this.config.storage.removeItem(this.storageKey).catch(onErr);\n } else {\n this.config.storage\n .setItem(this.storageKey, JSON.stringify(data))\n .catch(onErr);\n }\n }\n}\n","import { useEffect, useRef, useState, useCallback } from 'react';\nimport { AppState, Platform, type AppStateStatus } from 'react-native';\nimport type { UnreadCountResponse, SSEUnreadUpdateEvent } from '@pedi/chika-types';\nimport type { ChatConfig } from './types';\nimport { resolveServerUrl } from './resolve-url';\nimport { createSSEConnection, type SSEConnection } from './sse-connection';\nimport { createNetworkMonitor, type NetworkMonitor } from './network-monitor';\n\nconst DEFAULT_BACKGROUND_GRACE_MS = 2000;\nconst UNREAD_CUSTOM_EVENTS = ['unread_snapshot', 'unread_update', 'unread_clear'];\n\nexport interface UseUnreadOptions {\n config: ChatConfig;\n channelId: string;\n participantId: string;\n enabled?: boolean;\n}\n\nexport interface UseUnreadReturn {\n unreadCount: number;\n hasUnread: boolean;\n lastMessageAt: string | null;\n error: Error | null;\n}\n\nexport function useUnread(options: UseUnreadOptions): UseUnreadReturn {\n const { config, channelId, participantId, enabled = true } = options;\n\n const [unreadCount, setUnreadCount] = useState(0);\n const [lastMessageAt, setLastMessageAt] = useState<string | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n const connRef = useRef<SSEConnection | null>(null);\n const configRef = useRef(config);\n configRef.current = config;\n const appStateRef = useRef<AppStateStatus>(AppState.currentState);\n const backgroundTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const backgroundGraceMs =\n config.backgroundGraceMs ?? (Platform.OS === 'android' ? DEFAULT_BACKGROUND_GRACE_MS : 0);\n\n const [monitor, setMonitor] = useState<NetworkMonitor | null>(null);\n const resilienceEnabled = config.resilience !== false;\n const injectedMonitor =\n typeof config.resilience === 'object' ? config.resilience.networkMonitor : undefined;\n\n useEffect(() => {\n if (!resilienceEnabled) {\n setMonitor(null);\n return;\n }\n if (injectedMonitor) {\n setMonitor(injectedMonitor);\n return;\n }\n const m = createNetworkMonitor();\n setMonitor(m);\n return () => {\n m.dispose();\n setMonitor(null);\n };\n }, [resilienceEnabled, injectedMonitor]);\n\n const connect = useCallback(() => {\n connRef.current?.close();\n connRef.current = null;\n\n const serviceUrl = resolveServerUrl(configRef.current.manifest, channelId);\n const customHeaders = configRef.current.headers ?? {};\n const url = `${serviceUrl}/channels/${channelId}/unread?participant_id=${encodeURIComponent(participantId)}`;\n\n connRef.current = createSSEConnection(\n {\n url,\n headers: customHeaders,\n reconnectDelayMs: configRef.current.reconnectDelayMs,\n customEvents: UNREAD_CUSTOM_EVENTS,\n networkMonitor: monitor ?? undefined,\n },\n {\n onOpen: () => {\n setError(null);\n },\n onEvent: (eventType, data) => {\n try {\n if (eventType === 'unread_snapshot') {\n const snapshot: UnreadCountResponse = JSON.parse(data);\n setUnreadCount(snapshot.unread_count);\n setLastMessageAt(snapshot.last_message_at);\n } else if (eventType === 'unread_update') {\n const update: SSEUnreadUpdateEvent['data'] = JSON.parse(data);\n setUnreadCount((prev) => prev + 1);\n setLastMessageAt(update.created_at);\n } else if (eventType === 'unread_clear') {\n const clear: { channel_id: string; unread_count: number } = JSON.parse(data);\n setUnreadCount(clear.unread_count);\n }\n } catch {\n setError(new Error('Failed to parse unread SSE event'));\n }\n },\n onError: (err) => {\n setError(err);\n },\n onClosed: () => {\n connRef.current = null;\n },\n },\n );\n }, [channelId, participantId, monitor]);\n\n const disconnect = useCallback(() => {\n connRef.current?.close();\n connRef.current = null;\n }, []);\n\n useEffect(() => {\n setUnreadCount(0);\n setLastMessageAt(null);\n setError(null);\n\n if (!enabled) {\n disconnect();\n return;\n }\n\n // Wait for monitor before connecting (avoids double-connect on mount)\n if (resilienceEnabled && !monitor) return;\n\n connect();\n\n return () => {\n disconnect();\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n }\n };\n }, [channelId, participantId, enabled, connect, disconnect]);\n\n useEffect(() => {\n if (!enabled) return;\n\n const subscription = AppState.addEventListener('change', (nextAppState) => {\n const prev = appStateRef.current;\n appStateRef.current = nextAppState;\n\n if (!connRef.current && nextAppState !== 'active') return;\n\n const shouldTeardown =\n nextAppState === 'background' ||\n (Platform.OS === 'ios' && nextAppState === 'inactive');\n\n if (nextAppState === 'active') {\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n return;\n }\n\n if (prev.match(/inactive|background/) && !connRef.current) {\n connect();\n }\n } else if (shouldTeardown) {\n if (backgroundTimerRef.current) return;\n\n if (backgroundGraceMs === 0) {\n disconnect();\n } else {\n backgroundTimerRef.current = setTimeout(() => {\n backgroundTimerRef.current = null;\n disconnect();\n }, backgroundGraceMs);\n }\n }\n });\n\n return () => {\n subscription.remove();\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n }\n };\n }, [enabled, backgroundGraceMs, connect, disconnect]);\n\n return { unreadCount, hasUnread: unreadCount > 0, lastMessageAt, error };\n}\n"],"mappings":"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","names":["EventSource","DEFAULT_RECONNECT_DELAY_MS","import_react","import_react_native","DEFAULT_BACKGROUND_GRACE_MS"]}
package/dist/index.mjs CHANGED
@@ -645,7 +645,7 @@ var MessageQueue = class {
645
645
  const entry = {
646
646
  optimisticId,
647
647
  sendFn,
648
- status: this.config.networkMonitor.isConnected() ? "sending" : "queued",
648
+ status: "queued",
649
649
  retryCount: 0,
650
650
  abort,
651
651
  resolve,
@@ -685,10 +685,16 @@ var MessageQueue = class {
685
685
  }
686
686
  this.entries = [];
687
687
  }
688
+ /**
689
+ * Trigger a flush of queued messages. Returns immediately if a flush is
690
+ * already in progress or the network is offline. The in-progress flush
691
+ * will pick up any newly queued entries via its while loop.
692
+ */
688
693
  async flush() {
689
694
  if (this.flushing) return;
690
695
  if (!this.config.networkMonitor.isConnected()) return;
691
696
  this.flushing = true;
697
+ let awaitingSession = false;
692
698
  try {
693
699
  while (this.entries.length > 0) {
694
700
  const entry = this.entries.find((e) => e.status === "queued");
@@ -709,6 +715,12 @@ var MessageQueue = class {
709
715
  this.persist();
710
716
  } catch (err) {
711
717
  if (entry.abort.signal.aborted) continue;
718
+ if (err instanceof ChatDisconnectedError) {
719
+ entry.status = "queued";
720
+ this.config.onStatusChange?.();
721
+ awaitingSession = true;
722
+ break;
723
+ }
712
724
  entry.status = "failed";
713
725
  entry.error = err instanceof Error ? err : new Error(String(err));
714
726
  entry.retryCount++;
@@ -718,6 +730,9 @@ var MessageQueue = class {
718
730
  }
719
731
  } finally {
720
732
  this.flushing = false;
733
+ if (!awaitingSession && this.entries.some((e) => e.status === "queued") && this.config.networkMonitor.isConnected()) {
734
+ queueMicrotask(() => this.flush());
735
+ }
721
736
  }
722
737
  }
723
738
  persist() {
@@ -794,6 +809,7 @@ function useChat({ config, channelId, profile, onMessage }) {
794
809
  const callbacks = {
795
810
  onMessage: (message) => {
796
811
  if (disposedRef.current) return;
812
+ let matchedOptimisticId = null;
797
813
  setMessages((prev) => {
798
814
  if (pendingOptimisticIds.current.size === 0) {
799
815
  return [...prev, message];
@@ -802,14 +818,20 @@ function useChat({ config, channelId, profile, onMessage }) {
802
818
  (m) => pendingOptimisticIds.current.has(m.id) && m.sender_id === message.sender_id && m.body === message.body && m.type === message.type
803
819
  );
804
820
  if (optimisticIdx !== -1) {
805
- const optimisticId = prev[optimisticIdx].id;
806
- pendingOptimisticIds.current.delete(optimisticId);
821
+ matchedOptimisticId = prev[optimisticIdx].id;
822
+ pendingOptimisticIds.current.delete(matchedOptimisticId);
807
823
  const next = [...prev];
808
824
  next[optimisticIdx] = message;
809
825
  return next;
810
826
  }
811
827
  return [...prev, message];
812
828
  });
829
+ if (matchedOptimisticId) {
830
+ const s = queueRef.current?.getStatus(matchedOptimisticId);
831
+ if (s && s.status !== "sending") {
832
+ queueRef.current?.cancel(matchedOptimisticId);
833
+ }
834
+ }
813
835
  onMessageRef.current?.(message);
814
836
  },
815
837
  onStatusChange: (nextStatus) => {
@@ -850,6 +872,7 @@ function useChat({ config, channelId, profile, onMessage }) {
850
872
  }
851
873
  sessionRef.current = session;
852
874
  setParticipants(session.initialParticipants);
875
+ queueRef.current?.flush();
853
876
  if (pendingOptimisticIds.current.size > 0) {
854
877
  const pendingIds = pendingOptimisticIds.current;
855
878
  setMessages((prev) => {
@@ -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 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 const optimisticId = prev[optimisticIdx]!.id;\n pendingOptimisticIds.current.delete(optimisticId);\n const next = [...prev];\n next[optimisticIdx] = message;\n return next;\n }\n return [...prev, message];\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 // 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 } 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: this.config.networkMonitor.isConnected() ? 'sending' : '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 private async flush(): Promise<void> {\n if (this.flushing) return;\n if (!this.config.networkMonitor.isConnected()) return;\n\n this.flushing = true;\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 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 }\n }\n\n private persist(): void {\n if (!this.config.storage) return;\n\n const data: PersistedEntry[] = this.entries.map((e) => ({\n optimisticId: e.optimisticId,\n retryCount: e.retryCount,\n }));\n\n const onErr = (err: unknown) => {\n this.config.onError?.(\n err instanceof Error ? err : new Error('Queue storage write failed'),\n );\n };\n\n if (data.length === 0) {\n this.config.storage.removeItem(this.storageKey).catch(onErr);\n } else {\n this.config.storage\n .setItem(this.storageKey, JSON.stringify(data))\n .catch(onErr);\n }\n }\n}\n","import { useEffect, useRef, useState, useCallback } from 'react';\nimport { AppState, Platform, type AppStateStatus } from 'react-native';\nimport type { UnreadCountResponse, SSEUnreadUpdateEvent } from '@pedi/chika-types';\nimport type { ChatConfig } from './types';\nimport { resolveServerUrl } from './resolve-url';\nimport { createSSEConnection, type SSEConnection } from './sse-connection';\nimport { createNetworkMonitor, type NetworkMonitor } from './network-monitor';\n\nconst DEFAULT_BACKGROUND_GRACE_MS = 2000;\nconst UNREAD_CUSTOM_EVENTS = ['unread_snapshot', 'unread_update', 'unread_clear'];\n\nexport interface UseUnreadOptions {\n config: ChatConfig;\n channelId: string;\n participantId: string;\n enabled?: boolean;\n}\n\nexport interface UseUnreadReturn {\n unreadCount: number;\n hasUnread: boolean;\n lastMessageAt: string | null;\n error: Error | null;\n}\n\nexport function useUnread(options: UseUnreadOptions): UseUnreadReturn {\n const { config, channelId, participantId, enabled = true } = options;\n\n const [unreadCount, setUnreadCount] = useState(0);\n const [lastMessageAt, setLastMessageAt] = useState<string | null>(null);\n const [error, setError] = useState<Error | null>(null);\n\n const connRef = useRef<SSEConnection | null>(null);\n const configRef = useRef(config);\n configRef.current = config;\n const appStateRef = useRef<AppStateStatus>(AppState.currentState);\n const backgroundTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const backgroundGraceMs =\n config.backgroundGraceMs ?? (Platform.OS === 'android' ? DEFAULT_BACKGROUND_GRACE_MS : 0);\n\n const [monitor, setMonitor] = useState<NetworkMonitor | null>(null);\n const resilienceEnabled = config.resilience !== false;\n const injectedMonitor =\n typeof config.resilience === 'object' ? config.resilience.networkMonitor : undefined;\n\n useEffect(() => {\n if (!resilienceEnabled) {\n setMonitor(null);\n return;\n }\n if (injectedMonitor) {\n setMonitor(injectedMonitor);\n return;\n }\n const m = createNetworkMonitor();\n setMonitor(m);\n return () => {\n m.dispose();\n setMonitor(null);\n };\n }, [resilienceEnabled, injectedMonitor]);\n\n const connect = useCallback(() => {\n connRef.current?.close();\n connRef.current = null;\n\n const serviceUrl = resolveServerUrl(configRef.current.manifest, channelId);\n const customHeaders = configRef.current.headers ?? {};\n const url = `${serviceUrl}/channels/${channelId}/unread?participant_id=${encodeURIComponent(participantId)}`;\n\n connRef.current = createSSEConnection(\n {\n url,\n headers: customHeaders,\n reconnectDelayMs: configRef.current.reconnectDelayMs,\n customEvents: UNREAD_CUSTOM_EVENTS,\n networkMonitor: monitor ?? undefined,\n },\n {\n onOpen: () => {\n setError(null);\n },\n onEvent: (eventType, data) => {\n try {\n if (eventType === 'unread_snapshot') {\n const snapshot: UnreadCountResponse = JSON.parse(data);\n setUnreadCount(snapshot.unread_count);\n setLastMessageAt(snapshot.last_message_at);\n } else if (eventType === 'unread_update') {\n const update: SSEUnreadUpdateEvent['data'] = JSON.parse(data);\n setUnreadCount((prev) => prev + 1);\n setLastMessageAt(update.created_at);\n } else if (eventType === 'unread_clear') {\n const clear: { channel_id: string; unread_count: number } = JSON.parse(data);\n setUnreadCount(clear.unread_count);\n }\n } catch {\n setError(new Error('Failed to parse unread SSE event'));\n }\n },\n onError: (err) => {\n setError(err);\n },\n onClosed: () => {\n connRef.current = null;\n },\n },\n );\n }, [channelId, participantId, monitor]);\n\n const disconnect = useCallback(() => {\n connRef.current?.close();\n connRef.current = null;\n }, []);\n\n useEffect(() => {\n setUnreadCount(0);\n setLastMessageAt(null);\n setError(null);\n\n if (!enabled) {\n disconnect();\n return;\n }\n\n // Wait for monitor before connecting (avoids double-connect on mount)\n if (resilienceEnabled && !monitor) return;\n\n connect();\n\n return () => {\n disconnect();\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n }\n };\n }, [channelId, participantId, enabled, connect, disconnect]);\n\n useEffect(() => {\n if (!enabled) return;\n\n const subscription = AppState.addEventListener('change', (nextAppState) => {\n const prev = appStateRef.current;\n appStateRef.current = nextAppState;\n\n if (!connRef.current && nextAppState !== 'active') return;\n\n const shouldTeardown =\n nextAppState === 'background' ||\n (Platform.OS === 'ios' && nextAppState === 'inactive');\n\n if (nextAppState === 'active') {\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n return;\n }\n\n if (prev.match(/inactive|background/) && !connRef.current) {\n connect();\n }\n } else if (shouldTeardown) {\n if (backgroundTimerRef.current) return;\n\n if (backgroundGraceMs === 0) {\n disconnect();\n } else {\n backgroundTimerRef.current = setTimeout(() => {\n backgroundTimerRef.current = null;\n disconnect();\n }, backgroundGraceMs);\n }\n }\n });\n\n return () => {\n subscription.remove();\n if (backgroundTimerRef.current) {\n clearTimeout(backgroundTimerRef.current);\n backgroundTimerRef.current = null;\n }\n };\n }, [enabled, backgroundGraceMs, connect, disconnect]);\n\n return { unreadCount, hasUnread: unreadCount > 0, lastMessageAt, error };\n}\n"],"mappings":"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","names":["DEFAULT_RECONNECT_DELAY_MS","useEffect","useRef","useState","useCallback","AppState","Platform","DEFAULT_BACKGROUND_GRACE_MS","useState","useRef","AppState","Platform","useEffect","useCallback"]}
1
+ {"version":3,"sources":["../src/use-chat.ts","../src/errors.ts","../src/retry.ts","../src/resolve-url.ts","../src/sse-connection.ts","../src/session.ts","../src/network-monitor.ts","../src/message-queue.ts","../src/use-unread.ts"],"sourcesContent":["import { useEffect, useRef, useState, useCallback } from 'react';\nimport { AppState, Platform, type AppStateStatus } from 'react-native';\nimport type {\n ChatDomain,\n DefaultDomain,\n Message,\n Participant,\n MessageAttributes,\n SendMessageResponse,\n} from '@pedi/chika-types';\nimport type { UseChatOptions, UseChatReturn, ChatStatus } from './types';\nimport { ChatDisconnectedError, ChannelClosedError, QueueFullError, RetryExhaustedError } from './errors';\nimport { isRetryableError, resolveRetryConfig } from './retry';\nimport { createChatSession, type ChatSession, type SessionCallbacks } from './session';\nimport { createNetworkMonitor, type NetworkMonitor } from './network-monitor';\nimport { MessageQueue, type QueuedMessage } from './message-queue';\n\nconst DEFAULT_BACKGROUND_GRACE_MS = 2000;\nconst DEFAULT_MAX_QUEUE_SIZE = 50;\n\n// Module-scope queue registry, keyed by channelId. Survives component remounts.\nconst queueRegistry = new Map<string, { queue: MessageQueue; refCount: number }>();\n\n/**\n * React hook for real-time chat over SSE.\n * Manages connection lifecycle, AppState transitions, message deduplication, reconnection,\n * and optional network resilience (retry, offline queue, network monitoring).\n *\n * @template D - Chat domain type for role/message type narrowing. Defaults to DefaultDomain.\n */\nexport function useChat<D extends ChatDomain = DefaultDomain>(\n { config, channelId, profile, onMessage }: UseChatOptions<D>,\n): UseChatReturn<D> {\n const [messages, setMessages] = useState<Message<D>[]>([]);\n const [participants, setParticipants] = useState<Participant<D>[]>([]);\n const [status, setStatus] = useState<ChatStatus>('connecting');\n const [error, setError] = useState<Error | null>(null);\n const [pendingMessages, setPendingMessages] = useState<QueuedMessage[]>([]);\n\n const sessionRef = useRef<ChatSession<D> | null>(null);\n const disposedRef = useRef(false);\n const messagesRef = useRef(messages);\n messagesRef.current = messages;\n const statusRef = useRef(status);\n statusRef.current = status;\n const appStateRef = useRef<AppStateStatus>(AppState.currentState);\n const backgroundTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const profileRef = useRef(profile);\n profileRef.current = profile;\n const configRef = useRef(config);\n configRef.current = config;\n const onMessageRef = useRef(onMessage);\n onMessageRef.current = onMessage;\n const startingRef = useRef(false);\n const pendingOptimisticIds = useRef(new Set<string>());\n const [monitor, setMonitor] = useState<NetworkMonitor | null>(null);\n const 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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pedi/chika-sdk",
3
- "version": "1.0.6",
3
+ "version": "1.0.7",
4
4
  "description": "React Native SDK for Pedi Chika chat service",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -33,7 +33,7 @@
33
33
  "typecheck": "tsc --noEmit"
34
34
  },
35
35
  "dependencies": {
36
- "@pedi/chika-types": "^1.0.6",
36
+ "@pedi/chika-types": "^1.0.7",
37
37
  "react-native-sse": "^1.2.1"
38
38
  },
39
39
  "peerDependencies": {