@pedi/chika-sdk 1.0.4 → 1.0.6

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/use-chat.ts","../src/errors.ts","../src/resolve-url.ts","../src/sse-connection.ts","../src/session.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 } from './errors';\nimport { createChatSession, type ChatSession, type SessionCallbacks } from './session';\n\nconst DEFAULT_BACKGROUND_GRACE_MS = 2000;\n\n/**\n * React hook for real-time chat over SSE.\n * Manages connection lifecycle, AppState transitions, message deduplication, and reconnection.\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\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\n const backgroundGraceMs =\n config.backgroundGraceMs ?? (Platform.OS === 'android' ? DEFAULT_BACKGROUND_GRACE_MS : 0);\n\n const callbacks: SessionCallbacks<D> = {\n onMessage: (message) => {\n if (disposedRef.current) return;\n setMessages((prev: Message<D>[]) => {\n // Check if this SSE message reconciles a pending optimistic message.\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') setError(null);\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>(configRef.current, channelId, profileRef.current, callbacks);\n\n if (disposedRef.current) {\n session.disconnect();\n return;\n }\n\n sessionRef.current = session;\n setParticipants(session.initialParticipants);\n setMessages(session.initialMessages);\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 useEffect(() => {\n disposedRef.current = false;\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]);\n\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 let optimisticId: string | null = null;\n\n if (optimistic) {\n optimisticId = `optimistic_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`;\n pendingOptimisticIds.current.add(optimisticId);\n const provisionalMsg: Message<D> = {\n id: optimisticId,\n channel_id: channelId,\n sender_id: profileRef.current.id,\n sender_role: profileRef.current.role as D['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 try {\n const response = await session.sendMessage(type, body, attributes);\n\n if (optimistic && optimisticId) {\n pendingOptimisticIds.current.delete(optimisticId);\n setMessages((prev) => {\n // If SSE already reconciled this message, the optimistic ID is gone.\n const stillPending = prev.some((m) => m.id === optimisticId);\n if (!stillPending) return prev;\n return prev.map((m) =>\n m.id === optimisticId\n ? { ...m, id: response.id, created_at: response.created_at }\n : m,\n );\n });\n }\n\n return response;\n } catch (err) {\n if (optimistic && optimisticId) {\n pendingOptimisticIds.current.delete(optimisticId);\n setMessages((prev) => prev.filter((m) => m.id !== optimisticId));\n }\n throw err;\n }\n },\n [channelId],\n );\n\n const disconnect = useCallback(() => {\n sessionRef.current?.disconnect();\n sessionRef.current = null;\n setStatus('disconnected');\n }, []);\n\n return { messages, participants, status, error, sendMessage, disconnect };\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","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';\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}\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}\n\nexport function createSSEConnection(\n config: SSEConnectionConfig,\n callbacks: SSEConnectionCallbacks,\n): SSEConnection {\n const reconnectDelay = config.reconnectDelayMs ?? DEFAULT_RECONNECT_DELAY_MS;\n const customEvents = config.customEvents ?? [];\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\n function cleanup(): void {\n if (es) {\n es.removeAllEventListeners();\n es.close();\n es = null;\n }\n }\n\n function scheduleReconnect(): void {\n if (disposed || reconnectTimer) return;\n callbacks.onReconnecting?.();\n cleanup();\n\n reconnectTimer = setTimeout(() => {\n reconnectTimer = null;\n connect();\n }, reconnectDelay);\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 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 if (reconnectTimer) {\n clearTimeout(reconnectTimer);\n reconnectTimer = null;\n }\n cleanup();\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 } from './errors';\nimport { resolveServerUrl } from './resolve-url';\nimport { createSSEConnection, type SSEConnection } from './sse-connection';\n\nconst DEFAULT_RECONNECT_DELAY_MS = 3000;\nconst MAX_SEEN_IDS = 500;\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 sendMessage: (type: D['messageType'], body: string, attributes?: MessageAttributes<D>) => Promise<SendMessageResponse>;\n markAsRead: (messageId: string) => Promise<void>;\n disconnect: () => void;\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): 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\n callbacks.onStatusChange('connecting');\n\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 });\n\n if (joinRes.status === 410) {\n throw new ChannelClosedError(channelId);\n }\n\n if (!joinRes.ok) {\n throw new Error(`Join failed: ${joinRes.status} ${await joinRes.text()}`);\n }\n\n const { messages, participants, joined_at }: JoinResponse<D> = await joinRes.json();\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 function trimSeenIds(): void {\n if (seenMessageIds.size <= MAX_SEEN_IDS) 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 },\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 sseConn?.close();\n sseConn = null;\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\n sendMessage: async (type, body, attributes) => {\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 };\n\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 },\n );\n\n if (!res.ok) {\n throw new Error(`Send failed: ${res.status} ${await res.text()}`);\n }\n\n const response: SendMessageResponse = await res.json();\n seenMessageIds.add(response.id);\n return response;\n },\n\n markAsRead: async (messageId: string) => {\n const res = await fetch(`${serviceUrl}/channels/${channelId}/read`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', ...customHeaders },\n body: JSON.stringify({\n participant_id: profile.id,\n message_id: messageId,\n }),\n });\n if (!res.ok) {\n throw new Error(`markAsRead failed: ${res.status}`);\n }\n },\n\n disconnect: () => {\n disposed = true;\n sseConn?.close();\n sseConn = null;\n callbacks.onStatusChange('disconnected');\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';\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 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 },\n {\n onOpen: () => {\n setError(null);\n },\n onEvent: (eventType, data) => {\n try {\n if (eventType === 'unread_snapshot') {\n const snapshot: UnreadCountResponse = JSON.parse(data);\n setUnreadCount(snapshot.unread_count);\n setLastMessageAt(snapshot.last_message_at);\n } else if (eventType === 'unread_update') {\n const update: SSEUnreadUpdateEvent['data'] = JSON.parse(data);\n setUnreadCount((prev) => prev + 1);\n setLastMessageAt(update.created_at);\n } else if (eventType === 'unread_clear') {\n const clear: { channel_id: string; unread_count: number } = JSON.parse(data);\n setUnreadCount(clear.unread_count);\n }\n } catch {\n setError(new Error('Failed to parse unread SSE event'));\n }\n },\n onError: (err) => {\n setError(err);\n },\n onClosed: () => {\n connRef.current = null;\n },\n },\n );\n }, [channelId, participantId]);\n\n const disconnect = useCallback(() => {\n connRef.current?.close();\n connRef.current = null;\n }, []);\n\n useEffect(() => {\n setUnreadCount(0);\n setLastMessageAt(null);\n setError(null);\n\n if (!enabled) {\n disconnect();\n return;\n }\n\n 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","useCallback","useEffect"]}
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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pedi/chika-sdk",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "description": "React Native SDK for Pedi Chika chat service",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -33,12 +33,26 @@
33
33
  "typecheck": "tsc --noEmit"
34
34
  },
35
35
  "dependencies": {
36
- "@pedi/chika-types": "^1.0.4",
36
+ "@pedi/chika-types": "^1.0.6",
37
37
  "react-native-sse": "^1.2.1"
38
38
  },
39
39
  "peerDependencies": {
40
40
  "react": ">=18",
41
- "react-native": ">=0.72"
41
+ "react-native": ">=0.72",
42
+ "@react-native-community/netinfo": ">=9.0.0",
43
+ "@react-native-async-storage/async-storage": ">=1.0.0",
44
+ "react-native-mmkv": ">=2.0.0"
45
+ },
46
+ "peerDependenciesMeta": {
47
+ "@react-native-community/netinfo": {
48
+ "optional": true
49
+ },
50
+ "@react-native-async-storage/async-storage": {
51
+ "optional": true
52
+ },
53
+ "react-native-mmkv": {
54
+ "optional": true
55
+ }
42
56
  },
43
57
  "devDependencies": {
44
58
  "@types/react": "^19.2.14",