@antzsoft/chat-core 1.1.2 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -5
- package/dist/index.cjs +88 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -3
- package/dist/index.d.ts +37 -3
- package/dist/index.js +88 -25
- package/dist/index.js.map +1 -1
- package/docs/integration-guide.html +191 -13
- package/package.json +1 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/stores/chat.store.ts","../src/index.ts","../src/config/types.ts","../src/compression/compress.ts","../src/api/client.ts","../src/crypto/transit.ts","../src/crypto/session.ts","../src/api/auth.ts","../src/api/app-config.ts","../src/api/messages.ts","../src/api/conversations.ts","../src/api/storage.ts","../src/api/devices.ts","../src/api/users.ts","../src/socket/socket.ts","../src/crypto/detect.ts","../src/crypto/handshake.ts","../src/socket/emitters.ts","../src/stores/auth.store.ts","../src/client-facade.ts"],"sourcesContent":["import { create } from 'zustand';\nimport type { Message } from '../types/index.js';\n\ninterface TypingUser {\n userId: string;\n displayName: string;\n avatarUrl?: string;\n}\n\nexport interface LastReadEntry {\n messageId: string;\n readAt: string;\n}\n\ninterface ChatState {\n activeConversationId: string | null;\n pendingTarget: { conversationId: string; messageId: string } | null;\n typingUsers: Record<string, TypingUser[]>;\n onlineUsers: string[];\n /** keyed by conversationId — current user's last read pointer per conversation */\n lastRead: Record<string, LastReadEntry>;\n /** keyed by userId — last seen timestamp for each user */\n lastSeen: Record<string, string>;\n replyingTo: Message | null;\n editingMessage: Message | null;\n isSidebarOpen: boolean;\n isGroupInfoOpen: boolean;\n isStarredPanelOpen: boolean;\n\n setActiveConversation: (id: string | null) => void;\n setPendingTarget: (target: { conversationId: string; messageId: string } | null) => void;\n addTypingUser: (conversationId: string, user: TypingUser) => void;\n removeTypingUser: (conversationId: string, userId: string) => void;\n setUserOnline: (userId: string) => void;\n setUserOffline: (userId: string) => void;\n setOnlineUsers: (userIds: string[]) => void;\n setLastRead: (conversationId: string, messageId: string, readAt: string) => void;\n setLastSeen: (userId: string, lastSeenAt: string) => void;\n setReplyingTo: (message: Message | null) => void;\n setEditingMessage: (message: Message | null) => void;\n toggleSidebar: () => void;\n setSidebarOpen: (open: boolean) => void;\n toggleGroupInfo: () => void;\n setGroupInfoOpen: (open: boolean) => void;\n toggleStarredPanel: () => void;\n setStarredPanelOpen: (open: boolean) => void;\n}\n\nexport const useChatStore = create<ChatState>((set) => ({\n activeConversationId: null,\n pendingTarget: null,\n typingUsers: {},\n onlineUsers: [],\n lastRead: {},\n lastSeen: {},\n replyingTo: null,\n editingMessage: null,\n isSidebarOpen: true,\n isGroupInfoOpen: false,\n isStarredPanelOpen: false,\n\n setActiveConversation: (id) =>\n set({ activeConversationId: id, replyingTo: null, editingMessage: null }),\n\n setPendingTarget: (target) => set({ pendingTarget: target }),\n\n addTypingUser: (conversationId, user) =>\n set((state) => {\n const existing = state.typingUsers[conversationId] ?? [];\n const deduped = existing.filter((u) => u.userId !== user.userId);\n return { typingUsers: { ...state.typingUsers, [conversationId]: [...deduped, user] } };\n }),\n\n removeTypingUser: (conversationId, userId) =>\n set((state) => ({\n typingUsers: {\n ...state.typingUsers,\n [conversationId]: (state.typingUsers[conversationId] ?? []).filter(\n (u) => u.userId !== userId,\n ),\n },\n })),\n\n setUserOnline: (userId) =>\n set((state) => ({\n onlineUsers: state.onlineUsers.includes(userId)\n ? state.onlineUsers\n : [...state.onlineUsers, userId],\n })),\n\n setUserOffline: (userId) =>\n set((state) => ({ onlineUsers: state.onlineUsers.filter((id) => id !== userId) })),\n\n setOnlineUsers: (userIds) => set({ onlineUsers: userIds }),\n\n setLastRead: (conversationId, messageId, readAt) =>\n set((state) => ({\n lastRead: { ...state.lastRead, [conversationId]: { messageId, readAt } },\n })),\n\n setLastSeen: (userId, lastSeenAt) =>\n set((state) => ({\n lastSeen: { ...state.lastSeen, [userId]: lastSeenAt },\n })),\n\n setReplyingTo: (message) => set({ replyingTo: message, editingMessage: null }),\n\n setEditingMessage: (message) => set({ editingMessage: message, replyingTo: null }),\n\n toggleSidebar: () => set((state) => ({ isSidebarOpen: !state.isSidebarOpen })),\n setSidebarOpen: (open) => set({ isSidebarOpen: open }),\n\n toggleGroupInfo: () => set((state) => ({ isGroupInfoOpen: !state.isGroupInfoOpen })),\n setGroupInfoOpen: (open) => set({ isGroupInfoOpen: open }),\n\n toggleStarredPanel: () => set((state) => ({ isStarredPanelOpen: !state.isStarredPanelOpen })),\n setStarredPanelOpen: (open) => set({ isStarredPanelOpen: open }),\n}));\n","// Config\nexport type {\n AntzChatConfig,\n ResolvedConfig,\n UploadConfig,\n FileSizeLimits,\n ResolvedFileSizeLimits,\n PlatformUploadFn,\n PlatformCompressFn,\n CompressionConfig,\n ResolvedCompressionConfig,\n PersistStorage,\n} from './config/types.js';\nexport { resolveConfig } from './config/types.js';\n\n// Compression\nexport type { CompressionAlgorithm, CompressedFile } from './types/index.js';\nexport { getCompressionStrategy } from './compression/compress.js';\n\n// Types\nexport type {\n UploadableFile,\n User,\n AuthTokens,\n AuthResponse,\n LoginCredentials,\n RegisterData,\n Message,\n MessageContent,\n MessageReplyReference,\n ReplyAttachmentSnapshot,\n MessageReaction,\n Conversation,\n ConversationListParams,\n ConversationUnreadCount,\n UnreadSummary,\n Participant,\n Attachment,\n FileType,\n FileResponse,\n PresignedUrlRequest,\n PresignedUrlResponse,\n BatchUploadResult,\n UploadProgress,\n PaginatedResponse,\n CursorPaginatedResponse,\n SendMessagePayload,\n SendMessageAttachment,\n OptimisticAttachment,\n NewMessageEvent,\n MessageUpdatedEvent,\n MessageDeletedEvent,\n MessageDeletedForMeEvent,\n ReactionUpdatedEvent,\n TypingIndicatorEvent,\n UserStatusEvent,\n ReadReceiptEvent,\n MessageAckEvent,\n MessageDeliveredEvent,\n MessagesDeliveredEvent,\n} from './types/index.js';\n\n// API\nexport { authApi, messagesApi, conversationsApi, storageApi, uploadBatch, normalizeConversation, devicesApi, usersApi, appConfigApi } from './api/index.js';\nexport type { RegisterDeviceTokenPayload, MobileDeviceToken, WebPushDeviceToken } from './api/index.js';\nexport type { CreateGroupData, CreateDirectData, UpdateConversationData, ListMessagesParams, SearchParams, SendData } from './api/index.js';\nexport type { UserPreferences, QuietHours } from './types/index.js';\nexport type { AppConfig } from './types/index.js';\nexport { initApiClient, setApiClientInstance, getApiClient } from './api/client.js';\nexport type { TokenStore } from './api/client.js';\n\n// Socket\nexport { connectSocket, disconnectSocket, reconnectSocket, refreshSocketAuth, getSocket, tryGetSocket, getSocketStatus, onSocketStatus } from './socket/socket.js';\nexport type { SocketStatus, StatusListener } from './socket/socket.js';\nexport { socketEmit } from './socket/emitters.js';\n\n// Transit crypto — internal session setter exposed for platform adapters (RN noble)\nexport { setTransitSession } from './crypto/session.js';\n\n// Stores\nexport { initAuthStore, getAuthStore, createAuthStore, resetAuthStore } from './stores/auth.store.js';\nexport { useChatStore } from './stores/chat.store.js';\nexport type { LastReadEntry } from './stores/chat.store.js';\n\n// Headless client\nexport { AntzChatClient } from './client-facade.js';\n","import type { FileType, UploadableFile, PresignedUrlResponse, CompressedFile } from '../types/index.js';\n\nexport interface FileSizeLimits {\n image?: number;\n video?: number;\n audio?: number;\n document?: number;\n default?: number;\n}\n\n/**\n * Platform-provided function that performs the actual binary upload to a presigned URL.\n * - Web implementation: XMLHttpRequest with progress events\n * - RN implementation: fetch with FormData or direct body\n *\n * The core calls this — it never does the upload itself.\n */\nexport type PlatformUploadFn = (\n presigned: PresignedUrlResponse,\n file: UploadableFile,\n onProgress?: (pct: number) => void,\n) => Promise<void>;\n\n/**\n * Platform-provided persistent key-value storage for auth token persistence.\n * - Web: localStorage adapter\n * - RN: AsyncStorage adapter\n */\nexport interface PersistStorage {\n getItem(key: string): string | null | Promise<string | null>;\n setItem(key: string, value: string): void | Promise<void>;\n removeItem(key: string): void | Promise<void>;\n}\n\n/**\n * Platform-provided compression function. Optional — if omitted, files are uploaded as-is.\n * - Web: uses canvas (images) + CompressionStream (text/docs) — both browser-native\n * - RN: uses expo-image-manipulator (images) + pako (text/docs)\n * - Node: uses sharp (images) + zlib (text/docs)\n */\nexport type PlatformCompressFn = (\n file: UploadableFile,\n options: ResolvedCompressionConfig,\n) => Promise<CompressedFile>;\n\nexport interface CompressionConfig {\n /** Master switch. Default: true when platformCompressFn is provided, false otherwise */\n enabled?: boolean;\n /** WebP quality for images, 0–1. Default: 0.85 */\n imageQuality?: number;\n /** Longest side cap in px before encoding. Default: 1920 */\n imageMaxDimension?: number;\n /** Gzip text-based documents (plain, csv, json, xml, yaml, svg). Default: true */\n compressDocuments?: boolean;\n}\n\nexport interface ResolvedCompressionConfig {\n enabled: boolean;\n imageQuality: number;\n imageMaxDimension: number;\n compressDocuments: boolean;\n}\n\nexport interface UploadConfig {\n /**\n * Per-type file size limits in MB. Can also pass a single number for all types.\n * Defaults: image 5MB, video 25MB, audio 10MB, document 10MB.\n */\n maxFileSizeMB?: number | FileSizeLimits;\n\n /** Max files per message. Default: 10 */\n maxFilesPerMessage?: number;\n\n /** Which attachment types users can send. Default: all */\n allowedTypes?: Array<FileType>;\n\n /** Called when a file fails validation or upload */\n onUploadError?: (file: UploadableFile, error: Error) => void;\n\n /** Called with 0–100 aggregate progress during a batch upload */\n onProgress?: (progress: number) => void;\n}\n\nexport interface AntzChatConfig {\n /** REST API base URL — e.g. \"https://api.yourapp.com/api/v1\" */\n apiUrl: string;\n\n /**\n * WebSocket server URL. Defaults to apiUrl with /api/vN path stripped.\n * SDK connects to {socketUrl}/chat\n */\n socketUrl?: string;\n\n /** Static JWT. Use this OR authProvider, not both. */\n authToken?: string;\n\n /**\n * Dynamic token getter — called before requests and on socket reconnect.\n * Preferred when the host app manages its own auth lifecycle.\n */\n authProvider?: () => Promise<string>;\n\n /**\n * External user ID — required for non-builtin modes (antz / external / wso2).\n * Sent as x-user-id header on every request so the chat server can forward it\n * to the user-service for token validation.\n * Not needed for builtin mode (chat server issues its own JWTs).\n */\n userId?: string;\n\n /**\n * Profile picture for non-builtin modes.\n * Server fetches/decodes, hashes for dedup, uploads to chat storage on first use.\n * Only sent once at init — not re-sent on every request.\n * Client never needs to compute hashes; the server owns all dedup logic.\n */\n avatar?: {\n /** Full URL the server can fetch (preferred — Antz/external/wso2 clients know their own file URLs) */\n url?: string;\n /** Raw base64 string (with or without data:... prefix) as fallback */\n base64?: string;\n };\n\n /** Required for multi-tenant backends. Sent as X-Tenant-ID header. */\n tenantId?: string;\n\n /**\n * Enable payload-level transit encryption (ECDH key exchange + AES-256-GCM).\n * Encrypts every HTTP request/response body and every socket event payload.\n * Server must have TRANSIT_ENCRYPTION_ENABLED=true to match.\n * Default: true — set false only for local development/debugging.\n * Safe to toggle anytime — no data migration needed (wire-only, never affects storage).\n */\n transitEncryption?: boolean;\n\n upload?: UploadConfig;\n\n /**\n * Optional compression config. Compression is disabled if platformCompressFn is not provided.\n */\n compression?: CompressionConfig;\n\n /**\n * Platform-specific compression implementation. Optional — omit to disable compression.\n * Each SDK (web, RN) provides its own default; Node.js users wire in their own.\n */\n platformCompressFn?: PlatformCompressFn;\n\n /**\n * Number of messages fetched per page when loading chat history.\n * Default: 40\n */\n messagePageSize?: number;\n\n /**\n * Number of starred messages fetched per page.\n * Default: 30\n */\n starredMessagePageSize?: number;\n\n /**\n * Number of search results fetched per request.\n * Default: 50\n */\n searchPageSize?: number;\n\n /**\n * Platform-specific binary upload implementation.\n * Required — each SDK (web, RN) provides its own.\n */\n platformUploadFn: PlatformUploadFn;\n\n /**\n * Platform-specific persistent storage for auth tokens.\n * Required — each SDK provides its own (localStorage / AsyncStorage).\n */\n persistStorage: PersistStorage;\n}\n\n// ─── Resolved (all defaults filled) ─────────────────────────────────────────\n\nexport interface ResolvedFileSizeLimits {\n image: number;\n video: number;\n audio: number;\n document: number;\n default: number;\n}\n\nexport interface ResolvedConfig {\n apiUrl: string;\n socketUrl: string;\n socketOrigin: string;\n socketPath: string;\n authToken?: string;\n authProvider?: () => Promise<string>;\n userId?: string;\n tenantId?: string;\n avatar?: { url?: string; base64?: string };\n transitEncryption: boolean;\n upload: {\n maxFileSizeMB: ResolvedFileSizeLimits;\n maxFilesPerMessage: number;\n allowedTypes: FileType[];\n onUploadError?: (file: UploadableFile, error: Error) => void;\n onProgress?: (progress: number) => void;\n };\n platformUploadFn: PlatformUploadFn;\n platformCompressFn?: PlatformCompressFn;\n compression: ResolvedCompressionConfig;\n persistStorage: PersistStorage;\n messagePageSize: number;\n starredMessagePageSize: number;\n searchPageSize: number;\n}\n\nexport function resolveConfig(config: AntzChatConfig): ResolvedConfig {\n const socketUrl =\n config.socketUrl ??\n config.apiUrl.replace(/\\/api\\/v\\d+\\/?$/, '').replace(/\\/$/, '');\n\n // Auto-derive Socket.IO engine path from the socketUrl pathname.\n // If the server is behind a reverse proxy with a path prefix (e.g. /chat-api),\n // that prefix is already embedded in socketUrl. We append /socket.io to it.\n // Examples:\n // socketUrl = https://host/chat-api → socketPath = /chat-api/socket.io\n // socketUrl = https://host → socketPath = /socket.io\n // No env var or manual config needed — SERVER_URL on the server and apiUrl\n // on the client carry the same prefix, so both sides derive the same path.\n // Derive Socket.IO engine path and origin separately.\n // Socket.IO uses the URL passed to io() as the namespace origin — anything\n // in the pathname of that URL becomes part of the namespace, not the engine path.\n // So we must pass only the bare origin (https://host) to io(), and use the\n // path option for the engine mount point.\n //\n // socketUrl = https://host/chat-api\n // → socketOrigin = https://host (passed to io())\n // → socketPath = /chat-api/socket.io (engine path option)\n // → namespace = /chat (hardcoded in connectSocket)\n //\n // socketUrl = https://host\n // → socketOrigin = https://host\n // → socketPath = /socket.io\n let socketOrigin = socketUrl;\n let socketPath = '/socket.io';\n try {\n const parsed = new URL(socketUrl);\n socketOrigin = parsed.origin; // strips pathname, search, hash\n const pathname = parsed.pathname.replace(/\\/$/, '');\n if (pathname && pathname !== '/') socketPath = `${pathname}/socket.io`;\n } catch {\n // invalid URL — fall back to defaults\n }\n\n const raw = config.upload?.maxFileSizeMB;\n let limits: ResolvedFileSizeLimits;\n\n if (typeof raw === 'number') {\n limits = { image: raw, video: raw, audio: raw, document: raw, default: raw };\n } else {\n const fallback = raw?.default ?? 25;\n limits = {\n image: raw?.image ?? 5,\n video: raw?.video ?? 25,\n audio: raw?.audio ?? 10,\n document: raw?.document ?? 10,\n default: fallback,\n };\n }\n\n return {\n apiUrl: config.apiUrl.replace(/\\/$/, ''),\n socketUrl,\n socketOrigin,\n socketPath,\n authToken: config.authToken,\n authProvider: config.authProvider,\n userId: config.userId,\n tenantId: config.tenantId,\n avatar: config.avatar,\n transitEncryption: config.transitEncryption ?? true,\n upload: {\n maxFileSizeMB: limits,\n maxFilesPerMessage: config.upload?.maxFilesPerMessage ?? 10,\n allowedTypes: config.upload?.allowedTypes ?? ['image', 'video', 'audio', 'document'],\n onUploadError: config.upload?.onUploadError,\n onProgress: config.upload?.onProgress,\n },\n platformUploadFn: config.platformUploadFn,\n platformCompressFn: config.platformCompressFn,\n compression: {\n enabled: config.compression?.enabled ?? (config.platformCompressFn != null),\n imageQuality: config.compression?.imageQuality ?? 0.85,\n imageMaxDimension: config.compression?.imageMaxDimension ?? 1920,\n compressDocuments: config.compression?.compressDocuments ?? true,\n },\n persistStorage: config.persistStorage,\n messagePageSize: config.messagePageSize ?? 40,\n starredMessagePageSize: config.starredMessagePageSize ?? 30,\n searchPageSize: config.searchPageSize ?? 50,\n };\n}\n","import type { UploadableFile, CompressedFile, CompressionAlgorithm } from '../types/index.js';\nimport type { PlatformCompressFn, ResolvedCompressionConfig } from '../config/types.js';\n\n// MIME types that benefit from gzip (text-based, not already compressed)\nconst GZIP_MIME_TYPES = new Set([\n 'text/plain', 'text/csv', 'text/markdown', 'text/x-markdown',\n 'text/xml', 'application/xml', 'text/yaml', 'text/x-yaml',\n 'application/x-yaml', 'application/rtf', 'text/rtf',\n 'application/json', 'image/svg+xml',\n]);\n\nconst IMAGE_MIME_TYPES = new Set([\n 'image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/bmp', 'image/tiff',\n]);\n\n// Already-compressed formats — no gain from recompressing\nconst SKIP_MIME_TYPES = new Set([\n 'video/mp4', 'video/webm', 'video/quicktime',\n 'audio/mpeg', 'audio/wav', 'audio/ogg', 'audio/webm', 'audio/mp4',\n 'application/zip', 'application/pdf',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n]);\n\nexport type CompressionStrategy = 'image' | 'gzip' | 'skip';\n\nexport function getCompressionStrategy(\n mimeType: string,\n config: ResolvedCompressionConfig,\n): CompressionStrategy {\n if (SKIP_MIME_TYPES.has(mimeType)) return 'skip';\n if (IMAGE_MIME_TYPES.has(mimeType)) return 'image';\n if (config.compressDocuments && GZIP_MIME_TYPES.has(mimeType)) return 'gzip';\n return 'skip';\n}\n\n/**\n * Attempt to compress a file using the platform-provided compressor.\n * Returns the original file unchanged (as a CompressedFile with compressed=false)\n * if compression is disabled, no compressor is provided, or the strategy is 'skip'.\n */\nexport async function compressFile(\n file: UploadableFile,\n platformCompressFn: PlatformCompressFn | undefined,\n config: ResolvedCompressionConfig,\n): Promise<CompressedFile> {\n const noop: CompressedFile = {\n ...file,\n originalSize: file.size,\n compressed: false,\n compressionAlgorithm: 'none' as CompressionAlgorithm,\n };\n\n if (!config.enabled || !platformCompressFn) return noop;\n\n const strategy = getCompressionStrategy(file.type, config);\n if (strategy === 'skip') return noop;\n\n try {\n return await platformCompressFn(file, config);\n } catch {\n // Compression failure is non-fatal — fall back to original\n return noop;\n }\n}\n","import axios, {\n AxiosInstance,\n InternalAxiosRequestConfig,\n} from 'axios';\nimport type { ResolvedConfig } from '../config/types.js';\nimport type { AuthTokens } from '../types/index.js';\nimport { encryptPayload, decryptPayload, isTransitEnvelope } from '../crypto/transit.js';\nimport { getSessionKey, getSessionId, isTransitEnabled, waitForTransitReady, configureTransit } from '../crypto/session.js';\n\nexport type TokenStore = {\n getAccessToken: () => string | null | undefined;\n getRefreshToken: () => string | null | undefined;\n setTokens: (tokens: AuthTokens) => void;\n clearTokens: () => void;\n};\n\nlet _tokenStore: TokenStore | null = null;\nlet _config: ResolvedConfig | null = null;\n// Avatar headers are sent only on the first authenticated request after init.\n// The server hashes on receive and deduplicates — subsequent requests don't need them.\nlet _avatarSent = false;\n\nexport function initApiClient(config: ResolvedConfig, tokenStore: TokenStore): AxiosInstance {\n _config = config;\n _tokenStore = tokenStore;\n _avatarSent = false; // reset on re-init (new session / authToken change)\n\n const client = axios.create({\n baseURL: config.apiUrl,\n headers: { 'Content-Type': 'application/json' },\n });\n\n // Configure transit as early as possible — before any requests fire —\n // so waitForTransitReady() in the interceptor knows whether to block or not.\n configureTransit(config.transitEncryption);\n\n // ── Request interceptor ──────────────────────────────────────────────────\n client.interceptors.request.use(async (req: InternalAxiosRequestConfig) => {\n const token = _tokenStore?.getAccessToken();\n if (token) req.headers['Authorization'] = `Bearer ${token}`;\n if (_config?.userId) req.headers['x-user-id'] = _config.userId;\n if (_config?.tenantId) req.headers['X-Tenant-ID'] = _config.tenantId;\n // Send avatar on the first request only — server hashes and deduplicates\n if (token && !_avatarSent && _config?.avatar) {\n if (_config.avatar.base64) req.headers['x-avatar-base64'] = _config.avatar.base64;\n else if (_config.avatar.url) req.headers['x-avatar-url'] = _config.avatar.url;\n _avatarSent = true;\n }\n\n // Wait for the transit session key to be established before sending.\n // HTTP requests that fire before connectSocket completes (e.g. getMe, getConfig)\n // are held here until the ECDH handshake finishes. Resolves immediately when\n // transit is disabled or already established.\n await waitForTransitReady();\n\n // Transit encryption — attach session ID on every request so the server\n // can look up the session key. Encrypt body when present.\n if (isTransitEnabled()) {\n const sessionId = getSessionId();\n const key = getSessionKey();\n if (sessionId && key) {\n req.headers['x-transit-session'] = sessionId;\n if (req.data !== undefined && req.data !== null) {\n const envelope = await encryptPayload(req.data, key);\n req.data = envelope;\n req.headers['x-transit-encrypted'] = '1';\n }\n }\n }\n\n return req;\n });\n\n let isRefreshing = false;\n let refreshQueue: Array<(token: string) => void> = [];\n\n // ── Response interceptor ─────────────────────────────────────────────────\n client.interceptors.response.use(\n async (response) => {\n // Transit decryption — server wraps encrypted payload inside { success, data: <envelope> }.\n // Decrypt the inner envelope, then let the standard unwrap below handle { success, data }.\n if (isTransitEnabled()) {\n const key = getSessionKey();\n if (key) {\n // Case 1: entire response is the envelope (unlikely but handle it)\n if (isTransitEnvelope(response.data)) {\n response.data = await decryptPayload(response.data, key);\n }\n // Case 2: envelope is nested inside { success, data: <envelope> }\n else if (response.data?.data && isTransitEnvelope(response.data.data)) {\n response.data.data = await decryptPayload(response.data.data, key);\n }\n }\n }\n\n // Standard { success, data } unwrap\n if (\n response.data &&\n typeof response.data === 'object' &&\n 'success' in response.data &&\n 'data' in response.data\n ) {\n response.data = response.data.data;\n }\n return response;\n },\n async (error) => {\n // Decrypt error response body — error filter encrypts it too\n if (isTransitEnabled() && error.response?.data) {\n const key = getSessionKey();\n if (key) {\n try {\n if (isTransitEnvelope(error.response.data)) {\n error.response.data = await decryptPayload(error.response.data, key);\n } else if (error.response.data?.data && isTransitEnvelope(error.response.data.data)) {\n error.response.data.data = await decryptPayload(error.response.data.data, key);\n }\n } catch { /* decryption failed — leave as-is */ }\n }\n }\n\n const original = error.config as InternalAxiosRequestConfig & { _retry?: boolean };\n\n if (error.response?.status === 401 && !original._retry) {\n const refreshToken = _tokenStore?.getRefreshToken();\n if (!refreshToken) {\n _tokenStore?.clearTokens();\n return Promise.reject(error);\n }\n\n if (isRefreshing) {\n return new Promise((resolve) => {\n refreshQueue.push((newToken) => {\n original.headers['Authorization'] = `Bearer ${newToken}`;\n resolve(client(original));\n });\n });\n }\n\n original._retry = true;\n isRefreshing = true;\n\n try {\n const { data } = await axios.post<{ data: AuthTokens }>(\n `${_config!.apiUrl}/auth/refresh`,\n { refreshToken },\n );\n const tokens: AuthTokens = (data as any).data ?? data;\n _tokenStore?.setTokens(tokens);\n refreshQueue.forEach((cb) => cb(tokens.accessToken));\n refreshQueue = [];\n original.headers['Authorization'] = `Bearer ${tokens.accessToken}`;\n return client(original);\n } catch {\n _tokenStore?.clearTokens();\n return Promise.reject(error);\n } finally {\n isRefreshing = false;\n }\n }\n\n return Promise.reject(error);\n },\n );\n\n return client;\n}\n\nlet _instance: AxiosInstance | null = null;\n\nexport function setApiClientInstance(instance: AxiosInstance) {\n _instance = instance;\n}\n\nexport function getApiClient(): AxiosInstance {\n if (!_instance) throw new Error('[AntzChat] API client not initialized. Call initApiClient first.');\n return _instance;\n}\n","export interface TransitEnvelope {\n v: 1;\n iv: string; // base64, 12 bytes\n tag: string; // base64, 16 bytes\n ct: string; // base64, ciphertext\n}\n\n// ─── Encrypt ─────────────────────────────────────────────────────────────────\n\nexport async function encryptPayload(\n data: unknown,\n sessionKey: CryptoKey,\n): Promise<TransitEnvelope> {\n const iv = globalThis.crypto.getRandomValues(new Uint8Array(12));\n const plaintext = new TextEncoder().encode(JSON.stringify(data));\n\n const encrypted = await globalThis.crypto.subtle.encrypt(\n { name: 'AES-GCM', iv },\n sessionKey,\n plaintext,\n );\n\n // AES-GCM appends the 16-byte auth tag to the ciphertext buffer\n const ct = encrypted.slice(0, encrypted.byteLength - 16);\n const tag = encrypted.slice(encrypted.byteLength - 16);\n\n return {\n v: 1,\n iv: bufToB64(iv),\n tag: bufToB64(tag),\n ct: bufToB64(ct),\n };\n}\n\n// ─── Decrypt ─────────────────────────────────────────────────────────────────\n\nexport async function decryptPayload(\n envelope: TransitEnvelope,\n sessionKey: CryptoKey,\n): Promise<unknown> {\n const iv = b64ToBuf(envelope.iv);\n const tag = b64ToBuf(envelope.tag);\n const ct = b64ToBuf(envelope.ct);\n\n // Reassemble ct + tag as SubtleCrypto expects them concatenated\n const combined = new Uint8Array(ct.byteLength + tag.byteLength);\n combined.set(new Uint8Array(ct), 0);\n combined.set(new Uint8Array(tag), ct.byteLength);\n\n const decrypted = await globalThis.crypto.subtle.decrypt(\n { name: 'AES-GCM', iv: new Uint8Array(iv) },\n sessionKey,\n combined,\n );\n\n return JSON.parse(new TextDecoder().decode(decrypted));\n}\n\nexport function isTransitEnvelope(v: unknown): v is TransitEnvelope {\n return (\n typeof v === 'object' &&\n v !== null &&\n (v as any).v === 1 &&\n typeof (v as any).iv === 'string' &&\n typeof (v as any).tag === 'string' &&\n typeof (v as any).ct === 'string'\n );\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction bufToB64(buf: ArrayBuffer | Uint8Array): string {\n const bytes = buf instanceof Uint8Array ? buf : new Uint8Array(buf);\n let str = '';\n bytes.forEach(b => { str += String.fromCharCode(b); });\n return btoa(str);\n}\n\nfunction b64ToBuf(b64: string): ArrayBuffer {\n const bin = atob(b64);\n const buf = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) buf[i] = bin.charCodeAt(i);\n return buf.buffer;\n}\n","import type { TransitAlgo } from './detect.js';\n\ninterface TransitSession {\n sessionKey: CryptoKey;\n algo: TransitAlgo;\n sessionId: string;\n enabled: boolean;\n}\n\nlet _session: TransitSession | null = null;\n\n// Resolves when a session is first established — lets HTTP interceptor await it.\nlet _readyResolve: (() => void) | null = null;\nlet _readyPromise: Promise<void> | null = null;\n// Whether transit was explicitly configured (true/false set by config, null = not yet configured)\nlet _transitConfigured: boolean | null = null;\n\nexport function configureTransit(enabled: boolean): void {\n _transitConfigured = enabled;\n if (!enabled) {\n // Transit disabled — resolve immediately so HTTP requests don't block\n _readyResolve?.();\n _readyResolve = null;\n }\n}\n\nexport function waitForTransitReady(): Promise<void> {\n // Not configured yet or disabled — resolve immediately\n if (!_transitConfigured) return Promise.resolve();\n // Already have a session — resolve immediately\n if (_session) return Promise.resolve();\n // Create a promise that resolves when setTransitSession is called\n if (!_readyPromise) {\n _readyPromise = new Promise<void>((resolve) => {\n _readyResolve = resolve;\n });\n }\n return _readyPromise;\n}\n\nexport function setTransitSession(session: TransitSession): void {\n _session = session;\n // Resolve any pending HTTP requests waiting for the session key\n _readyResolve?.();\n _readyResolve = null;\n}\n\nexport function getTransitSession(): TransitSession | null {\n return _session;\n}\n\nexport function clearTransitSession(): void {\n _session = null;\n // Reset the ready promise so the next connection can block again\n _readyPromise = null;\n _readyResolve = null;\n}\n\nexport function isTransitEnabled(): boolean {\n return _session?.enabled === true;\n}\n\nexport function getSessionKey(): CryptoKey | null {\n return _session?.sessionKey ?? null;\n}\n\n// Returns sessionId for the x-transit-session header sent with HTTP requests.\nexport function getSessionId(): string | null {\n return _session?.sessionId ?? null;\n}\n","import type { AuthResponse, AuthTokens, LoginCredentials, RegisterData, User } from '../types/index.js';\nimport { getApiClient } from './client.js';\n\nexport const authApi = {\n async login(credentials: LoginCredentials): Promise<AuthResponse> {\n const { data } = await getApiClient().post<AuthResponse>('/auth/login', credentials);\n return data;\n },\n\n async register(payload: RegisterData): Promise<AuthResponse> {\n const { data } = await getApiClient().post<AuthResponse>('/auth/register', payload);\n return data;\n },\n\n async refresh(refreshToken: string): Promise<AuthTokens> {\n const { data } = await getApiClient().post<AuthTokens>('/auth/refresh', { refreshToken });\n return data;\n },\n\n async logout(refreshToken?: string): Promise<void> {\n await getApiClient().post('/auth/logout', refreshToken ? { refreshToken } : {});\n },\n\n async logoutAll(): Promise<void> {\n await getApiClient().post('/auth/logout-all');\n },\n\n async getMe(): Promise<User> {\n const { data } = await getApiClient().get<User>('/users/me');\n return data;\n },\n\n // Upload a new avatar file (builtin mode — multipart)\n async uploadAvatar(file: File | Blob, mimeType?: string): Promise<{ avatarUrl: string }> {\n const form = new FormData();\n form.append('avatar', file instanceof File ? file : new File([file], 'avatar.jpg', { type: mimeType ?? 'image/jpeg' }));\n const { data } = await getApiClient().put<{ avatarUrl: string }>('/users/me/avatar', form, {\n headers: { 'Content-Type': 'multipart/form-data' },\n });\n return data;\n },\n\n // Sync avatar from URL or base64 (non-builtin modes — or post-init update)\n async syncAvatar(source: { url?: string; base64?: string }): Promise<{ avatarUrl: string }> {\n const headers: Record<string, string> = {};\n if (source.base64) headers['x-avatar-base64'] = source.base64;\n else if (source.url) headers['x-avatar-url'] = source.url;\n const { data } = await getApiClient().post<{ avatarUrl: string }>('/users/me/avatar/sync', {}, { headers });\n return data;\n },\n};\n","import { getApiClient } from './client.js';\nimport type { AppConfig } from '../types/index.js';\n\nexport const appConfigApi = {\n async get(): Promise<AppConfig> {\n const { data } = await getApiClient().get<AppConfig>('/app/config');\n return data;\n },\n};\n","import type {\n Message,\n CursorPaginatedResponse,\n PaginatedResponse,\n SendMessagePayload,\n} from '../types/index.js';\nimport { getApiClient } from './client.js';\n\nexport interface ListMessagesParams {\n cursor?: string;\n limit?: number;\n direction?: 'before' | 'after';\n}\n\nexport interface SearchParams {\n query: string;\n conversationId?: string;\n page?: number;\n limit?: number;\n}\n\nexport interface SendData {\n text?: string;\n attachments?: SendMessagePayload['attachments'];\n replyTo?: string;\n tempId?: string;\n}\n\nexport const messagesApi = {\n async list(conversationId: string, params: ListMessagesParams = {}): Promise<CursorPaginatedResponse<Message>> {\n const { cursor, direction, ...rest } = params;\n const serverParams: Record<string, unknown> = { ...rest };\n if (cursor) {\n serverParams[direction === 'after' ? 'after' : 'before'] = cursor;\n }\n const { data } = await getApiClient().get<CursorPaginatedResponse<Message>>(\n `/conversations/${conversationId}/messages`,\n { params: serverParams },\n );\n return data;\n },\n\n async get(messageId: string): Promise<Message> {\n const { data } = await getApiClient().get<Message>(`/messages/${messageId}`);\n return data;\n },\n\n async send(conversationId: string, payload: SendData): Promise<Message> {\n const { data } = await getApiClient().post<Message>(\n `/conversations/${conversationId}/messages`,\n payload,\n );\n return data;\n },\n\n async update(messageId: string, text: string): Promise<Message> {\n const { data } = await getApiClient().put<Message>(`/messages/${messageId}`, { text });\n return data;\n },\n\n async delete(messageId: string): Promise<void> {\n await getApiClient().delete(`/messages/${messageId}`);\n },\n\n async deleteForMe(messageId: string): Promise<void> {\n await getApiClient().delete(`/messages/${messageId}/for-me`);\n },\n\n async addReaction(messageId: string, emoji: string): Promise<Message> {\n const { data } = await getApiClient().post<Message>(`/messages/${messageId}/reactions`, { emoji });\n return data;\n },\n\n async removeReaction(messageId: string, emoji: string): Promise<Message> {\n const { data } = await getApiClient().delete<Message>(\n `/messages/${messageId}/reactions/${encodeURIComponent(emoji)}`,\n );\n return data;\n },\n\n async star(messageId: string): Promise<void> {\n await getApiClient().post(`/messages/${messageId}/star`);\n },\n\n async unstar(messageId: string): Promise<void> {\n await getApiClient().delete(`/messages/${messageId}/star`);\n },\n\n async getStarred(params: { page?: number; limit?: number; conversationId?: string } = {}): Promise<PaginatedResponse<Message>> {\n const { data } = await getApiClient().get<PaginatedResponse<Message>>('/messages/starred', { params });\n return data;\n },\n\n async search(params: SearchParams): Promise<PaginatedResponse<Message>> {\n const { data } = await getApiClient().get<PaginatedResponse<Message>>('/messages/search', { params });\n return data;\n },\n\n async getLastRead(conversationId: string): Promise<{ lastReadMessageId: string | null; lastReadAt: string | null }> {\n const { data } = await getApiClient().get<{ lastReadMessageId: string | null; lastReadAt: string | null }>(\n `/conversations/${conversationId}/read-receipt`,\n );\n return data;\n },\n\n async markAsRead(conversationId: string, messageId?: string): Promise<void> {\n await getApiClient().post(`/conversations/${conversationId}/read`, messageId ? { messageId } : {});\n },\n\n async pin(messageId: string): Promise<Message> {\n const { data } = await getApiClient().post<Message>(`/messages/${messageId}/pin`);\n return data;\n },\n\n async unpin(messageId: string): Promise<Message> {\n const { data } = await getApiClient().delete<Message>(`/messages/${messageId}/pin`);\n return data;\n },\n\n async getPinned(conversationId: string): Promise<Message[]> {\n const { data } = await getApiClient().get<Message[]>(`/conversations/${conversationId}/pinned-messages`);\n return data;\n },\n};\n","import type { Conversation, ConversationListParams, ConversationUnreadCount, Message, MessageStatus, Participant, PaginatedResponse, UnreadSummary, User } from '../types/index.js';\nimport { getApiClient } from './client.js';\n\nfunction normalizeParticipant(p: any): Participant {\n const hasUserDetails = p.displayName || p.username || p.avatarUrl;\n return {\n userId: p.userId,\n role: p.role,\n joinedAt: p.joinedAt,\n isActive: p.isActive,\n user: hasUserDetails\n ? {\n id: p.userId,\n tenantId: '',\n email: '',\n username: p.username ?? '',\n displayName: p.displayName ?? p.username ?? '',\n avatarUrl: p.avatarUrl,\n status: 'offline' as const,\n createdAt: p.joinedAt ?? '',\n updatedAt: p.joinedAt ?? '',\n }\n : (p.user as User | undefined),\n };\n}\n\nfunction normalizeLastMessage(lastMsg: any): Message | undefined {\n if (!lastMsg) return undefined;\n if (lastMsg.content !== undefined) return lastMsg as Message;\n return {\n id: lastMsg.messageId ?? '',\n tenantId: '',\n conversationId: '',\n senderId: '',\n content: {\n type: lastMsg.hasAttachments ? 'attachment' : 'text',\n text: lastMsg.contentPreview,\n },\n reactions: [],\n status: (lastMsg.status ?? 'sent') as MessageStatus,\n isEdited: false,\n sentAt: lastMsg.sentAt ?? '',\n createdAt: lastMsg.sentAt ?? '',\n };\n}\n\nexport function normalizeConversation(conv: any): Conversation {\n return {\n ...conv,\n id: conv.id ?? conv.conversationId,\n participants: (conv.participants ?? []).map(normalizeParticipant),\n lastMessage: normalizeLastMessage(conv.lastMessage),\n };\n}\n\nexport interface CreateGroupData {\n name: string;\n description?: string;\n participantIds: string[];\n}\n\nexport interface CreateDirectData {\n userId: string;\n}\n\nexport interface UpdateConversationData {\n name?: string;\n description?: string;\n}\n\nexport const conversationsApi = {\n async list(params: ConversationListParams = {}): Promise<PaginatedResponse<Conversation>> {\n const { data } = await getApiClient().get<PaginatedResponse<Conversation>>('/conversations', { params });\n return { ...data, data: data.data.map(normalizeConversation) };\n },\n\n async get(conversationId: string): Promise<Conversation> {\n const { data } = await getApiClient().get<Conversation>(`/conversations/${conversationId}`);\n return normalizeConversation(data);\n },\n\n async createGroup(payload: CreateGroupData): Promise<Conversation> {\n const { data } = await getApiClient().post<Conversation>('/conversations', payload);\n return normalizeConversation(data);\n },\n\n async createDirect(payload: CreateDirectData): Promise<Conversation> {\n const { data } = await getApiClient().post<Conversation>('/conversations/direct', payload);\n return normalizeConversation(data);\n },\n\n async update(conversationId: string, payload: UpdateConversationData): Promise<Conversation> {\n const { data } = await getApiClient().put<Conversation>(`/conversations/${conversationId}`, payload);\n return normalizeConversation(data);\n },\n\n async delete(conversationId: string): Promise<void> {\n await getApiClient().delete(`/conversations/${conversationId}`);\n },\n\n async addParticipants(conversationId: string, userIds: string[], role?: 'admin' | 'member'): Promise<Conversation> {\n const { data } = await getApiClient().post<Conversation>(\n `/conversations/${conversationId}/participants`,\n { userIds, ...(role && { role }) },\n );\n return normalizeConversation(data);\n },\n\n async removeParticipant(conversationId: string, userId: string): Promise<Conversation> {\n const { data } = await getApiClient().delete<Conversation>(\n `/conversations/${conversationId}/participants/${userId}`,\n );\n return normalizeConversation(data);\n },\n\n async updateParticipantRole(conversationId: string, userId: string, role: 'admin' | 'member'): Promise<Conversation> {\n const { data } = await getApiClient().put<Conversation>(\n `/conversations/${conversationId}/participants/${userId}/role`,\n { role },\n );\n return normalizeConversation(data);\n },\n\n async mute(conversationId: string, mutedUntil?: string): Promise<void> {\n await getApiClient().post(`/conversations/${conversationId}/mute`, mutedUntil ? { mutedUntil } : {});\n },\n\n async unmute(conversationId: string): Promise<void> {\n await getApiClient().delete(`/conversations/${conversationId}/mute`);\n },\n\n async pin(conversationId: string): Promise<void> {\n await getApiClient().post(`/conversations/${conversationId}/pin`);\n },\n\n async unpin(conversationId: string): Promise<void> {\n await getApiClient().delete(`/conversations/${conversationId}/pin`);\n },\n\n async leave(conversationId: string): Promise<void> {\n await getApiClient().delete(`/conversations/${conversationId}/leave`);\n },\n\n async getMembers(conversationId: string, filter?: 'deleted' | 'all'): Promise<Participant[]> {\n const { data } = await getApiClient().get<Participant[]>(\n `/conversations/${conversationId}/participants`,\n filter ? { params: { filter } } : undefined,\n );\n return data;\n },\n\n /**\n * Get unread message count for a single conversation.\n * Use this after app foreground or socket reconnect to refresh a specific count.\n */\n async getUnreadCount(conversationId: string): Promise<ConversationUnreadCount> {\n const { data } = await getApiClient().get<ConversationUnreadCount>(\n `/conversations/${conversationId}/unread`,\n );\n return data;\n },\n\n /**\n * Get total unread count across all conversations + per-conversation breakdown.\n * Use on app cold start, foreground resume, or after socket reconnect.\n * The socket keeps counts live while connected — this is the source of truth\n * when the socket was down.\n */\n async getUnreadSummary(): Promise<UnreadSummary> {\n const { data } = await getApiClient().get<UnreadSummary>('/conversations/unread');\n return data;\n },\n\n /**\n * Set the group icon from an already-uploaded file (admin only).\n * The fileId comes from uploadBatch() / client.uploadFiles() — same as attachments.\n * Server copies storageKey into conversation.iconMeta and deletes the chat_files record.\n */\n async uploadIcon(conversationId: string, fileId: string): Promise<Conversation> {\n const { data } = await getApiClient().put<Conversation>(\n `/conversations/${conversationId}/icon`,\n { fileId },\n );\n return normalizeConversation(data);\n },\n\n};\n","import type {\n BatchUploadResult,\n FileResponse,\n PaginatedResponse,\n PresignedUrlRequest,\n PresignedUrlResponse,\n FileType,\n UploadableFile,\n} from '../types/index.js';\nimport type { PlatformUploadFn, PlatformCompressFn, ResolvedCompressionConfig } from '../config/types.js';\nimport { compressFile } from '../compression/compress.js';\nimport { getApiClient } from './client.js';\n\nexport const storageApi = {\n async requestPresignedUrl(payload: PresignedUrlRequest): Promise<PresignedUrlResponse> {\n const { data } = await getApiClient().post<PresignedUrlResponse>('/storage/presigned-url', payload);\n return data;\n },\n\n async requestPresignedUrlBatch(files: PresignedUrlRequest[]): Promise<{\n urls: PresignedUrlResponse[];\n errors: Array<{ filename: string; error: string }>;\n }> {\n const { data } = await getApiClient().post('/storage/presigned-url/batch', { files });\n return data;\n },\n\n async confirmUpload(fileId: string): Promise<FileResponse> {\n const { data } = await getApiClient().post<FileResponse>(`/storage/confirm/${fileId}`);\n return data;\n },\n\n async getFile(fileId: string): Promise<FileResponse> {\n const { data } = await getApiClient().get<FileResponse>(`/storage/files/${fileId}`);\n return data;\n },\n\n async getFileUrl(fileId: string, expiresIn?: number): Promise<{ url: string; expiresAt: string }> {\n const { data } = await getApiClient().get(`/storage/files/${fileId}/url`, {\n params: expiresIn ? { expiresIn } : {},\n });\n return data;\n },\n\n async deleteFile(fileId: string): Promise<void> {\n await getApiClient().delete(`/storage/files/${fileId}`);\n },\n\n async getConversationFiles(\n conversationId: string,\n params: { page?: number; limit?: number; type?: FileType } = {},\n ): Promise<PaginatedResponse<FileResponse>> {\n const { data } = await getApiClient().get(\n `/storage/conversations/${conversationId}/files`,\n { params },\n );\n return data;\n },\n\n async getMyFiles(params: { page?: number; limit?: number } = {}): Promise<PaginatedResponse<FileResponse>> {\n const { data } = await getApiClient().get('/storage/my-files', { params });\n return data;\n },\n};\n\n/**\n * High-level batch upload.\n * The actual binary transfer is delegated to platformUploadFn so this\n * function is platform-agnostic (works on web and React Native).\n * If platformCompressFn + compressionConfig are provided, each file is\n * compressed before the presigned URL is requested (so the server receives\n * the correct compressed size and MIME type).\n */\nexport async function uploadBatch(\n files: UploadableFile[],\n platformUploadFn: PlatformUploadFn,\n conversationId?: string,\n onProgress?: (pct: number) => void,\n platformCompressFn?: PlatformCompressFn,\n compressionConfig?: ResolvedCompressionConfig,\n): Promise<BatchUploadResult> {\n // Compress all files first (no-ops for unsupported types or when disabled)\n const compressedFiles = await Promise.all(\n files.map((f) => compressFile(f, platformCompressFn, compressionConfig ?? { enabled: false, imageQuality: 0.85, imageMaxDimension: 1920, compressDocuments: true })),\n );\n\n const requests: PresignedUrlRequest[] = compressedFiles.map((f) => ({\n filename: f.name,\n mimeType: f.type,\n size: f.size,\n conversationId,\n ...(f.compressed && {\n metadata: {\n compressed: f.compressed,\n originalSize: f.originalSize,\n compressionAlgorithm: f.compressionAlgorithm,\n },\n }),\n }));\n\n const { urls, errors: requestErrors } = await storageApi.requestPresignedUrlBatch(requests);\n\n const progressMap: Record<number, number> = {};\n const reportProgress = () => {\n if (!onProgress) return;\n const vals = Object.values(progressMap);\n const avg = vals.reduce((s, v) => s + v, 0) / Math.max(vals.length, 1);\n onProgress(Math.round(avg));\n };\n\n const successful: FileResponse[] = [];\n const failed: Array<{ filename: string; error: string }> = [...requestErrors];\n\n await Promise.all(\n urls.map(async (presigned, idx) => {\n const file = compressedFiles[idx];\n progressMap[idx] = 0;\n try {\n await platformUploadFn(presigned, file, (pct) => {\n progressMap[idx] = Math.round(pct * 0.9);\n reportProgress();\n });\n\n const result = await storageApi.confirmUpload(presigned.fileId);\n progressMap[idx] = 100;\n reportProgress();\n successful.push(result);\n } catch (err) {\n failed.push({ filename: file.name, error: (err as Error).message });\n }\n }),\n );\n\n return { successful, failed };\n}\n","import { getApiClient } from './client.js';\n\n// ─── Types ─────────────────────────────────────────────────────────────────\n\n/** Shared fields for every device registration. */\ninterface DeviceTokenBase {\n /**\n * Client-generated stable UUID for this device.\n * Store this in localStorage / SecureStore / Keychain and reuse it on every\n * app start so the same record is updated rather than duplicated.\n */\n deviceId: string;\n platform: 'ios' | 'android' | 'web';\n provider: 'expo' | 'fcm' | 'apns' | 'web-push';\n userAgent?: string;\n}\n\n/** Mobile push registration (expo | fcm | apns). */\nexport interface MobileDeviceToken extends DeviceTokenBase {\n provider: 'expo' | 'fcm' | 'apns';\n /** FCM registration token / APNs device token / Expo push token. */\n token: string;\n endpoint?: never;\n p256dh?: never;\n auth?: never;\n}\n\n/** Web push (VAPID) registration. */\nexport interface WebPushDeviceToken extends DeviceTokenBase {\n provider: 'web-push';\n /** PushSubscription.endpoint */\n endpoint: string;\n /** base64url-encoded PushSubscription key 'p256dh' */\n p256dh: string;\n /** base64url-encoded PushSubscription key 'auth' */\n auth: string;\n token?: never;\n}\n\nexport type RegisterDeviceTokenPayload = MobileDeviceToken | WebPushDeviceToken;\n\n// ─── API ───────────────────────────────────────────────────────────────────\n\nexport const devicesApi = {\n /**\n * Register or update a device push token with the chat server.\n *\n * Upserts by `deviceId` — calling this multiple times with the same deviceId\n * simply refreshes the token value (tokens can rotate silently on some platforms).\n *\n * The SDK never calls this automatically. The parent app or the `tokenProvider`\n * option in `pushNotifications` config is responsible for calling it after\n * obtaining the token from the OS / browser.\n */\n async register(payload: RegisterDeviceTokenPayload): Promise<void> {\n await getApiClient().post('/users/me/devices', payload);\n },\n\n /**\n * Remove a device token from the chat server.\n * Call this on logout so the user stops receiving push notifications on this device.\n */\n async remove(deviceId: string): Promise<void> {\n await getApiClient().delete(`/users/me/devices/${deviceId}`);\n },\n};\n","import type { PaginatedResponse, User, UserPreferences } from '../types/index.js';\nimport { getApiClient } from './client.js';\n\nexport const usersApi = {\n async list(params: { query?: string; page?: number; limit?: number } = {}): Promise<PaginatedResponse<User>> {\n const { data } = await getApiClient().get<PaginatedResponse<User>>('/users', { params });\n return data;\n },\n\n async getById(userId: string): Promise<User> {\n const { data } = await getApiClient().get<User>(`/users/${userId}`);\n return data;\n },\n\n async getLastSeen(userId: string): Promise<{ lastSeenAt: string | null }> {\n const { data } = await getApiClient().get<User>(`/users/${userId}`);\n return { lastSeenAt: data.lastSeenAt ?? null };\n },\n\n /**\n * Update notification preferences for the current user.\n * Partial update — only send fields you want to change.\n * A prefs record is automatically created with defaults when a device\n * token is first registered, so this never fails with \"not found\".\n */\n async updatePreferences(prefs: UserPreferences): Promise<User> {\n const { data } = await getApiClient().put<User>('/users/me/preferences', prefs);\n return data;\n },\n\n /**\n * Fetch current notification preferences for the current user.\n * Returns null if no prefs record exists yet (all defaults apply).\n */\n async getPreferences(): Promise<UserPreferences | null> {\n try {\n const { data } = await getApiClient().get<UserPreferences>('/users/me/preferences');\n return data;\n } catch {\n return null;\n }\n },\n};\n","import { io, Socket } from 'socket.io-client';\nimport type { ResolvedConfig } from '../config/types.js';\nimport type { ReadReceiptEvent, UserStatusEvent } from '../types/index.js';\nimport { detectTransitAlgo, resetAlgoCache } from '../crypto/detect.js';\nimport { fetchServerKeys, performHandshake } from '../crypto/handshake.js';\nimport { setTransitSession, clearTransitSession, configureTransit, isTransitEnabled, getSessionKey } from '../crypto/session.js';\nimport { encryptPayload, decryptPayload, isTransitEnvelope } from '../crypto/transit.js';\n\nexport type SocketStatus = 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'error';\nexport type StatusListener = (status: SocketStatus) => void;\n\nlet _socket: Socket | null = null;\nlet _status: SocketStatus = 'disconnected';\nconst _statusListeners = new Set<StatusListener>();\n\n// Stored so SocketProvider can call refreshAndReconnect() without re-passing config\nlet _getToken: (() => string | null | undefined) | null = null;\nlet _userId: string | undefined;\nlet _tenantId: string | undefined;\n\nfunction setStatus(s: SocketStatus) {\n _status = s;\n _statusListeners.forEach(l => l(s));\n}\n\nexport function getSocket(): Socket {\n if (!_socket) throw new Error('[AntzChat] Socket not initialized. Call connectSocket first.');\n return _socket;\n}\n\n/** Returns the socket if connected, null otherwise. Use this for fire-and-forget\n * operations (markRead, typing) that should silently no-op when not yet connected. */\nexport function tryGetSocket(): Socket | null {\n return _socket?.connected ? _socket : null;\n}\n\nexport function getSocketStatus(): SocketStatus {\n return _status;\n}\n\nexport function onSocketStatus(listener: StatusListener): () => void {\n _statusListeners.add(listener);\n return () => _statusListeners.delete(listener);\n}\n\n// ─── Transit helpers ──────────────────────────────────────────────────────────\n\n// Wraps socket.emit: encrypts payload when transit is enabled and key is ready.\nexport async function secureEmit(\n socket: Socket,\n event: string,\n payload: unknown,\n ack?: (response: unknown) => void,\n): Promise<void> {\n if (isTransitEnabled()) {\n const key = getSessionKey();\n if (key) {\n const envelope = await encryptPayload(payload, key);\n if (ack) {\n socket.emit(event, envelope, async (encryptedResponse: unknown) => {\n if (isTransitEnvelope(encryptedResponse)) {\n try {\n const decrypted = await decryptPayload(encryptedResponse, key);\n ack(decrypted);\n } catch {\n ack(encryptedResponse);\n }\n } else {\n ack(encryptedResponse);\n }\n });\n } else {\n socket.emit(event, envelope);\n }\n return;\n }\n }\n // Transit disabled or key not ready — send plaintext\n if (ack) {\n socket.emit(event, payload, ack);\n } else {\n socket.emit(event, payload);\n }\n}\n\n// Registers an incoming event handler that auto-decrypts when transit is enabled.\nexport function secureOn(\n socket: Socket,\n event: string,\n handler: (data: unknown) => void,\n): void {\n socket.on(event, async (raw: unknown) => {\n if (isTransitEnabled() && isTransitEnvelope(raw)) {\n const key = getSessionKey();\n if (key) {\n try {\n const data = await decryptPayload(raw, key);\n handler(data);\n return;\n } catch {\n console.error(`[AntzChat] Transit decryption failed for event: ${event}`);\n return;\n }\n }\n }\n handler(raw);\n });\n}\n\n// ─── Connect ──────────────────────────────────────────────────────────────────\n\nexport async function connectSocket(\n config: ResolvedConfig,\n getToken: () => string | null | undefined,\n): Promise<Socket> {\n if (_socket && !_socket.disconnected) return _socket;\n\n _getToken = getToken;\n _userId = config.userId;\n _tenantId = config.tenantId;\n\n const token = getToken();\n\n const socketHandshakeAuth: Record<string, unknown> = {\n token: token ? `Bearer ${token}` : '',\n ...(config.userId && { userId: config.userId }),\n ...(config.tenantId && { tenantId: config.tenantId }),\n ...(config.avatar?.url && { avatarUrl: config.avatar.url }),\n ...(config.avatar?.base64 && { avatarBase64: config.avatar.base64 }),\n };\n\n // ── Transit handshake setup ──────────────────────────────────────────────\n // performHandshake returns a bound deriveSessionKey closure — captures the\n // ephemeral private key for this specific connection with no shared module state.\n let boundDeriveSessionKey: ((sessionId: string) => Promise<CryptoKey>) | null = null;\n\n if (config.transitEncryption) {\n try {\n const serverKeys = await fetchServerKeys(config.apiUrl);\n if (!serverKeys.enabled) {\n throw new Error(\n '[AntzChat] Transit encryption mismatch: SDK has transitEncryption=true but server ' +\n 'has TRANSIT_ENCRYPTION_ENABLED=false. Align the config on both sides.',\n );\n }\n const algo = await detectTransitAlgo();\n boundDeriveSessionKey = await performHandshake(algo, serverKeys, socketHandshakeAuth);\n } catch (err: any) {\n if (err.message?.startsWith('[AntzChat] Transit encryption mismatch')) throw err;\n // Non-fatal (dev/offline) — log and continue without transit encryption\n console.warn('[AntzChat] Transit handshake setup failed, connecting without transit encryption:', err.message);\n }\n } else {\n // Explicitly disabled — verify server also has it off to catch config mismatches early\n try {\n const serverKeys = await fetchServerKeys(config.apiUrl);\n if (serverKeys.enabled) {\n throw new Error(\n '[AntzChat] Transit encryption mismatch: SDK has transitEncryption=false but server ' +\n 'has TRANSIT_ENCRYPTION_ENABLED=true. Align the config on both sides.',\n );\n }\n } catch (err: any) {\n if (err.message?.startsWith('[AntzChat] Transit encryption mismatch')) throw err;\n // /crypto/pubkey unavailable (old server without transit support) — proceed\n }\n }\n\n _socket = io(`${config.socketOrigin}/chat`, {\n auth: socketHandshakeAuth,\n path: config.socketPath,\n transports: ['websocket', 'polling'],\n reconnection: true,\n reconnectionAttempts: 10,\n reconnectionDelay: 1000,\n reconnectionDelayMax: 5000,\n autoConnect: true,\n });\n\n setStatus('connecting');\n\n _socket.on('connect', () => setStatus('connected'));\n _socket.on('disconnect', () => {\n setStatus('disconnected');\n clearTransitSession();\n });\n _socket.on('connect_error', (err) => {\n console.error('[AntzChat] Socket connect_error:', err?.message, (err as any)?.data);\n setStatus('error');\n });\n _socket.on('reconnecting', () => setStatus('reconnecting'));\n _socket.on('reconnect', () => setStatus('connected'));\n\n // ── Transit session key derivation ──────────────────────────────────────\n // Block connectSocket from resolving until the session key is ready.\n // This ensures all HTTP requests and socket.on() registrations that happen\n // after await connectSocket() already have an active session key.\n if (config.transitEncryption && boundDeriveSessionKey) {\n await new Promise<void>((resolve) => {\n const done = () => {\n clearTimeout(timeout);\n resolve();\n };\n\n // Safety timeout — if transit_session never arrives (connect_error, server\n // doesn't support transit), unblock HTTP and continue without encryption.\n const timeout = setTimeout(() => {\n console.warn('[AntzChat] transit_session timeout — unblocking HTTP without transit encryption');\n // Disable transit so HTTP requests are not blocked indefinitely\n configureTransit(false);\n done();\n }, 5000);\n\n _socket!.on('transit_session', async ({ sessionId }: { sessionId: string }) => {\n try {\n if (boundDeriveSessionKey) {\n const sessionKey = await boundDeriveSessionKey(sessionId);\n const algo = (socketHandshakeAuth['transitAlgo'] as 'x25519' | 'p256') ?? 'p256';\n setTransitSession({ sessionKey, algo, sessionId, enabled: true });\n }\n } catch (err: any) {\n console.error('[AntzChat] Failed to derive transit session key:', err.message);\n }\n done();\n });\n\n // Also unblock if the socket fails to connect at all\n _socket!.on('connect_error', () => done());\n });\n }\n\n // ── Application event listeners ──────────────────────────────────────────\n // Use secureOn so incoming events are auto-decrypted when transit is enabled.\n secureOn(_socket, 'read_receipt', (event) => {\n import('../stores/chat.store.js').then(({ useChatStore }) => {\n const e = event as ReadReceiptEvent;\n useChatStore.getState().setLastRead(e.conversationId, e.messageId, e.readAt);\n });\n });\n\n secureOn(_socket, 'user_online', (event) => {\n import('../stores/chat.store.js').then(({ useChatStore }) => {\n useChatStore.getState().setUserOnline((event as UserStatusEvent).userId);\n });\n });\n\n secureOn(_socket, 'user_offline', (event) => {\n import('../stores/chat.store.js').then(({ useChatStore }) => {\n const e = event as UserStatusEvent;\n const store = useChatStore.getState();\n store.setUserOffline(e.userId);\n if (e.lastSeenAt) store.setLastSeen(e.userId, e.lastSeenAt);\n });\n });\n\n // Return a proxy so all socket.on() calls from SocketProvider/consumers\n // automatically go through secureOn (decrypt on receive).\n return createSecureSocketProxy(_socket);\n}\n\n// Wraps a Socket so every .on() call is transparently routed through secureOn.\n// Events that don't need decryption (transit disabled or no key) pass through unchanged.\nfunction createSecureSocketProxy(socket: Socket): Socket {\n return new Proxy(socket, {\n get(target, prop) {\n if (prop === 'on') {\n return (event: string, handler: (data: unknown) => void) => {\n // Low-level Socket.IO internal events pass through directly\n const internal = ['connect', 'disconnect', 'connect_error', 'reconnect', 'reconnecting', 'error'];\n if (internal.includes(event as string)) {\n return target.on(event as any, handler as any);\n }\n secureOn(target, event, handler);\n return socket; // for chaining\n };\n }\n const val = (target as any)[prop];\n return typeof val === 'function' ? val.bind(target) : val;\n },\n });\n}\n\nexport function disconnectSocket() {\n if (_socket) {\n _socket.disconnect();\n _socket = null;\n setStatus('disconnected');\n }\n clearTransitSession();\n resetAlgoCache();\n _getToken = null;\n _userId = undefined;\n _tenantId = undefined;\n}\n\nexport function reconnectSocket(token: string, userId?: string, tenantId?: string) {\n if (_socket) {\n _socket.auth = {\n token: `Bearer ${token}`,\n ...(userId && { userId }),\n ...(tenantId && { tenantId }),\n };\n _socket.connect();\n }\n}\n\n/**\n * Updates the socket auth token from the stored getToken function and reconnects.\n * Called by SocketProvider on connect_error to handle expired-token scenarios.\n * Returns true if the token was updated, false if no token source is available.\n */\nexport function refreshSocketAuth(): boolean {\n if (!_socket || !_getToken) return false;\n const fresh = _getToken();\n if (!fresh) return false;\n _socket.auth = {\n token: `Bearer ${fresh}`,\n ...(_userId && { userId: _userId }),\n ...(_tenantId && { tenantId: _tenantId }),\n };\n return true;\n}\n","export type TransitAlgo = 'x25519' | 'p256';\n\nlet _cached: TransitAlgo | null = null;\n\n// Probes Web Crypto API for X25519 support once; caches result for the session.\n// RN and Node callers never call this — they always get X25519 via @noble.\nexport async function detectTransitAlgo(): Promise<TransitAlgo> {\n if (_cached) return _cached;\n\n try {\n await globalThis.crypto.subtle.generateKey(\n { name: 'X25519' } as any,\n false,\n ['deriveKey'],\n );\n _cached = 'x25519';\n } catch {\n _cached = 'p256';\n }\n\n return _cached;\n}\n\nexport function getCachedAlgo(): TransitAlgo | null {\n return _cached;\n}\n\nexport function resetAlgoCache(): void {\n _cached = null;\n}\n","import type { TransitAlgo } from './detect.js';\n\nexport interface ServerPublicKeys {\n x25519: string; // base64\n p256: string; // base64\n enabled: boolean;\n}\n\n// Fetches server public keys + enabled flag. Called once per init.\n// Unwraps the server's standard { success, data } envelope if present.\nexport async function fetchServerKeys(apiUrl: string): Promise<ServerPublicKeys> {\n const res = await fetch(`${apiUrl}/crypto/pubkey`);\n if (!res.ok) throw new Error(`[AntzChat] Failed to fetch server public key: ${res.status}`);\n const body = await res.json() as any;\n // Server wraps all responses in { success, data } via TransformInterceptor\n return (body?.data ?? body) as ServerPublicKeys;\n}\n\n// Performs the client side of the ECDH handshake:\n// 1. Generates an ephemeral key pair\n// 2. Injects the public key into socketHandshakeAuth (read by io() on connect)\n// 3. Returns a bound deriveSessionKey function that uses this handshake's\n// private key — no shared module state, safe for concurrent/re-entrant calls.\nexport async function performHandshake(\n algo: TransitAlgo,\n serverKeys: ServerPublicKeys,\n socketHandshakeAuth: Record<string, unknown>,\n): Promise<(sessionId: string) => Promise<CryptoKey>> {\n const ephemeral = await globalThis.crypto.subtle.generateKey(\n algo === 'x25519'\n ? { name: 'X25519' }\n : { name: 'ECDH', namedCurve: 'P-256' } as any,\n false,\n ['deriveBits'],\n );\n\n const pubRaw = await globalThis.crypto.subtle.exportKey('raw', (ephemeral as CryptoKeyPair).publicKey);\n\n socketHandshakeAuth['transitEphemeralPub'] = bufToB64(pubRaw);\n socketHandshakeAuth['transitAlgo'] = algo;\n\n const ephemeralPriv = (ephemeral as CryptoKeyPair).privateKey;\n\n // Return a closure — private key and server keys are captured here, not in module state.\n return (sessionId: string) => deriveSessionKey(ephemeralPriv, algo, serverKeys, sessionId);\n}\n\nasync function deriveSessionKey(\n ephemeralPriv: CryptoKey,\n algo: TransitAlgo,\n serverKeys: ServerPublicKeys,\n sessionId: string,\n): Promise<CryptoKey> {\n const serverPubB64 = algo === 'x25519' ? serverKeys.x25519 : serverKeys.p256;\n const serverPubRaw = b64ToBuf(serverPubB64);\n\n const keyAlgoParams = algo === 'x25519'\n ? { name: 'X25519' }\n : { name: 'ECDH', namedCurve: 'P-256' };\n\n const serverPubKey = await globalThis.crypto.subtle.importKey(\n 'raw',\n serverPubRaw,\n keyAlgoParams as any,\n false,\n [],\n );\n\n const sharedBits = await globalThis.crypto.subtle.deriveBits(\n { name: algo === 'x25519' ? 'X25519' : 'ECDH', public: serverPubKey } as any,\n ephemeralPriv,\n 256,\n );\n\n const hkdfKey = await globalThis.crypto.subtle.importKey(\n 'raw',\n sharedBits,\n 'HKDF',\n false,\n ['deriveKey'],\n );\n\n const salt = new TextEncoder().encode(sessionId);\n const info = new TextEncoder().encode('antz-transit-v1');\n\n return globalThis.crypto.subtle.deriveKey(\n { name: 'HKDF', hash: 'SHA-256', salt, info },\n hkdfKey,\n { name: 'AES-GCM', length: 256 },\n false,\n ['encrypt', 'decrypt'],\n );\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction bufToB64(buf: ArrayBuffer): string {\n const bytes = new Uint8Array(buf);\n let str = '';\n bytes.forEach(b => { str += String.fromCharCode(b); });\n return btoa(str);\n}\n\nfunction b64ToBuf(b64: string): ArrayBuffer {\n const bin = atob(b64);\n const buf = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) buf[i] = bin.charCodeAt(i);\n return buf.buffer;\n}\n","import type { SendMessagePayload } from '../types/index.js';\nimport { getSocket, tryGetSocket, onSocketStatus, secureEmit } from './socket.js';\n\nconst ACK_TIMEOUT = 5000;\nconst RECONNECT_WAIT_TIMEOUT = 15000;\n\nfunction waitForReconnect(): Promise<void> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n unsubscribe();\n reject(new Error('[AntzChat] Socket reconnect timeout'));\n }, RECONNECT_WAIT_TIMEOUT);\n\n const unsubscribe = onSocketStatus((status) => {\n if (status === 'connected') {\n clearTimeout(timer);\n unsubscribe();\n resolve();\n } else if (status === 'error') {\n clearTimeout(timer);\n unsubscribe();\n reject(new Error('[AntzChat] Socket reconnect failed'));\n }\n });\n });\n}\n\n// withAck: for operations that need a response (send message, reactions, etc.)\n// Encrypts payload and decrypts ack response transparently via secureEmit.\nasync function withAck<T>(event: string, payload: unknown): Promise<T> {\n let socket = tryGetSocket();\n if (!socket) {\n await waitForReconnect();\n socket = tryGetSocket();\n }\n if (!socket) return Promise.reject(new Error(`[AntzChat] Socket not connected (event: ${event})`));\n\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => reject(new Error(`Socket ack timeout: ${event}`)), ACK_TIMEOUT);\n secureEmit(socket!, event, payload, (response) => {\n clearTimeout(timer);\n resolve(response as T);\n });\n });\n}\n\n// fireAndForget: best-effort — silently no-ops when not connected.\nfunction fireAndForget(event: string, payload: unknown): void {\n const socket = tryGetSocket();\n if (!socket) return;\n secureEmit(socket, event, payload);\n}\n\nexport const socketEmit = {\n joinRoom(conversationId: string) {\n fireAndForget('join_room', { conversationId });\n },\n\n leaveRoom(conversationId: string) {\n fireAndForget('leave_room', { conversationId });\n },\n\n sendMessage(payload: SendMessagePayload): Promise<unknown> {\n return withAck('send_message', payload);\n },\n\n updateMessage(messageId: string, text: string): Promise<unknown> {\n return withAck('update_message', { messageId, text });\n },\n\n deleteMessage(messageId: string): Promise<unknown> {\n return withAck('delete_message', { messageId });\n },\n\n deleteMessageForMe(messageId: string): Promise<unknown> {\n return withAck('delete_message_for_me', { messageId });\n },\n\n addReaction(messageId: string, emoji: string): Promise<unknown> {\n return withAck('add_reaction', { messageId, emoji });\n },\n\n removeReaction(messageId: string, emoji: string): Promise<unknown> {\n return withAck('remove_reaction', { messageId, emoji });\n },\n\n pinMessage(messageId: string): Promise<unknown> {\n return withAck('pin_message', { messageId });\n },\n\n unpinMessage(messageId: string): Promise<unknown> {\n return withAck('unpin_message', { messageId });\n },\n\n // markRead and typing are best-effort — silently dropped if socket not ready\n typing(conversationId: string, isTyping: boolean) {\n fireAndForget('typing', { conversationId, isTyping });\n },\n\n markRead(conversationId: string, messageId?: string) {\n fireAndForget('mark_read', { conversationId, ...(messageId ? { messageId } : {}) });\n },\n\n getOnlineUsers(userIds: string[]): Promise<string[]> {\n const socket = tryGetSocket();\n if (!socket) return Promise.resolve([]);\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => reject(new Error('get_online_users timeout')), ACK_TIMEOUT);\n secureEmit(socket, 'get_online_users', { userIds }, (response) => {\n clearTimeout(timer);\n if (response && typeof response === 'object' && 'onlineStatus' in (response as object)) {\n const status = (response as { onlineStatus: Record<string, boolean> }).onlineStatus;\n resolve(Object.entries(status).filter(([, v]) => v).map(([k]) => k));\n } else if (Array.isArray(response)) {\n resolve(response as string[]);\n } else {\n resolve([]);\n }\n });\n });\n },\n\n getTypingUsers(conversationId: string): Promise<unknown> {\n return withAck('get_typing_users', { conversationId });\n },\n};\n","import { create } from 'zustand';\nimport { persist } from 'zustand/middleware';\nimport type { AuthTokens, User } from '../types/index.js';\nimport type { PersistStorage } from '../config/types.js';\n\ninterface AuthState {\n user: User | null;\n tokens: AuthTokens | null;\n isAuthenticated: boolean;\n isLoading: boolean;\n isHydrated: boolean;\n\n setAuth: (user: User, tokens: AuthTokens) => void;\n setTokens: (tokens: AuthTokens) => void;\n setUser: (user: User) => void;\n logout: () => void;\n setLoading: (loading: boolean) => void;\n setHydrated: (hydrated: boolean) => void;\n}\n\n/**\n * Creates a platform-specific auth store.\n * Call once during SDK init with the platform's PersistStorage adapter.\n * - Web: pass a localStorage adapter\n * - RN: pass an AsyncStorage adapter\n */\nexport function createAuthStore(storage: PersistStorage) {\n if (!storage) throw new Error('[AntzChat] createAuthStore requires a valid PersistStorage — received undefined. Make sure the SDK config is fully resolved before initializing the store.');\n // Use a ref object so the onRehydrateStorage closure can call store.setState\n // without hitting a temporal dead zone. On web, localStorage is synchronous\n // and onRehydrateStorage fires during create() before `store` is assigned.\n const ref: { store: any } = { store: null };\n\n const store = create<AuthState>()(\n persist(\n (set) => ({\n user: null,\n tokens: null,\n isAuthenticated: false,\n isLoading: false,\n isHydrated: false,\n\n setAuth: (user, tokens) =>\n set({ user, tokens, isAuthenticated: true, isLoading: false }),\n\n setTokens: (tokens) => set({ tokens }),\n\n setUser: (user) => set({ user }),\n\n logout: () =>\n set({ user: null, tokens: null, isAuthenticated: false, isLoading: false }),\n\n setLoading: (isLoading) => set({ isLoading }),\n\n setHydrated: (isHydrated) => set({ isHydrated }),\n }),\n {\n name: 'antz-chat-auth',\n storage: {\n getItem: (name) => {\n const result = storage.getItem(name);\n if (result instanceof Promise) {\n return result.then((str) => (str ? JSON.parse(str) : null));\n }\n return result ? JSON.parse(result) : null;\n },\n setItem: (name, value) => {\n storage.setItem(name, JSON.stringify(value));\n },\n removeItem: (name) => storage.removeItem(name),\n },\n partialize: (state) => ({\n user: state.user,\n tokens: state.tokens,\n isAuthenticated: state.isAuthenticated,\n }) as AuthState,\n onRehydrateStorage: () => (state, error) => {\n if (error) {\n console.warn('[AntzChat] Auth store rehydration failed:', error);\n }\n // Access via ref — safe even when called synchronously during create()\n ref.store?.setState({ isHydrated: true });\n },\n },\n ),\n );\n\n // Assign ref immediately after create() completes\n ref.store = store;\n\n // If already hydrated synchronously (web/localStorage rehydrated during create),\n // set the flag now since the closure above may have called ref.store when it was null.\n if (!store.getState().isHydrated) {\n // Fallback: async storage (RN) — force hydrated after 3s max\n const hydrationTimeout = setTimeout(() => {\n if (!store.getState().isHydrated) {\n store.setState({ isHydrated: true });\n }\n }, 3000);\n\n // Clean up if it resolves naturally before timeout\n const unsub = store.subscribe((s) => {\n if (s.isHydrated) {\n clearTimeout(hydrationTimeout);\n unsub();\n }\n });\n }\n\n const tokenStore = {\n getAccessToken: () => store.getState().tokens?.accessToken,\n getRefreshToken: () => store.getState().tokens?.refreshToken,\n setTokens: (tokens: AuthTokens) => store.getState().setTokens(tokens),\n clearTokens: () => store.getState().logout(),\n };\n\n return { useAuthStore: store, authTokenStore: tokenStore };\n}\n\n// ─── Singleton instances (set during SDK init) ────────────────────────────────\n// Each SDK (web / RN) calls createAuthStore once and exports these.\n// Direct import of these will throw until initAuthStore is called.\n\nlet _authStore: ReturnType<typeof createAuthStore> | null = null;\n\n/**\n * Initialize the auth store singleton.\n * Idempotent — subsequent calls with the same storage return the existing instance.\n * This ensures tokens persisted in localStorage/AsyncStorage survive re-renders\n * and React StrictMode double-invocations.\n */\nexport function initAuthStore(storage: PersistStorage) {\n if (!_authStore) {\n _authStore = createAuthStore(storage);\n }\n return _authStore;\n}\n\nexport function getAuthStore() {\n if (!_authStore) throw new Error('[AntzChat] Auth store not initialized. Call initAuthStore first.');\n return _authStore;\n}\n\n/** Reset the singleton — only for tests / SDK teardown. */\nexport function resetAuthStore() {\n _authStore = null;\n}\n","import type { AntzChatConfig, PlatformUploadFn } from './config/types.js';\nimport { resolveConfig } from './config/types.js';\nimport { initApiClient, setApiClientInstance } from './api/client.js';\nimport { initAuthStore } from './stores/auth.store.js';\nimport { authApi } from './api/auth.js';\nimport { messagesApi } from './api/messages.js';\nimport { conversationsApi } from './api/conversations.js';\nimport { storageApi, uploadBatch } from './api/storage.js';\nimport { usersApi } from './api/users.js';\nimport { connectSocket, disconnectSocket, getSocket } from './socket/socket.js';\nimport { socketEmit } from './socket/emitters.js';\nimport type { UploadableFile } from './types/index.js';\n\ninterface ClientSocketHandle {\n emit: typeof socketEmit;\n on(event: string, handler: (...args: unknown[]) => void): void;\n off(event: string, handler: (...args: unknown[]) => void): void;\n}\n\n/**\n * Headless client for non-React consumers or direct programmatic use.\n * Usage:\n * const client = new AntzChatClient({ apiUrl, authToken, platformUploadFn, persistStorage })\n * await client.connect()\n */\nexport class AntzChatClient {\n private _config;\n private _authStore;\n\n readonly auth = authApi;\n readonly messages = messagesApi;\n readonly conversations = conversationsApi;\n readonly storage = storageApi;\n readonly users = usersApi;\n readonly socket: ClientSocketHandle = {\n emit: socketEmit,\n on: (event, handler) => getSocket().on(event, handler as (...args: any[]) => void),\n off: (event, handler) => getSocket().off(event, handler as (...args: any[]) => void),\n };\n\n constructor(rawConfig: AntzChatConfig) {\n this._config = resolveConfig(rawConfig);\n this._authStore = initAuthStore(rawConfig.persistStorage);\n\n if (rawConfig.authToken) {\n this._authStore.authTokenStore.setTokens({\n accessToken: rawConfig.authToken,\n refreshToken: '',\n tokenType: 'Bearer',\n expiresIn: 0,\n });\n }\n\n const instance = initApiClient(this._config, this._authStore.authTokenStore);\n setApiClientInstance(instance);\n }\n\n async connect(): Promise<void> {\n await connectSocket(this._config, () => this._authStore.authTokenStore.getAccessToken());\n }\n\n disconnect(): void {\n disconnectSocket();\n }\n\n uploadFiles(files: UploadableFile[], conversationId?: string) {\n return uploadBatch(\n files,\n this._config.platformUploadFn,\n conversationId,\n this._config.upload.onProgress,\n this._config.platformCompressFn,\n this._config.compression,\n );\n }\n\n async uploadIcon(conversationId: string, file: UploadableFile) {\n const result = await this.uploadFiles([file], conversationId);\n const fileId = result.successful[0]?.id;\n if (!fileId) throw new Error('Icon upload failed');\n return conversationsApi.uploadIcon(conversationId, fileId);\n }\n}\n"],"mappings":"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","names":["axios","bufToB64","b64ToBuf","useChatStore","import_zustand"]}
|
|
1
|
+
{"version":3,"sources":["../src/stores/chat.store.ts","../src/index.ts","../src/config/types.ts","../src/compression/compress.ts","../src/api/client.ts","../src/crypto/transit.ts","../src/crypto/session.ts","../src/api/auth.ts","../src/api/app-config.ts","../src/api/messages.ts","../src/api/conversations.ts","../src/api/storage.ts","../src/api/devices.ts","../src/api/users.ts","../src/socket/socket.ts","../src/crypto/detect.ts","../src/crypto/handshake.ts","../src/socket/emitters.ts","../src/stores/auth.store.ts","../src/client-facade.ts"],"sourcesContent":["import { create } from 'zustand';\nimport type { Message } from '../types/index.js';\n\ninterface TypingUser {\n userId: string;\n displayName: string;\n avatarUrl?: string;\n}\n\nexport interface LastReadEntry {\n messageId: string;\n readAt: string;\n}\n\ninterface ChatState {\n activeConversationId: string | null;\n pendingTarget: { conversationId: string; messageId: string } | null;\n typingUsers: Record<string, TypingUser[]>;\n onlineUsers: string[];\n /** keyed by conversationId — current user's last read pointer per conversation */\n lastRead: Record<string, LastReadEntry>;\n /** keyed by userId — last seen timestamp for each user */\n lastSeen: Record<string, string>;\n replyingTo: Message | null;\n editingMessage: Message | null;\n isSidebarOpen: boolean;\n isGroupInfoOpen: boolean;\n isStarredPanelOpen: boolean;\n\n setActiveConversation: (id: string | null) => void;\n setPendingTarget: (target: { conversationId: string; messageId: string } | null) => void;\n addTypingUser: (conversationId: string, user: TypingUser) => void;\n removeTypingUser: (conversationId: string, userId: string) => void;\n setUserOnline: (userId: string) => void;\n setUserOffline: (userId: string) => void;\n setOnlineUsers: (userIds: string[]) => void;\n setLastRead: (conversationId: string, messageId: string, readAt: string) => void;\n setLastSeen: (userId: string, lastSeenAt: string) => void;\n setReplyingTo: (message: Message | null) => void;\n setEditingMessage: (message: Message | null) => void;\n toggleSidebar: () => void;\n setSidebarOpen: (open: boolean) => void;\n toggleGroupInfo: () => void;\n setGroupInfoOpen: (open: boolean) => void;\n toggleStarredPanel: () => void;\n setStarredPanelOpen: (open: boolean) => void;\n}\n\nexport const useChatStore = create<ChatState>((set) => ({\n activeConversationId: null,\n pendingTarget: null,\n typingUsers: {},\n onlineUsers: [],\n lastRead: {},\n lastSeen: {},\n replyingTo: null,\n editingMessage: null,\n isSidebarOpen: true,\n isGroupInfoOpen: false,\n isStarredPanelOpen: false,\n\n setActiveConversation: (id) =>\n set({ activeConversationId: id, replyingTo: null, editingMessage: null }),\n\n setPendingTarget: (target) => set({ pendingTarget: target }),\n\n addTypingUser: (conversationId, user) =>\n set((state) => {\n const existing = state.typingUsers[conversationId] ?? [];\n const deduped = existing.filter((u) => u.userId !== user.userId);\n return { typingUsers: { ...state.typingUsers, [conversationId]: [...deduped, user] } };\n }),\n\n removeTypingUser: (conversationId, userId) =>\n set((state) => ({\n typingUsers: {\n ...state.typingUsers,\n [conversationId]: (state.typingUsers[conversationId] ?? []).filter(\n (u) => u.userId !== userId,\n ),\n },\n })),\n\n setUserOnline: (userId) =>\n set((state) => ({\n onlineUsers: state.onlineUsers.includes(userId)\n ? state.onlineUsers\n : [...state.onlineUsers, userId],\n })),\n\n setUserOffline: (userId) =>\n set((state) => ({ onlineUsers: state.onlineUsers.filter((id) => id !== userId) })),\n\n setOnlineUsers: (userIds) => set({ onlineUsers: userIds }),\n\n setLastRead: (conversationId, messageId, readAt) =>\n set((state) => ({\n lastRead: { ...state.lastRead, [conversationId]: { messageId, readAt } },\n })),\n\n setLastSeen: (userId, lastSeenAt) =>\n set((state) => ({\n lastSeen: { ...state.lastSeen, [userId]: lastSeenAt },\n })),\n\n setReplyingTo: (message) => set({ replyingTo: message, editingMessage: null }),\n\n setEditingMessage: (message) => set({ editingMessage: message, replyingTo: null }),\n\n toggleSidebar: () => set((state) => ({ isSidebarOpen: !state.isSidebarOpen })),\n setSidebarOpen: (open) => set({ isSidebarOpen: open }),\n\n toggleGroupInfo: () => set((state) => ({ isGroupInfoOpen: !state.isGroupInfoOpen })),\n setGroupInfoOpen: (open) => set({ isGroupInfoOpen: open }),\n\n toggleStarredPanel: () => set((state) => ({ isStarredPanelOpen: !state.isStarredPanelOpen })),\n setStarredPanelOpen: (open) => set({ isStarredPanelOpen: open }),\n}));\n","// Config\nexport type {\n AntzChatConfig,\n ResolvedConfig,\n UploadConfig,\n FileSizeLimits,\n ResolvedFileSizeLimits,\n PlatformUploadFn,\n PlatformCompressFn,\n CompressionConfig,\n ResolvedCompressionConfig,\n PersistStorage,\n} from './config/types.js';\nexport { resolveConfig } from './config/types.js';\n\n// Compression\nexport type { CompressionAlgorithm, CompressedFile } from './types/index.js';\nexport { getCompressionStrategy } from './compression/compress.js';\n\n// Types\nexport type {\n UploadableFile,\n User,\n AuthTokens,\n AuthResponse,\n LoginCredentials,\n RegisterData,\n Message,\n MessageContent,\n MessageReplyReference,\n ReplyAttachmentSnapshot,\n MessageReaction,\n Conversation,\n ConversationListParams,\n ConversationUnreadCount,\n UnreadSummary,\n Participant,\n Attachment,\n FileType,\n FileResponse,\n PresignedUrlRequest,\n PresignedUrlResponse,\n BatchUploadResult,\n UploadProgress,\n PaginatedResponse,\n CursorPaginatedResponse,\n SendMessagePayload,\n SendMessageAttachment,\n OptimisticAttachment,\n NewMessageEvent,\n MessageUpdatedEvent,\n MessageDeletedEvent,\n MessageDeletedForMeEvent,\n ReactionUpdatedEvent,\n TypingIndicatorEvent,\n UserStatusEvent,\n ReadReceiptEvent,\n MessageAckEvent,\n MessageDeliveredEvent,\n MessagesDeliveredEvent,\n} from './types/index.js';\n\n// API\nexport { authApi, messagesApi, conversationsApi, storageApi, uploadBatch, normalizeConversation, devicesApi, usersApi, appConfigApi } from './api/index.js';\nexport type { RegisterDeviceTokenPayload, MobileDeviceToken, WebPushDeviceToken } from './api/index.js';\nexport type { UpdateProfilePayload } from './api/index.js';\nexport type { CreateGroupData, CreateDirectData, UpdateConversationData, ListMessagesParams, SearchParams, SendData } from './api/index.js';\nexport type { UserPreferences, QuietHours } from './types/index.js';\nexport type { AppConfig } from './types/index.js';\nexport { initApiClient, setApiClientInstance, getApiClient } from './api/client.js';\nexport type { TokenStore } from './api/client.js';\n\n// Socket\nexport { connectSocket, disconnectSocket, reconnectSocket, refreshSocketAuth, getSocket, tryGetSocket, getSocketStatus, onSocketStatus } from './socket/socket.js';\nexport type { SocketStatus, StatusListener } from './socket/socket.js';\nexport { socketEmit } from './socket/emitters.js';\n\n// Transit crypto — internal session setter exposed for platform adapters (RN noble)\nexport { setTransitSession } from './crypto/session.js';\n\n// Stores\nexport { initAuthStore, getAuthStore, createAuthStore, resetAuthStore } from './stores/auth.store.js';\nexport { useChatStore } from './stores/chat.store.js';\nexport type { LastReadEntry } from './stores/chat.store.js';\n\n// Headless client\nexport { AntzChatClient } from './client-facade.js';\n","import type { FileType, UploadableFile, PresignedUrlResponse, CompressedFile } from '../types/index.js';\n\nexport interface FileSizeLimits {\n image?: number;\n video?: number;\n audio?: number;\n document?: number;\n default?: number;\n}\n\n/**\n * Platform-provided function that performs the actual binary upload to a presigned URL.\n * - Web implementation: XMLHttpRequest with progress events\n * - RN implementation: fetch with FormData or direct body\n *\n * The core calls this — it never does the upload itself.\n */\nexport type PlatformUploadFn = (\n presigned: PresignedUrlResponse,\n file: UploadableFile,\n onProgress?: (pct: number) => void,\n) => Promise<void>;\n\n/**\n * Platform-provided persistent key-value storage for auth token persistence.\n * - Web: localStorage adapter\n * - RN: AsyncStorage adapter\n */\nexport interface PersistStorage {\n getItem(key: string): string | null | Promise<string | null>;\n setItem(key: string, value: string): void | Promise<void>;\n removeItem(key: string): void | Promise<void>;\n}\n\n/**\n * Platform-provided compression function. Optional — if omitted, files are uploaded as-is.\n * - Web: uses canvas (images) + CompressionStream (text/docs) — both browser-native\n * - RN: uses expo-image-manipulator (images) + pako (text/docs)\n * - Node: uses sharp (images) + zlib (text/docs)\n */\nexport type PlatformCompressFn = (\n file: UploadableFile,\n options: ResolvedCompressionConfig,\n) => Promise<CompressedFile>;\n\nexport interface CompressionConfig {\n /** Master switch. Default: true when platformCompressFn is provided, false otherwise */\n enabled?: boolean;\n /** WebP quality for images, 0–1. Default: 0.85 */\n imageQuality?: number;\n /** Longest side cap in px before encoding. Default: 1920 */\n imageMaxDimension?: number;\n /** Gzip text-based documents (plain, csv, json, xml, yaml, svg). Default: true */\n compressDocuments?: boolean;\n}\n\nexport interface ResolvedCompressionConfig {\n enabled: boolean;\n imageQuality: number;\n imageMaxDimension: number;\n compressDocuments: boolean;\n}\n\nexport interface UploadConfig {\n /**\n * Per-type file size limits in MB. Can also pass a single number for all types.\n * Defaults: image 5MB, video 25MB, audio 10MB, document 10MB.\n */\n maxFileSizeMB?: number | FileSizeLimits;\n\n /** Max files per message. Default: 10 */\n maxFilesPerMessage?: number;\n\n /** Which attachment types users can send. Default: all */\n allowedTypes?: Array<FileType>;\n\n /** Called when a file fails validation or upload */\n onUploadError?: (file: UploadableFile, error: Error) => void;\n\n /** Called with 0–100 aggregate progress during a batch upload */\n onProgress?: (progress: number) => void;\n}\n\nexport interface AntzChatConfig {\n /** REST API base URL — e.g. \"https://api.yourapp.com/api/v1\" */\n apiUrl: string;\n\n /**\n * WebSocket server URL. Defaults to apiUrl with /api/vN path stripped.\n * SDK connects to {socketUrl}/chat\n */\n socketUrl?: string;\n\n /** Static JWT. Use this OR authProvider, not both. */\n authToken?: string;\n\n /**\n * Dynamic token getter — called before requests and on socket reconnect.\n * Preferred when the host app manages its own auth lifecycle.\n */\n authProvider?: () => Promise<string>;\n\n /**\n * External user ID — required for non-builtin modes (antz / external / wso2).\n * Sent as x-user-id header on every request so the chat server can forward it\n * to the user-service for token validation.\n * Not needed for builtin mode (chat server issues its own JWTs).\n */\n userId?: string;\n\n /**\n * Profile picture for non-builtin modes.\n * Server fetches/decodes, hashes for dedup, uploads to chat storage on first use.\n * Only sent once at init — not re-sent on every request.\n * Client never needs to compute hashes; the server owns all dedup logic.\n */\n avatar?: {\n /** Full URL the server can fetch (preferred — Antz/external/wso2 clients know their own file URLs) */\n url?: string;\n /** Raw base64 string (with or without data:... prefix) as fallback */\n base64?: string;\n };\n\n /** Required for multi-tenant backends. Sent as X-Tenant-ID header. */\n tenantId?: string;\n\n /**\n * Enable payload-level transit encryption (ECDH key exchange + AES-256-GCM).\n * Encrypts every HTTP request/response body and every socket event payload.\n * Server must have TRANSIT_ENCRYPTION_ENABLED=true to match.\n * Default: true — set false only for local development/debugging.\n * Safe to toggle anytime — no data migration needed (wire-only, never affects storage).\n */\n transitEncryption?: boolean;\n\n upload?: UploadConfig;\n\n /**\n * Optional compression config. Compression is disabled if platformCompressFn is not provided.\n */\n compression?: CompressionConfig;\n\n /**\n * Platform-specific compression implementation. Optional — omit to disable compression.\n * Each SDK (web, RN) provides its own default; Node.js users wire in their own.\n */\n platformCompressFn?: PlatformCompressFn;\n\n /**\n * Number of messages fetched per page when loading chat history.\n * Default: 40\n */\n messagePageSize?: number;\n\n /**\n * Number of starred messages fetched per page.\n * Default: 30\n */\n starredMessagePageSize?: number;\n\n /**\n * Number of search results fetched per request.\n * Default: 50\n */\n searchPageSize?: number;\n\n /**\n * Platform-specific binary upload implementation.\n * Required — each SDK (web, RN) provides its own.\n */\n platformUploadFn: PlatformUploadFn;\n\n /**\n * Platform-specific persistent storage for auth tokens.\n * Required — each SDK provides its own (localStorage / AsyncStorage).\n */\n persistStorage: PersistStorage;\n}\n\n// ─── Resolved (all defaults filled) ─────────────────────────────────────────\n\nexport interface ResolvedFileSizeLimits {\n image: number;\n video: number;\n audio: number;\n document: number;\n default: number;\n}\n\nexport interface ResolvedConfig {\n apiUrl: string;\n socketUrl: string;\n socketOrigin: string;\n socketPath: string;\n authToken?: string;\n authProvider?: () => Promise<string>;\n userId?: string;\n tenantId?: string;\n avatar?: { url?: string; base64?: string };\n transitEncryption: boolean;\n upload: {\n maxFileSizeMB: ResolvedFileSizeLimits;\n maxFilesPerMessage: number;\n allowedTypes: FileType[];\n onUploadError?: (file: UploadableFile, error: Error) => void;\n onProgress?: (progress: number) => void;\n };\n platformUploadFn: PlatformUploadFn;\n platformCompressFn?: PlatformCompressFn;\n compression: ResolvedCompressionConfig;\n persistStorage: PersistStorage;\n messagePageSize: number;\n starredMessagePageSize: number;\n searchPageSize: number;\n}\n\nexport function resolveConfig(config: AntzChatConfig): ResolvedConfig {\n const socketUrl =\n config.socketUrl ??\n config.apiUrl.replace(/\\/api\\/v\\d+\\/?$/, '').replace(/\\/$/, '');\n\n // Auto-derive Socket.IO engine path from the socketUrl pathname.\n // If the server is behind a reverse proxy with a path prefix (e.g. /chat-api),\n // that prefix is already embedded in socketUrl. We append /socket.io to it.\n // Examples:\n // socketUrl = https://host/chat-api → socketPath = /chat-api/socket.io\n // socketUrl = https://host → socketPath = /socket.io\n // No env var or manual config needed — SERVER_URL on the server and apiUrl\n // on the client carry the same prefix, so both sides derive the same path.\n // Derive Socket.IO engine path and origin separately.\n // Socket.IO uses the URL passed to io() as the namespace origin — anything\n // in the pathname of that URL becomes part of the namespace, not the engine path.\n // So we must pass only the bare origin (https://host) to io(), and use the\n // path option for the engine mount point.\n //\n // socketUrl = https://host/chat-api\n // → socketOrigin = https://host (passed to io())\n // → socketPath = /chat-api/socket.io (engine path option)\n // → namespace = /chat (hardcoded in connectSocket)\n //\n // socketUrl = https://host\n // → socketOrigin = https://host\n // → socketPath = /socket.io\n let socketOrigin = socketUrl;\n let socketPath = '/socket.io';\n try {\n const parsed = new URL(socketUrl);\n socketOrigin = parsed.origin; // strips pathname, search, hash\n const pathname = parsed.pathname.replace(/\\/$/, '');\n if (pathname && pathname !== '/') socketPath = `${pathname}/socket.io`;\n } catch {\n // invalid URL — fall back to defaults\n }\n\n const raw = config.upload?.maxFileSizeMB;\n let limits: ResolvedFileSizeLimits;\n\n if (typeof raw === 'number') {\n limits = { image: raw, video: raw, audio: raw, document: raw, default: raw };\n } else {\n const fallback = raw?.default ?? 25;\n limits = {\n image: raw?.image ?? 5,\n video: raw?.video ?? 25,\n audio: raw?.audio ?? 10,\n document: raw?.document ?? 10,\n default: fallback,\n };\n }\n\n return {\n apiUrl: config.apiUrl.replace(/\\/$/, ''),\n socketUrl,\n socketOrigin,\n socketPath,\n authToken: config.authToken,\n authProvider: config.authProvider,\n userId: config.userId,\n tenantId: config.tenantId,\n avatar: config.avatar,\n transitEncryption: config.transitEncryption ?? true,\n upload: {\n maxFileSizeMB: limits,\n maxFilesPerMessage: config.upload?.maxFilesPerMessage ?? 10,\n allowedTypes: config.upload?.allowedTypes ?? ['image', 'video', 'audio', 'document'],\n onUploadError: config.upload?.onUploadError,\n onProgress: config.upload?.onProgress,\n },\n platformUploadFn: config.platformUploadFn,\n platformCompressFn: config.platformCompressFn,\n compression: {\n enabled: config.compression?.enabled ?? (config.platformCompressFn != null),\n imageQuality: config.compression?.imageQuality ?? 0.85,\n imageMaxDimension: config.compression?.imageMaxDimension ?? 1920,\n compressDocuments: config.compression?.compressDocuments ?? true,\n },\n persistStorage: config.persistStorage,\n messagePageSize: config.messagePageSize ?? 40,\n starredMessagePageSize: config.starredMessagePageSize ?? 30,\n searchPageSize: config.searchPageSize ?? 50,\n };\n}\n","import type { UploadableFile, CompressedFile, CompressionAlgorithm } from '../types/index.js';\nimport type { PlatformCompressFn, ResolvedCompressionConfig } from '../config/types.js';\n\n// MIME types that benefit from gzip (text-based, not already compressed)\nconst GZIP_MIME_TYPES = new Set([\n 'text/plain', 'text/csv', 'text/markdown', 'text/x-markdown',\n 'text/xml', 'application/xml', 'text/yaml', 'text/x-yaml',\n 'application/x-yaml', 'application/rtf', 'text/rtf',\n 'application/json', 'image/svg+xml',\n]);\n\nconst IMAGE_MIME_TYPES = new Set([\n 'image/jpeg', 'image/png', 'image/gif', 'image/webp', 'image/bmp', 'image/tiff',\n]);\n\n// Already-compressed formats — no gain from recompressing\nconst SKIP_MIME_TYPES = new Set([\n 'video/mp4', 'video/webm', 'video/quicktime',\n 'audio/mpeg', 'audio/wav', 'audio/ogg', 'audio/webm', 'audio/mp4',\n 'application/zip', 'application/pdf',\n 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n]);\n\nexport type CompressionStrategy = 'image' | 'gzip' | 'skip';\n\nexport function getCompressionStrategy(\n mimeType: string,\n config: ResolvedCompressionConfig,\n): CompressionStrategy {\n if (SKIP_MIME_TYPES.has(mimeType)) return 'skip';\n if (IMAGE_MIME_TYPES.has(mimeType)) return 'image';\n if (config.compressDocuments && GZIP_MIME_TYPES.has(mimeType)) return 'gzip';\n return 'skip';\n}\n\n/**\n * Attempt to compress a file using the platform-provided compressor.\n * Returns the original file unchanged (as a CompressedFile with compressed=false)\n * if compression is disabled, no compressor is provided, or the strategy is 'skip'.\n */\nexport async function compressFile(\n file: UploadableFile,\n platformCompressFn: PlatformCompressFn | undefined,\n config: ResolvedCompressionConfig,\n): Promise<CompressedFile> {\n const noop: CompressedFile = {\n ...file,\n originalSize: file.size,\n compressed: false,\n compressionAlgorithm: 'none' as CompressionAlgorithm,\n };\n\n if (!config.enabled || !platformCompressFn) return noop;\n\n const strategy = getCompressionStrategy(file.type, config);\n if (strategy === 'skip') return noop;\n\n try {\n return await platformCompressFn(file, config);\n } catch {\n // Compression failure is non-fatal — fall back to original\n return noop;\n }\n}\n","import axios, {\n AxiosInstance,\n InternalAxiosRequestConfig,\n} from 'axios';\nimport type { ResolvedConfig } from '../config/types.js';\nimport type { AuthTokens } from '../types/index.js';\nimport { encryptPayload, decryptPayload, isTransitEnvelope } from '../crypto/transit.js';\nimport { getSessionKey, getSessionId, isTransitEnabled, waitForTransitReady, configureTransit } from '../crypto/session.js';\n\nexport type TokenStore = {\n getAccessToken: () => string | null | undefined;\n getRefreshToken: () => string | null | undefined;\n setTokens: (tokens: AuthTokens) => void;\n clearTokens: () => void;\n};\n\nlet _tokenStore: TokenStore | null = null;\nlet _config: ResolvedConfig | null = null;\n// Avatar headers are sent only on the first authenticated request after init.\n// The server hashes on receive and deduplicates — subsequent requests don't need them.\nlet _avatarSent = false;\n\nexport function initApiClient(config: ResolvedConfig, tokenStore: TokenStore): AxiosInstance {\n _config = config;\n _tokenStore = tokenStore;\n _avatarSent = false; // reset on re-init (new session / authToken change)\n\n const client = axios.create({\n baseURL: config.apiUrl,\n headers: { 'Content-Type': 'application/json' },\n });\n\n // Configure transit as early as possible — before any requests fire —\n // so waitForTransitReady() in the interceptor knows whether to block or not.\n configureTransit(config.transitEncryption);\n\n // ── Request interceptor ──────────────────────────────────────────────────\n client.interceptors.request.use(async (req: InternalAxiosRequestConfig) => {\n const token = _tokenStore?.getAccessToken();\n if (token) req.headers['Authorization'] = `Bearer ${token}`;\n if (_config?.userId) req.headers['x-user-id'] = _config.userId;\n if (_config?.tenantId) req.headers['X-Tenant-ID'] = _config.tenantId;\n // Send avatar on the first request only — server hashes and deduplicates\n if (token && !_avatarSent && _config?.avatar) {\n if (_config.avatar.base64) req.headers['x-avatar-base64'] = _config.avatar.base64;\n else if (_config.avatar.url) req.headers['x-avatar-url'] = _config.avatar.url;\n _avatarSent = true;\n }\n\n // Wait for the transit session key to be established before sending.\n // HTTP requests that fire before connectSocket completes (e.g. getMe, getConfig)\n // are held here until the ECDH handshake finishes. Resolves immediately when\n // transit is disabled or already established.\n await waitForTransitReady();\n\n // Transit encryption — attach session ID on every request so the server\n // can look up the session key. Encrypt body when present.\n if (isTransitEnabled()) {\n const sessionId = getSessionId();\n const key = getSessionKey();\n if (sessionId && key) {\n req.headers['x-transit-session'] = sessionId;\n if (req.data !== undefined && req.data !== null) {\n const envelope = await encryptPayload(req.data, key);\n req.data = envelope;\n req.headers['x-transit-encrypted'] = '1';\n }\n }\n }\n\n return req;\n });\n\n let isRefreshing = false;\n let refreshQueue: Array<(token: string) => void> = [];\n\n // ── Response interceptor ─────────────────────────────────────────────────\n client.interceptors.response.use(\n async (response) => {\n // Transit decryption — server wraps encrypted payload inside { success, data: <envelope> }.\n // Decrypt the inner envelope, then let the standard unwrap below handle { success, data }.\n if (isTransitEnabled()) {\n const key = getSessionKey();\n if (key) {\n // Case 1: entire response is the envelope (unlikely but handle it)\n if (isTransitEnvelope(response.data)) {\n response.data = await decryptPayload(response.data, key);\n }\n // Case 2: envelope is nested inside { success, data: <envelope> }\n else if (response.data?.data && isTransitEnvelope(response.data.data)) {\n response.data.data = await decryptPayload(response.data.data, key);\n }\n }\n }\n\n // Standard { success, data } unwrap\n if (\n response.data &&\n typeof response.data === 'object' &&\n 'success' in response.data &&\n 'data' in response.data\n ) {\n response.data = response.data.data;\n }\n return response;\n },\n async (error) => {\n // Decrypt error response body — error filter encrypts it too\n if (isTransitEnabled() && error.response?.data) {\n const key = getSessionKey();\n if (key) {\n try {\n if (isTransitEnvelope(error.response.data)) {\n error.response.data = await decryptPayload(error.response.data, key);\n } else if (error.response.data?.data && isTransitEnvelope(error.response.data.data)) {\n error.response.data.data = await decryptPayload(error.response.data.data, key);\n }\n } catch { /* decryption failed — leave as-is */ }\n }\n }\n\n const original = error.config as InternalAxiosRequestConfig & { _retry?: boolean };\n\n if (error.response?.status === 401 && !original._retry) {\n const refreshToken = _tokenStore?.getRefreshToken();\n if (!refreshToken) {\n _tokenStore?.clearTokens();\n return Promise.reject(error);\n }\n\n if (isRefreshing) {\n return new Promise((resolve) => {\n refreshQueue.push((newToken) => {\n original.headers['Authorization'] = `Bearer ${newToken}`;\n resolve(client(original));\n });\n });\n }\n\n original._retry = true;\n isRefreshing = true;\n\n try {\n const { data } = await axios.post<{ data: AuthTokens }>(\n `${_config!.apiUrl}/auth/refresh`,\n { refreshToken },\n );\n const tokens: AuthTokens = (data as any).data ?? data;\n _tokenStore?.setTokens(tokens);\n refreshQueue.forEach((cb) => cb(tokens.accessToken));\n refreshQueue = [];\n original.headers['Authorization'] = `Bearer ${tokens.accessToken}`;\n return client(original);\n } catch {\n _tokenStore?.clearTokens();\n return Promise.reject(error);\n } finally {\n isRefreshing = false;\n }\n }\n\n return Promise.reject(error);\n },\n );\n\n return client;\n}\n\nlet _instance: AxiosInstance | null = null;\n\nexport function setApiClientInstance(instance: AxiosInstance) {\n _instance = instance;\n}\n\nexport function getApiClient(): AxiosInstance {\n if (!_instance) throw new Error('[AntzChat] API client not initialized. Call initApiClient first.');\n return _instance;\n}\n","export interface TransitEnvelope {\n v: 1;\n iv: string; // base64, 12 bytes\n tag: string; // base64, 16 bytes\n ct: string; // base64, ciphertext\n}\n\n// ─── Encrypt ─────────────────────────────────────────────────────────────────\n\nexport async function encryptPayload(\n data: unknown,\n sessionKey: CryptoKey,\n): Promise<TransitEnvelope> {\n const iv = globalThis.crypto.getRandomValues(new Uint8Array(12));\n const plaintext = new TextEncoder().encode(JSON.stringify(data));\n\n const encrypted = await globalThis.crypto.subtle.encrypt(\n { name: 'AES-GCM', iv },\n sessionKey,\n plaintext,\n );\n\n // AES-GCM appends the 16-byte auth tag to the ciphertext buffer\n const ct = encrypted.slice(0, encrypted.byteLength - 16);\n const tag = encrypted.slice(encrypted.byteLength - 16);\n\n return {\n v: 1,\n iv: bufToB64(iv),\n tag: bufToB64(tag),\n ct: bufToB64(ct),\n };\n}\n\n// ─── Decrypt ─────────────────────────────────────────────────────────────────\n\nexport async function decryptPayload(\n envelope: TransitEnvelope,\n sessionKey: CryptoKey,\n): Promise<unknown> {\n const iv = b64ToBuf(envelope.iv);\n const tag = b64ToBuf(envelope.tag);\n const ct = b64ToBuf(envelope.ct);\n\n // Reassemble ct + tag as SubtleCrypto expects them concatenated\n const combined = new Uint8Array(ct.byteLength + tag.byteLength);\n combined.set(new Uint8Array(ct), 0);\n combined.set(new Uint8Array(tag), ct.byteLength);\n\n const decrypted = await globalThis.crypto.subtle.decrypt(\n { name: 'AES-GCM', iv: new Uint8Array(iv) },\n sessionKey,\n combined,\n );\n\n return JSON.parse(new TextDecoder().decode(decrypted));\n}\n\nexport function isTransitEnvelope(v: unknown): v is TransitEnvelope {\n return (\n typeof v === 'object' &&\n v !== null &&\n (v as any).v === 1 &&\n typeof (v as any).iv === 'string' &&\n typeof (v as any).tag === 'string' &&\n typeof (v as any).ct === 'string'\n );\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction bufToB64(buf: ArrayBuffer | Uint8Array): string {\n const bytes = buf instanceof Uint8Array ? buf : new Uint8Array(buf);\n let str = '';\n bytes.forEach(b => { str += String.fromCharCode(b); });\n return btoa(str);\n}\n\nfunction b64ToBuf(b64: string): ArrayBuffer {\n const bin = atob(b64);\n const buf = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) buf[i] = bin.charCodeAt(i);\n return buf.buffer;\n}\n","import type { TransitAlgo } from './detect.js';\n\ninterface TransitSession {\n sessionKey: CryptoKey;\n algo: TransitAlgo;\n sessionId: string;\n enabled: boolean;\n}\n\n// All state lives on globalThis so Turbopack <locals> module splits and any\n// other bundler that creates multiple instances of this module still share a\n// single source of truth. The symbol key prevents accidental collisions.\nconst _KEY = Symbol.for('__antz_chat_transit__');\n\ninterface TransitState {\n session: TransitSession | null;\n sessionEverEstablished: boolean;\n readyResolve: (() => void) | null;\n readyPromise: Promise<void> | null;\n transitConfigured: boolean | null;\n}\n\nfunction getState(): TransitState {\n const g = globalThis as any;\n if (!g[_KEY]) {\n g[_KEY] = {\n session: null,\n sessionEverEstablished: false,\n readyResolve: null,\n readyPromise: null,\n transitConfigured: null,\n } satisfies TransitState;\n }\n return g[_KEY] as TransitState;\n}\n\nexport function configureTransit(enabled: boolean): void {\n const s = getState();\n s.transitConfigured = enabled;\n if (!enabled) {\n // Transit disabled — resolve immediately so HTTP requests don't block\n s.readyResolve?.();\n s.readyResolve = null;\n }\n}\n\nexport function waitForTransitReady(): Promise<void> {\n const s = getState();\n // Not configured yet or disabled — resolve immediately\n if (!s.transitConfigured) return Promise.resolve();\n // Already have an active session — resolve immediately\n if (s.session) return Promise.resolve();\n // Session was previously established but is now cleared (socket dropped/reconnecting).\n // The encryption block in the interceptor is guarded by isTransitEnabled() and will\n // skip itself with no session, so there is no point blocking here.\n if (s.sessionEverEstablished) return Promise.resolve();\n // First startup — block until the initial ECDH handshake completes.\n if (!s.readyPromise) {\n s.readyPromise = new Promise<void>((resolve) => {\n s.readyResolve = resolve;\n });\n }\n return s.readyPromise;\n}\n\nexport function setTransitSession(session: TransitSession): void {\n const s = getState();\n s.session = session;\n s.sessionEverEstablished = true;\n // Resolve any pending HTTP requests waiting for the session key\n s.readyResolve?.();\n s.readyResolve = null;\n}\n\nexport function getTransitSession(): TransitSession | null {\n return getState().session;\n}\n\nexport function clearTransitSession(): void {\n const s = getState();\n s.session = null;\n // Reset the ready promise so it can be recreated on next waitForTransitReady call.\n // sessionEverEstablished intentionally left true — HTTP requests fired while\n // reconnecting should not block (isTransitEnabled() guards encryption anyway).\n s.readyPromise = null;\n s.readyResolve = null;\n}\n\nexport function isTransitEnabled(): boolean {\n return getState().session?.enabled === true;\n}\n\nexport function getSessionKey(): CryptoKey | null {\n return getState().session?.sessionKey ?? null;\n}\n\n// Returns sessionId for the x-transit-session header sent with HTTP requests.\nexport function getSessionId(): string | null {\n return getState().session?.sessionId ?? null;\n}\n","import type { AuthResponse, AuthTokens, LoginCredentials, RegisterData, User } from '../types/index.js';\nimport { getApiClient } from './client.js';\n\nexport const authApi = {\n async login(credentials: LoginCredentials): Promise<AuthResponse> {\n const { data } = await getApiClient().post<AuthResponse>('/auth/login', credentials);\n return data;\n },\n\n async register(payload: RegisterData): Promise<AuthResponse> {\n const { data } = await getApiClient().post<AuthResponse>('/auth/register', payload);\n return data;\n },\n\n async refresh(refreshToken: string): Promise<AuthTokens> {\n const { data } = await getApiClient().post<AuthTokens>('/auth/refresh', { refreshToken });\n return data;\n },\n\n async logout(refreshToken?: string): Promise<void> {\n await getApiClient().post('/auth/logout', refreshToken ? { refreshToken } : {});\n },\n\n async logoutAll(): Promise<void> {\n await getApiClient().post('/auth/logout-all');\n },\n\n async getMe(): Promise<User> {\n const { data } = await getApiClient().get<User>('/users/me');\n return data;\n },\n\n // Upload a new avatar file (builtin mode — multipart)\n async uploadAvatar(file: File | Blob, mimeType?: string): Promise<{ avatarUrl: string }> {\n const form = new FormData();\n form.append('avatar', file instanceof File ? file : new File([file], 'avatar.jpg', { type: mimeType ?? 'image/jpeg' }));\n const { data } = await getApiClient().put<{ avatarUrl: string }>('/users/me/avatar', form, {\n headers: { 'Content-Type': 'multipart/form-data' },\n });\n return data;\n },\n\n // Sync avatar from URL or base64 (non-builtin modes — or post-init update)\n async syncAvatar(source: { url?: string; base64?: string }): Promise<{ avatarUrl: string }> {\n const headers: Record<string, string> = {};\n if (source.base64) headers['x-avatar-base64'] = source.base64;\n else if (source.url) headers['x-avatar-url'] = source.url;\n const { data } = await getApiClient().post<{ avatarUrl: string }>('/users/me/avatar/sync', {}, { headers });\n return data;\n },\n};\n","import { getApiClient } from './client.js';\nimport type { AppConfig } from '../types/index.js';\n\nexport const appConfigApi = {\n async get(): Promise<AppConfig> {\n const { data } = await getApiClient().get<AppConfig>('/app/config');\n return data;\n },\n};\n","import type {\n Message,\n CursorPaginatedResponse,\n PaginatedResponse,\n SendMessagePayload,\n} from '../types/index.js';\nimport { getApiClient } from './client.js';\n\nexport interface ListMessagesParams {\n cursor?: string;\n limit?: number;\n direction?: 'before' | 'after';\n}\n\nexport interface SearchParams {\n query: string;\n conversationId?: string;\n page?: number;\n limit?: number;\n}\n\nexport interface SendData {\n text?: string;\n attachments?: SendMessagePayload['attachments'];\n replyTo?: string;\n tempId?: string;\n}\n\nexport const messagesApi = {\n async list(conversationId: string, params: ListMessagesParams = {}): Promise<CursorPaginatedResponse<Message>> {\n const { cursor, direction, ...rest } = params;\n const serverParams: Record<string, unknown> = { ...rest };\n if (cursor) {\n serverParams[direction === 'after' ? 'after' : 'before'] = cursor;\n }\n const { data } = await getApiClient().get<CursorPaginatedResponse<Message>>(\n `/conversations/${conversationId}/messages`,\n { params: serverParams },\n );\n return data;\n },\n\n async get(messageId: string): Promise<Message> {\n const { data } = await getApiClient().get<Message>(`/messages/${messageId}`);\n return data;\n },\n\n async send(conversationId: string, payload: SendData): Promise<Message> {\n const { data } = await getApiClient().post<Message>(\n `/conversations/${conversationId}/messages`,\n payload,\n );\n return data;\n },\n\n async update(messageId: string, text: string): Promise<Message> {\n const { data } = await getApiClient().put<Message>(`/messages/${messageId}`, { text });\n return data;\n },\n\n async delete(messageId: string): Promise<void> {\n await getApiClient().delete(`/messages/${messageId}`);\n },\n\n async deleteForMe(messageId: string): Promise<void> {\n await getApiClient().delete(`/messages/${messageId}/for-me`);\n },\n\n async addReaction(messageId: string, emoji: string): Promise<Message> {\n const { data } = await getApiClient().post<Message>(`/messages/${messageId}/reactions`, { emoji });\n return data;\n },\n\n async removeReaction(messageId: string, emoji: string): Promise<Message> {\n const { data } = await getApiClient().delete<Message>(\n `/messages/${messageId}/reactions/${encodeURIComponent(emoji)}`,\n );\n return data;\n },\n\n async star(messageId: string): Promise<void> {\n await getApiClient().post(`/messages/${messageId}/star`);\n },\n\n async unstar(messageId: string): Promise<void> {\n await getApiClient().delete(`/messages/${messageId}/star`);\n },\n\n async getStarred(params: { page?: number; limit?: number; conversationId?: string } = {}): Promise<PaginatedResponse<Message>> {\n const { data } = await getApiClient().get<PaginatedResponse<Message>>('/messages/starred', { params });\n return data;\n },\n\n async search(params: SearchParams): Promise<PaginatedResponse<Message>> {\n const { data } = await getApiClient().get<PaginatedResponse<Message>>('/messages/search', { params });\n return data;\n },\n\n async getLastRead(conversationId: string): Promise<{ lastReadMessageId: string | null; lastReadAt: string | null }> {\n const { data } = await getApiClient().get<{ lastReadMessageId: string | null; lastReadAt: string | null }>(\n `/conversations/${conversationId}/read-receipt`,\n );\n return data;\n },\n\n async markAsRead(conversationId: string, messageId?: string): Promise<void> {\n await getApiClient().post(`/conversations/${conversationId}/read`, messageId ? { messageId } : {});\n },\n\n async pin(messageId: string): Promise<Message> {\n const { data } = await getApiClient().post<Message>(`/messages/${messageId}/pin`);\n return data;\n },\n\n async unpin(messageId: string): Promise<Message> {\n const { data } = await getApiClient().delete<Message>(`/messages/${messageId}/pin`);\n return data;\n },\n\n async getPinned(conversationId: string): Promise<Message[]> {\n const { data } = await getApiClient().get<Message[]>(`/conversations/${conversationId}/pinned-messages`);\n return data;\n },\n};\n","import type { Conversation, ConversationListParams, ConversationUnreadCount, Message, MessageStatus, Participant, PaginatedResponse, UnreadSummary, User } from '../types/index.js';\nimport { getApiClient } from './client.js';\n\nfunction normalizeParticipant(p: any): Participant {\n const hasUserDetails = p.displayName || p.username || p.avatarUrl;\n return {\n userId: p.userId,\n role: p.role,\n joinedAt: p.joinedAt,\n isActive: p.isActive,\n user: hasUserDetails\n ? {\n id: p.userId,\n tenantId: '',\n email: '',\n username: p.username ?? '',\n displayName: p.displayName ?? p.username ?? '',\n avatarUrl: p.avatarUrl,\n status: 'offline' as const,\n createdAt: p.joinedAt ?? '',\n updatedAt: p.joinedAt ?? '',\n }\n : (p.user as User | undefined),\n };\n}\n\nfunction normalizeLastMessage(lastMsg: any): Message | undefined {\n if (!lastMsg) return undefined;\n if (lastMsg.content !== undefined) return lastMsg as Message;\n return {\n id: lastMsg.messageId ?? '',\n tenantId: '',\n conversationId: '',\n senderId: '',\n content: {\n type: lastMsg.hasAttachments ? 'attachment' : 'text',\n text: lastMsg.contentPreview,\n },\n reactions: [],\n status: (lastMsg.status ?? 'sent') as MessageStatus,\n isEdited: false,\n sentAt: lastMsg.sentAt ?? '',\n createdAt: lastMsg.sentAt ?? '',\n };\n}\n\nexport function normalizeConversation(conv: any): Conversation {\n return {\n ...conv,\n id: conv.id ?? conv.conversationId,\n participants: (conv.participants ?? []).map(normalizeParticipant),\n lastMessage: normalizeLastMessage(conv.lastMessage),\n };\n}\n\nexport interface CreateGroupData {\n name: string;\n description?: string;\n participantIds: string[];\n}\n\nexport interface CreateDirectData {\n userId: string;\n}\n\nexport interface UpdateConversationData {\n name?: string;\n description?: string;\n}\n\nexport const conversationsApi = {\n async list(params: ConversationListParams = {}): Promise<PaginatedResponse<Conversation>> {\n const { data } = await getApiClient().get<PaginatedResponse<Conversation>>('/conversations', { params });\n return { ...data, data: data.data.map(normalizeConversation) };\n },\n\n async get(conversationId: string): Promise<Conversation> {\n const { data } = await getApiClient().get<Conversation>(`/conversations/${conversationId}`);\n return normalizeConversation(data);\n },\n\n async createGroup(payload: CreateGroupData): Promise<Conversation> {\n const { data } = await getApiClient().post<Conversation>('/conversations', payload);\n return normalizeConversation(data);\n },\n\n async createDirect(payload: CreateDirectData): Promise<Conversation> {\n const { data } = await getApiClient().post<Conversation>('/conversations/direct', payload);\n return normalizeConversation(data);\n },\n\n async update(conversationId: string, payload: UpdateConversationData): Promise<Conversation> {\n const { data } = await getApiClient().put<Conversation>(`/conversations/${conversationId}`, payload);\n return normalizeConversation(data);\n },\n\n async delete(conversationId: string): Promise<void> {\n await getApiClient().delete(`/conversations/${conversationId}`);\n },\n\n async addParticipants(conversationId: string, userIds: string[], role?: 'admin' | 'member'): Promise<Conversation> {\n const { data } = await getApiClient().post<Conversation>(\n `/conversations/${conversationId}/participants`,\n { userIds, ...(role && { role }) },\n );\n return normalizeConversation(data);\n },\n\n async removeParticipant(conversationId: string, userId: string): Promise<Conversation> {\n const { data } = await getApiClient().delete<Conversation>(\n `/conversations/${conversationId}/participants/${userId}`,\n );\n return normalizeConversation(data);\n },\n\n async updateParticipantRole(conversationId: string, userId: string, role: 'admin' | 'member'): Promise<Conversation> {\n const { data } = await getApiClient().put<Conversation>(\n `/conversations/${conversationId}/participants/${userId}/role`,\n { role },\n );\n return normalizeConversation(data);\n },\n\n async mute(conversationId: string, mutedUntil?: string): Promise<void> {\n await getApiClient().post(`/conversations/${conversationId}/mute`, mutedUntil ? { mutedUntil } : {});\n },\n\n async unmute(conversationId: string): Promise<void> {\n await getApiClient().delete(`/conversations/${conversationId}/mute`);\n },\n\n async pin(conversationId: string): Promise<void> {\n await getApiClient().post(`/conversations/${conversationId}/pin`);\n },\n\n async unpin(conversationId: string): Promise<void> {\n await getApiClient().delete(`/conversations/${conversationId}/pin`);\n },\n\n async leave(conversationId: string, andDelete?: boolean): Promise<void> {\n const url = andDelete\n ? `/conversations/${conversationId}/leave?delete=true`\n : `/conversations/${conversationId}/leave`;\n await getApiClient().delete(url);\n },\n\n async getMembers(conversationId: string, filter?: 'deleted' | 'all'): Promise<Participant[]> {\n const { data } = await getApiClient().get<Participant[]>(\n `/conversations/${conversationId}/participants`,\n filter ? { params: { filter } } : undefined,\n );\n return data;\n },\n\n /**\n * Get unread message count for a single conversation.\n * Use this after app foreground or socket reconnect to refresh a specific count.\n */\n async getUnreadCount(conversationId: string): Promise<ConversationUnreadCount> {\n const { data } = await getApiClient().get<ConversationUnreadCount>(\n `/conversations/${conversationId}/unread`,\n );\n return data;\n },\n\n /**\n * Get total unread count across all conversations + per-conversation breakdown.\n * Use on app cold start, foreground resume, or after socket reconnect.\n * The socket keeps counts live while connected — this is the source of truth\n * when the socket was down.\n */\n async getUnreadSummary(): Promise<UnreadSummary> {\n const { data } = await getApiClient().get<UnreadSummary>('/conversations/unread');\n return data;\n },\n\n /**\n * Set the group icon from an already-uploaded file (admin only).\n * The fileId comes from uploadBatch() / client.uploadFiles() — same as attachments.\n * Server copies storageKey into conversation.iconMeta and deletes the chat_files record.\n */\n async uploadIcon(conversationId: string, fileId: string): Promise<Conversation> {\n const { data } = await getApiClient().put<Conversation>(\n `/conversations/${conversationId}/icon`,\n { fileId },\n );\n return normalizeConversation(data);\n },\n\n async removeIcon(conversationId: string): Promise<Conversation> {\n const { data } = await getApiClient().delete<Conversation>(\n `/conversations/${conversationId}/icon`,\n );\n return normalizeConversation(data);\n },\n\n};\n","import type {\n BatchUploadResult,\n FileResponse,\n PaginatedResponse,\n PresignedUrlRequest,\n PresignedUrlResponse,\n FileType,\n UploadableFile,\n} from '../types/index.js';\nimport type { PlatformUploadFn, PlatformCompressFn, ResolvedCompressionConfig } from '../config/types.js';\nimport { compressFile } from '../compression/compress.js';\nimport { getApiClient } from './client.js';\n\nexport const storageApi = {\n async requestPresignedUrl(payload: PresignedUrlRequest): Promise<PresignedUrlResponse> {\n const { data } = await getApiClient().post<PresignedUrlResponse>('/storage/presigned-url', payload);\n return data;\n },\n\n async requestPresignedUrlBatch(files: PresignedUrlRequest[]): Promise<{\n urls: PresignedUrlResponse[];\n errors: Array<{ filename: string; error: string }>;\n }> {\n const { data } = await getApiClient().post('/storage/presigned-url/batch', { files });\n return data;\n },\n\n async confirmUpload(fileId: string): Promise<FileResponse> {\n const { data } = await getApiClient().post<FileResponse>(`/storage/confirm/${fileId}`);\n return data;\n },\n\n async getFile(fileId: string): Promise<FileResponse> {\n const { data } = await getApiClient().get<FileResponse>(`/storage/files/${fileId}`);\n return data;\n },\n\n async getFileUrl(fileId: string, expiresIn?: number): Promise<{ url: string; expiresAt: string }> {\n const { data } = await getApiClient().get(`/storage/files/${fileId}/url`, {\n params: expiresIn ? { expiresIn } : {},\n });\n return data;\n },\n\n async deleteFile(fileId: string): Promise<void> {\n await getApiClient().delete(`/storage/files/${fileId}`);\n },\n\n async getConversationFiles(\n conversationId: string,\n params: { page?: number; limit?: number; type?: FileType } = {},\n ): Promise<PaginatedResponse<FileResponse>> {\n const { data } = await getApiClient().get(\n `/storage/conversations/${conversationId}/files`,\n { params },\n );\n return data;\n },\n\n async getMyFiles(params: { page?: number; limit?: number } = {}): Promise<PaginatedResponse<FileResponse>> {\n const { data } = await getApiClient().get('/storage/my-files', { params });\n return data;\n },\n};\n\n/**\n * High-level batch upload.\n * The actual binary transfer is delegated to platformUploadFn so this\n * function is platform-agnostic (works on web and React Native).\n * If platformCompressFn + compressionConfig are provided, each file is\n * compressed before the presigned URL is requested (so the server receives\n * the correct compressed size and MIME type).\n */\nexport async function uploadBatch(\n files: UploadableFile[],\n platformUploadFn: PlatformUploadFn,\n conversationId?: string,\n onProgress?: (pct: number) => void,\n platformCompressFn?: PlatformCompressFn,\n compressionConfig?: ResolvedCompressionConfig,\n): Promise<BatchUploadResult> {\n // Compress all files first (no-ops for unsupported types or when disabled)\n const compressedFiles = await Promise.all(\n files.map((f) => compressFile(f, platformCompressFn, compressionConfig ?? { enabled: false, imageQuality: 0.85, imageMaxDimension: 1920, compressDocuments: true })),\n );\n\n const requests: PresignedUrlRequest[] = compressedFiles.map((f) => ({\n filename: f.name,\n mimeType: f.type,\n size: f.size,\n conversationId,\n ...(f.compressed && {\n metadata: {\n compressed: f.compressed,\n originalSize: f.originalSize,\n compressionAlgorithm: f.compressionAlgorithm,\n },\n }),\n }));\n\n const { urls, errors: requestErrors } = await storageApi.requestPresignedUrlBatch(requests);\n\n const progressMap: Record<number, number> = {};\n const reportProgress = () => {\n if (!onProgress) return;\n const vals = Object.values(progressMap);\n const avg = vals.reduce((s, v) => s + v, 0) / Math.max(vals.length, 1);\n onProgress(Math.round(avg));\n };\n\n const successful: FileResponse[] = [];\n const failed: Array<{ filename: string; error: string }> = [...requestErrors];\n\n await Promise.all(\n urls.map(async (presigned, idx) => {\n const file = compressedFiles[idx];\n progressMap[idx] = 0;\n try {\n await platformUploadFn(presigned, file, (pct) => {\n progressMap[idx] = Math.round(pct * 0.9);\n reportProgress();\n });\n\n const result = await storageApi.confirmUpload(presigned.fileId);\n progressMap[idx] = 100;\n reportProgress();\n successful.push(result);\n } catch (err) {\n failed.push({ filename: file.name, error: (err as Error).message });\n }\n }),\n );\n\n return { successful, failed };\n}\n","import { getApiClient } from './client.js';\n\n// ─── Types ─────────────────────────────────────────────────────────────────\n\n/** Shared fields for every device registration. */\ninterface DeviceTokenBase {\n /**\n * Client-generated stable UUID for this device.\n * Store this in localStorage / SecureStore / Keychain and reuse it on every\n * app start so the same record is updated rather than duplicated.\n */\n deviceId: string;\n platform: 'ios' | 'android' | 'web';\n provider: 'expo' | 'fcm' | 'apns' | 'web-push';\n userAgent?: string;\n}\n\n/** Mobile push registration (expo | fcm | apns). */\nexport interface MobileDeviceToken extends DeviceTokenBase {\n provider: 'expo' | 'fcm' | 'apns';\n /** FCM registration token / APNs device token / Expo push token. */\n token: string;\n endpoint?: never;\n p256dh?: never;\n auth?: never;\n}\n\n/** Web push (VAPID) registration. */\nexport interface WebPushDeviceToken extends DeviceTokenBase {\n provider: 'web-push';\n /** PushSubscription.endpoint */\n endpoint: string;\n /** base64url-encoded PushSubscription key 'p256dh' */\n p256dh: string;\n /** base64url-encoded PushSubscription key 'auth' */\n auth: string;\n token?: never;\n}\n\nexport type RegisterDeviceTokenPayload = MobileDeviceToken | WebPushDeviceToken;\n\n// ─── API ───────────────────────────────────────────────────────────────────\n\nexport const devicesApi = {\n /**\n * Register or update a device push token with the chat server.\n *\n * Upserts by `deviceId` — calling this multiple times with the same deviceId\n * simply refreshes the token value (tokens can rotate silently on some platforms).\n *\n * The SDK never calls this automatically. The parent app or the `tokenProvider`\n * option in `pushNotifications` config is responsible for calling it after\n * obtaining the token from the OS / browser.\n */\n async register(payload: RegisterDeviceTokenPayload): Promise<void> {\n await getApiClient().post('/users/me/devices', payload);\n },\n\n /**\n * Remove a device token from the chat server.\n * Call this on logout so the user stops receiving push notifications on this device.\n */\n async remove(deviceId: string): Promise<void> {\n await getApiClient().delete(`/users/me/devices/${deviceId}`);\n },\n};\n","import type { PaginatedResponse, User, UserPreferences } from '../types/index.js';\nimport { getApiClient } from './client.js';\n\n/**\n * Fields for updating the current user's profile.\n *\n * Works in both builtin and non-builtin (external/SSO/WSO2) modes.\n *\n * In non-builtin modes, firstName/lastName/email/displayName/phone are also\n * synced from the upstream user-service (on socket connect if stale, and every\n * 2 hours via cron). Calling updateProfile() is the way to push an immediate\n * update to the chat server without waiting for the next sync cycle — useful\n * when the host app knows a profile change just happened in the identity provider.\n *\n * Uniqueness constraints: email and phone must each be unique within the tenant.\n * A 409 is returned if another user already holds the same email or phone.\n *\n * username and status are intentionally excluded — username is a system\n * identifier and status is lifecycle-managed by user-service.\n */\nexport interface UpdateProfilePayload {\n firstName?: string;\n lastName?: string;\n email?: string;\n displayName?: string;\n phone?: string;\n}\n\nexport const usersApi = {\n async list(params: { query?: string; page?: number; limit?: number } = {}): Promise<PaginatedResponse<User>> {\n const { data } = await getApiClient().get<PaginatedResponse<User>>('/users', { params });\n return data;\n },\n\n async getById(userId: string): Promise<User> {\n const { data } = await getApiClient().get<User>(`/users/${userId}`);\n return data;\n },\n\n async getLastSeen(userId: string): Promise<{ lastSeenAt: string | null }> {\n const { data } = await getApiClient().get<User>(`/users/${userId}`);\n return { lastSeenAt: data.lastSeenAt ?? null };\n },\n\n /**\n * Update basic profile fields for the current user.\n * Works in both builtin and non-builtin modes. Use this to push an immediate\n * profile update to the chat server when the host app knows a change just\n * happened — without waiting for the next 2-hour sync cycle.\n */\n async updateProfile(payload: UpdateProfilePayload): Promise<User> {\n const { data } = await getApiClient().put<User>('/users/me', payload);\n return data;\n },\n\n /**\n * Update notification preferences for the current user.\n * Partial update — only send fields you want to change.\n * A prefs record is automatically created with defaults when a device\n * token is first registered, so this never fails with \"not found\".\n */\n async updatePreferences(prefs: UserPreferences): Promise<User> {\n const { data } = await getApiClient().put<User>('/users/me/preferences', prefs);\n return data;\n },\n\n /**\n * Fetch current notification preferences for the current user.\n * Returns null if no prefs record exists yet (all defaults apply).\n */\n async getPreferences(): Promise<UserPreferences | null> {\n try {\n const { data } = await getApiClient().get<UserPreferences>('/users/me/preferences');\n return data;\n } catch {\n return null;\n }\n },\n};\n","import { io, Socket } from 'socket.io-client';\nimport type { ResolvedConfig } from '../config/types.js';\nimport type { ReadReceiptEvent, UserStatusEvent } from '../types/index.js';\nimport { detectTransitAlgo, resetAlgoCache } from '../crypto/detect.js';\nimport { fetchServerKeys, performHandshake } from '../crypto/handshake.js';\nimport { setTransitSession, clearTransitSession, configureTransit, isTransitEnabled, getSessionKey } from '../crypto/session.js';\nimport { encryptPayload, decryptPayload, isTransitEnvelope } from '../crypto/transit.js';\n\nexport type SocketStatus = 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | 'error';\nexport type StatusListener = (status: SocketStatus) => void;\n\nlet _socket: Socket | null = null;\nlet _status: SocketStatus = 'disconnected';\nconst _statusListeners = new Set<StatusListener>();\n\n// Stored so SocketProvider can call refreshAndReconnect() without re-passing config\nlet _getToken: (() => string | null | undefined) | null = null;\nlet _userId: string | undefined;\nlet _tenantId: string | undefined;\n\nfunction setStatus(s: SocketStatus) {\n _status = s;\n _statusListeners.forEach(l => l(s));\n}\n\nexport function getSocket(): Socket {\n if (!_socket) throw new Error('[AntzChat] Socket not initialized. Call connectSocket first.');\n return _socket;\n}\n\n/** Returns the socket if connected, null otherwise. Use this for fire-and-forget\n * operations (markRead, typing) that should silently no-op when not yet connected. */\nexport function tryGetSocket(): Socket | null {\n return _socket?.connected ? _socket : null;\n}\n\nexport function getSocketStatus(): SocketStatus {\n return _status;\n}\n\nexport function onSocketStatus(listener: StatusListener): () => void {\n _statusListeners.add(listener);\n return () => _statusListeners.delete(listener);\n}\n\n// ─── Transit helpers ──────────────────────────────────────────────────────────\n\n// Wraps socket.emit: encrypts payload when transit is enabled and key is ready.\nexport async function secureEmit(\n socket: Socket,\n event: string,\n payload: unknown,\n ack?: (response: unknown) => void,\n): Promise<void> {\n if (isTransitEnabled()) {\n const key = getSessionKey();\n if (key) {\n const envelope = await encryptPayload(payload, key);\n if (ack) {\n socket.emit(event, envelope, async (encryptedResponse: unknown) => {\n if (isTransitEnvelope(encryptedResponse)) {\n try {\n const decrypted = await decryptPayload(encryptedResponse, key);\n ack(decrypted);\n } catch {\n ack(encryptedResponse);\n }\n } else {\n ack(encryptedResponse);\n }\n });\n } else {\n socket.emit(event, envelope);\n }\n return;\n }\n }\n // Transit disabled or key not ready — send plaintext\n if (ack) {\n socket.emit(event, payload, ack);\n } else {\n socket.emit(event, payload);\n }\n}\n\n// Registers an incoming event handler that auto-decrypts when transit is enabled.\nexport function secureOn(\n socket: Socket,\n event: string,\n handler: (data: unknown) => void,\n): void {\n socket.on(event, async (raw: unknown) => {\n if (isTransitEnabled() && isTransitEnvelope(raw)) {\n const key = getSessionKey();\n if (key) {\n try {\n const data = await decryptPayload(raw, key);\n handler(data);\n return;\n } catch {\n console.error(`[AntzChat] Transit decryption failed for event: ${event}`);\n return;\n }\n }\n }\n handler(raw);\n });\n}\n\n// ─── Connect ──────────────────────────────────────────────────────────────────\n\nexport async function connectSocket(\n config: ResolvedConfig,\n getToken: () => string | null | undefined,\n): Promise<Socket> {\n if (_socket && !_socket.disconnected) return _socket;\n\n _getToken = getToken;\n _userId = config.userId;\n _tenantId = config.tenantId;\n\n const token = getToken();\n\n const socketHandshakeAuth: Record<string, unknown> = {\n token: token ? `Bearer ${token}` : '',\n ...(config.userId && { userId: config.userId }),\n ...(config.tenantId && { tenantId: config.tenantId }),\n ...(config.avatar?.url && { avatarUrl: config.avatar.url }),\n ...(config.avatar?.base64 && { avatarBase64: config.avatar.base64 }),\n };\n\n // ── Transit handshake setup ──────────────────────────────────────────────\n // performHandshake returns a bound deriveSessionKey closure — captures the\n // ephemeral private key for this specific connection with no shared module state.\n let boundDeriveSessionKey: ((sessionId: string) => Promise<CryptoKey>) | null = null;\n\n if (config.transitEncryption) {\n try {\n const serverKeys = await fetchServerKeys(config.apiUrl);\n if (!serverKeys.enabled) {\n throw new Error(\n '[AntzChat] Transit encryption mismatch: SDK has transitEncryption=true but server ' +\n 'has TRANSIT_ENCRYPTION_ENABLED=false. Align the config on both sides.',\n );\n }\n const algo = await detectTransitAlgo();\n boundDeriveSessionKey = await performHandshake(algo, serverKeys, socketHandshakeAuth);\n } catch (err: any) {\n if (err.message?.startsWith('[AntzChat] Transit encryption mismatch')) throw err;\n // Non-fatal (dev/offline) — log and continue without transit encryption\n console.warn('[AntzChat] Transit handshake setup failed, connecting without transit encryption:', err.message);\n }\n } else {\n // Explicitly disabled — verify server also has it off to catch config mismatches early\n try {\n const serverKeys = await fetchServerKeys(config.apiUrl);\n if (serverKeys.enabled) {\n throw new Error(\n '[AntzChat] Transit encryption mismatch: SDK has transitEncryption=false but server ' +\n 'has TRANSIT_ENCRYPTION_ENABLED=true. Align the config on both sides.',\n );\n }\n } catch (err: any) {\n if (err.message?.startsWith('[AntzChat] Transit encryption mismatch')) throw err;\n // /crypto/pubkey unavailable (old server without transit support) — proceed\n }\n }\n\n _socket = io(`${config.socketOrigin}/chat`, {\n auth: socketHandshakeAuth,\n path: config.socketPath,\n transports: ['websocket', 'polling'],\n reconnection: true,\n reconnectionAttempts: 10,\n reconnectionDelay: 1000,\n reconnectionDelayMax: 5000,\n autoConnect: true,\n });\n\n setStatus('connecting');\n\n _socket.on('connect', () => setStatus('connected'));\n _socket.on('disconnect', () => {\n setStatus('disconnected');\n clearTransitSession();\n });\n _socket.on('connect_error', (err) => {\n console.error('[AntzChat] Socket connect_error:', err?.message, (err as any)?.data);\n setStatus('error');\n });\n _socket.on('reconnecting', () => setStatus('reconnecting'));\n _socket.on('reconnect', () => setStatus('connected'));\n\n // ── Transit session key derivation ──────────────────────────────────────\n // Block connectSocket from resolving until the session key is ready.\n // This ensures all HTTP requests and socket.on() registrations that happen\n // after await connectSocket() already have an active session key.\n if (config.transitEncryption && boundDeriveSessionKey) {\n await new Promise<void>((resolve) => {\n const done = () => {\n clearTimeout(timeout);\n resolve();\n };\n\n // Safety timeout — if transit_session never arrives (connect_error, server\n // doesn't support transit), unblock HTTP and continue without encryption.\n const timeout = setTimeout(() => {\n console.warn('[AntzChat] transit_session timeout — unblocking HTTP without transit encryption');\n // Disable transit so HTTP requests are not blocked indefinitely\n configureTransit(false);\n done();\n }, 5000);\n\n _socket!.on('transit_session', async ({ sessionId }: { sessionId: string }) => {\n try {\n if (boundDeriveSessionKey) {\n const sessionKey = await boundDeriveSessionKey(sessionId);\n const algo = (socketHandshakeAuth['transitAlgo'] as 'x25519' | 'p256') ?? 'p256';\n setTransitSession({ sessionKey, algo, sessionId, enabled: true });\n }\n } catch (err: any) {\n console.error('[AntzChat] Failed to derive transit session key:', err.message);\n }\n done();\n });\n\n // Also unblock if the socket fails to connect at all\n _socket!.on('connect_error', () => done());\n });\n }\n\n // ── Application event listeners ──────────────────────────────────────────\n // Use secureOn so incoming events are auto-decrypted when transit is enabled.\n secureOn(_socket, 'read_receipt', (event) => {\n import('../stores/chat.store.js').then(({ useChatStore }) => {\n const e = event as ReadReceiptEvent;\n useChatStore.getState().setLastRead(e.conversationId, e.messageId, e.readAt);\n });\n });\n\n secureOn(_socket, 'user_online', (event) => {\n import('../stores/chat.store.js').then(({ useChatStore }) => {\n useChatStore.getState().setUserOnline((event as UserStatusEvent).userId);\n });\n });\n\n secureOn(_socket, 'user_offline', (event) => {\n import('../stores/chat.store.js').then(({ useChatStore }) => {\n const e = event as UserStatusEvent;\n const store = useChatStore.getState();\n store.setUserOffline(e.userId);\n if (e.lastSeenAt) store.setLastSeen(e.userId, e.lastSeenAt);\n });\n });\n\n // Return a proxy so all socket.on() calls from SocketProvider/consumers\n // automatically go through secureOn (decrypt on receive).\n return createSecureSocketProxy(_socket);\n}\n\n// Wraps a Socket so every .on() call is transparently routed through secureOn.\n// Events that don't need decryption (transit disabled or no key) pass through unchanged.\nfunction createSecureSocketProxy(socket: Socket): Socket {\n return new Proxy(socket, {\n get(target, prop) {\n if (prop === 'on') {\n return (event: string, handler: (data: unknown) => void) => {\n // Low-level Socket.IO internal events pass through directly\n const internal = ['connect', 'disconnect', 'connect_error', 'reconnect', 'reconnecting', 'error'];\n if (internal.includes(event as string)) {\n return target.on(event as any, handler as any);\n }\n secureOn(target, event, handler);\n return socket; // for chaining\n };\n }\n const val = (target as any)[prop];\n return typeof val === 'function' ? val.bind(target) : val;\n },\n });\n}\n\nexport function disconnectSocket() {\n if (_socket) {\n _socket.disconnect();\n _socket = null;\n setStatus('disconnected');\n }\n clearTransitSession();\n resetAlgoCache();\n _getToken = null;\n _userId = undefined;\n _tenantId = undefined;\n}\n\nexport function reconnectSocket(token: string, userId?: string, tenantId?: string) {\n if (_socket) {\n _socket.auth = {\n token: `Bearer ${token}`,\n ...(userId && { userId }),\n ...(tenantId && { tenantId }),\n };\n _socket.connect();\n }\n}\n\n/**\n * Updates the socket auth token from the stored getToken function and reconnects.\n * Called by SocketProvider on connect_error to handle expired-token scenarios.\n * Returns true if the token was updated, false if no token source is available.\n */\nexport function refreshSocketAuth(): boolean {\n if (!_socket || !_getToken) return false;\n const fresh = _getToken();\n if (!fresh) return false;\n _socket.auth = {\n token: `Bearer ${fresh}`,\n ...(_userId && { userId: _userId }),\n ...(_tenantId && { tenantId: _tenantId }),\n };\n return true;\n}\n","export type TransitAlgo = 'x25519' | 'p256';\n\nlet _cached: TransitAlgo | null = null;\n\n// Probes Web Crypto API for X25519 support once; caches result for the session.\n// RN and Node callers never call this — they always get X25519 via @noble.\nexport async function detectTransitAlgo(): Promise<TransitAlgo> {\n if (_cached) return _cached;\n\n try {\n await globalThis.crypto.subtle.generateKey(\n { name: 'X25519' } as any,\n false,\n ['deriveKey'],\n );\n _cached = 'x25519';\n } catch {\n _cached = 'p256';\n }\n\n return _cached;\n}\n\nexport function getCachedAlgo(): TransitAlgo | null {\n return _cached;\n}\n\nexport function resetAlgoCache(): void {\n _cached = null;\n}\n","import type { TransitAlgo } from './detect.js';\n\nexport interface ServerPublicKeys {\n x25519: string; // base64\n p256: string; // base64\n enabled: boolean;\n}\n\n// Fetches server public keys + enabled flag. Called once per init.\n// Unwraps the server's standard { success, data } envelope if present.\nexport async function fetchServerKeys(apiUrl: string): Promise<ServerPublicKeys> {\n const res = await fetch(`${apiUrl}/crypto/pubkey`);\n if (!res.ok) throw new Error(`[AntzChat] Failed to fetch server public key: ${res.status}`);\n const body = await res.json() as any;\n // Server wraps all responses in { success, data } via TransformInterceptor\n return (body?.data ?? body) as ServerPublicKeys;\n}\n\n// Performs the client side of the ECDH handshake:\n// 1. Generates an ephemeral key pair\n// 2. Injects the public key into socketHandshakeAuth (read by io() on connect)\n// 3. Returns a bound deriveSessionKey function that uses this handshake's\n// private key — no shared module state, safe for concurrent/re-entrant calls.\nexport async function performHandshake(\n algo: TransitAlgo,\n serverKeys: ServerPublicKeys,\n socketHandshakeAuth: Record<string, unknown>,\n): Promise<(sessionId: string) => Promise<CryptoKey>> {\n const ephemeral = await globalThis.crypto.subtle.generateKey(\n algo === 'x25519'\n ? { name: 'X25519' }\n : { name: 'ECDH', namedCurve: 'P-256' } as any,\n false,\n ['deriveBits'],\n );\n\n const pubRaw = await globalThis.crypto.subtle.exportKey('raw', (ephemeral as CryptoKeyPair).publicKey);\n\n socketHandshakeAuth['transitEphemeralPub'] = bufToB64(pubRaw);\n socketHandshakeAuth['transitAlgo'] = algo;\n\n const ephemeralPriv = (ephemeral as CryptoKeyPair).privateKey;\n\n // Return a closure — private key and server keys are captured here, not in module state.\n return (sessionId: string) => deriveSessionKey(ephemeralPriv, algo, serverKeys, sessionId);\n}\n\nasync function deriveSessionKey(\n ephemeralPriv: CryptoKey,\n algo: TransitAlgo,\n serverKeys: ServerPublicKeys,\n sessionId: string,\n): Promise<CryptoKey> {\n const serverPubB64 = algo === 'x25519' ? serverKeys.x25519 : serverKeys.p256;\n const serverPubRaw = b64ToBuf(serverPubB64);\n\n const keyAlgoParams = algo === 'x25519'\n ? { name: 'X25519' }\n : { name: 'ECDH', namedCurve: 'P-256' };\n\n const serverPubKey = await globalThis.crypto.subtle.importKey(\n 'raw',\n serverPubRaw,\n keyAlgoParams as any,\n false,\n [],\n );\n\n const sharedBits = await globalThis.crypto.subtle.deriveBits(\n { name: algo === 'x25519' ? 'X25519' : 'ECDH', public: serverPubKey } as any,\n ephemeralPriv,\n 256,\n );\n\n const hkdfKey = await globalThis.crypto.subtle.importKey(\n 'raw',\n sharedBits,\n 'HKDF',\n false,\n ['deriveKey'],\n );\n\n const salt = new TextEncoder().encode(sessionId);\n const info = new TextEncoder().encode('antz-transit-v1');\n\n return globalThis.crypto.subtle.deriveKey(\n { name: 'HKDF', hash: 'SHA-256', salt, info },\n hkdfKey,\n { name: 'AES-GCM', length: 256 },\n false,\n ['encrypt', 'decrypt'],\n );\n}\n\n// ─── Helpers ─────────────────────────────────────────────────────────────────\n\nfunction bufToB64(buf: ArrayBuffer): string {\n const bytes = new Uint8Array(buf);\n let str = '';\n bytes.forEach(b => { str += String.fromCharCode(b); });\n return btoa(str);\n}\n\nfunction b64ToBuf(b64: string): ArrayBuffer {\n const bin = atob(b64);\n const buf = new Uint8Array(bin.length);\n for (let i = 0; i < bin.length; i++) buf[i] = bin.charCodeAt(i);\n return buf.buffer;\n}\n","import type { SendMessagePayload } from '../types/index.js';\nimport { getSocket, tryGetSocket, onSocketStatus, secureEmit } from './socket.js';\n\nconst ACK_TIMEOUT = 5000;\nconst RECONNECT_WAIT_TIMEOUT = 15000;\n\n// Serial queue for send_message — prevents concurrent acks from racing the\n// 5s timeout when the user sends many messages in quick succession.\n//\n// Safety limits:\n// QUEUE_MAX_SIZE — hard cap on pending entries; overflow rejects immediately\n// QUEUE_ENTRY_TTL — max time an entry may wait in the queue before being\n// dropped (before it even reaches the socket), so stale\n// messages never arrive at the server minutes later\nconst QUEUE_MAX_SIZE = 100;\nconst QUEUE_ENTRY_TTL = 30_000; // 30 s\n\ntype QueueEntry = {\n run: () => Promise<unknown>;\n resolve: (v: unknown) => void;\n reject: (e: unknown) => void;\n enqueuedAt: number;\n};\n\nconst sendQueue: QueueEntry[] = [];\nlet sendQueueRunning = false;\n\nasync function drainSendQueue() {\n if (sendQueueRunning) return;\n sendQueueRunning = true;\n while (sendQueue.length > 0) {\n const entry = sendQueue.shift()!;\n if (Date.now() - entry.enqueuedAt > QUEUE_ENTRY_TTL) {\n entry.reject(new Error('[AntzChat] Message dropped: queued too long'));\n continue;\n }\n try {\n entry.resolve(await entry.run());\n } catch (e) {\n entry.reject(e);\n }\n }\n sendQueueRunning = false;\n}\n\nfunction queueSendMessage(payload: unknown): Promise<unknown> {\n if (sendQueue.length >= QUEUE_MAX_SIZE) {\n return Promise.reject(new Error('[AntzChat] Send queue full: too many messages in flight'));\n }\n return new Promise((resolve, reject) => {\n sendQueue.push({ run: () => withAck('send_message', payload), resolve, reject, enqueuedAt: Date.now() });\n drainSendQueue();\n });\n}\n\nfunction waitForReconnect(): Promise<void> {\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n unsubscribe();\n reject(new Error('[AntzChat] Socket reconnect timeout'));\n }, RECONNECT_WAIT_TIMEOUT);\n\n const unsubscribe = onSocketStatus((status) => {\n if (status === 'connected') {\n clearTimeout(timer);\n unsubscribe();\n resolve();\n } else if (status === 'error') {\n clearTimeout(timer);\n unsubscribe();\n reject(new Error('[AntzChat] Socket reconnect failed'));\n }\n });\n });\n}\n\n// withAck: for operations that need a response (send message, reactions, etc.)\n// Encrypts payload and decrypts ack response transparently via secureEmit.\nasync function withAck<T>(event: string, payload: unknown): Promise<T> {\n let socket = tryGetSocket();\n if (!socket) {\n await waitForReconnect();\n socket = tryGetSocket();\n }\n if (!socket) return Promise.reject(new Error(`[AntzChat] Socket not connected (event: ${event})`));\n\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => reject(new Error(`Socket ack timeout: ${event}`)), ACK_TIMEOUT);\n secureEmit(socket!, event, payload, (response) => {\n clearTimeout(timer);\n resolve(response as T);\n });\n });\n}\n\n// fireAndForget: best-effort — silently no-ops when not connected.\nfunction fireAndForget(event: string, payload: unknown): void {\n const socket = tryGetSocket();\n if (!socket) return;\n secureEmit(socket, event, payload);\n}\n\nexport const socketEmit = {\n joinRoom(conversationId: string) {\n fireAndForget('join_room', { conversationId });\n },\n\n leaveRoom(conversationId: string) {\n fireAndForget('leave_room', { conversationId });\n },\n\n sendMessage(payload: SendMessagePayload): Promise<unknown> {\n return queueSendMessage(payload);\n },\n\n updateMessage(messageId: string, text: string): Promise<unknown> {\n return withAck('update_message', { messageId, text });\n },\n\n deleteMessage(messageId: string): Promise<unknown> {\n return withAck('delete_message', { messageId });\n },\n\n deleteMessageForMe(messageId: string): Promise<unknown> {\n return withAck('delete_message_for_me', { messageId });\n },\n\n addReaction(messageId: string, emoji: string): Promise<unknown> {\n return withAck('add_reaction', { messageId, emoji });\n },\n\n removeReaction(messageId: string, emoji: string): Promise<unknown> {\n return withAck('remove_reaction', { messageId, emoji });\n },\n\n pinMessage(messageId: string): Promise<unknown> {\n return withAck('pin_message', { messageId });\n },\n\n unpinMessage(messageId: string): Promise<unknown> {\n return withAck('unpin_message', { messageId });\n },\n\n // markRead and typing are best-effort — silently dropped if socket not ready\n typing(conversationId: string, isTyping: boolean) {\n fireAndForget('typing', { conversationId, isTyping });\n },\n\n markRead(conversationId: string, messageId?: string) {\n fireAndForget('mark_read', { conversationId, ...(messageId ? { messageId } : {}) });\n },\n\n getOnlineUsers(userIds: string[]): Promise<string[]> {\n const socket = tryGetSocket();\n if (!socket) return Promise.resolve([]);\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => reject(new Error('get_online_users timeout')), ACK_TIMEOUT);\n secureEmit(socket, 'get_online_users', { userIds }, (response) => {\n clearTimeout(timer);\n if (response && typeof response === 'object' && 'onlineStatus' in (response as object)) {\n const status = (response as { onlineStatus: Record<string, boolean> }).onlineStatus;\n resolve(Object.entries(status).filter(([, v]) => v).map(([k]) => k));\n } else if (Array.isArray(response)) {\n resolve(response as string[]);\n } else {\n resolve([]);\n }\n });\n });\n },\n\n getTypingUsers(conversationId: string): Promise<unknown> {\n return withAck('get_typing_users', { conversationId });\n },\n};\n","import { create } from 'zustand';\nimport { persist } from 'zustand/middleware';\nimport type { AuthTokens, User } from '../types/index.js';\nimport type { PersistStorage } from '../config/types.js';\n\ninterface AuthState {\n user: User | null;\n tokens: AuthTokens | null;\n isAuthenticated: boolean;\n isLoading: boolean;\n isHydrated: boolean;\n\n setAuth: (user: User, tokens: AuthTokens) => void;\n setTokens: (tokens: AuthTokens) => void;\n setUser: (user: User) => void;\n logout: () => void;\n setLoading: (loading: boolean) => void;\n setHydrated: (hydrated: boolean) => void;\n}\n\n/**\n * Creates a platform-specific auth store.\n * Call once during SDK init with the platform's PersistStorage adapter.\n * - Web: pass a localStorage adapter\n * - RN: pass an AsyncStorage adapter\n */\nexport function createAuthStore(storage: PersistStorage) {\n if (!storage) throw new Error('[AntzChat] createAuthStore requires a valid PersistStorage — received undefined. Make sure the SDK config is fully resolved before initializing the store.');\n // Use a ref object so the onRehydrateStorage closure can call store.setState\n // without hitting a temporal dead zone. On web, localStorage is synchronous\n // and onRehydrateStorage fires during create() before `store` is assigned.\n const ref: { store: any } = { store: null };\n\n const store = create<AuthState>()(\n persist(\n (set) => ({\n user: null,\n tokens: null,\n isAuthenticated: false,\n isLoading: false,\n isHydrated: false,\n\n setAuth: (user, tokens) =>\n set({ user, tokens, isAuthenticated: true, isLoading: false }),\n\n setTokens: (tokens) => set({ tokens }),\n\n setUser: (user) => set({ user }),\n\n logout: () =>\n set({ user: null, tokens: null, isAuthenticated: false, isLoading: false }),\n\n setLoading: (isLoading) => set({ isLoading }),\n\n setHydrated: (isHydrated) => set({ isHydrated }),\n }),\n {\n name: 'antz-chat-auth',\n storage: {\n getItem: (name) => {\n const result = storage.getItem(name);\n if (result instanceof Promise) {\n return result.then((str) => (str ? JSON.parse(str) : null));\n }\n return result ? JSON.parse(result) : null;\n },\n setItem: (name, value) => {\n storage.setItem(name, JSON.stringify(value));\n },\n removeItem: (name) => storage.removeItem(name),\n },\n partialize: (state) => ({\n user: state.user,\n tokens: state.tokens,\n isAuthenticated: state.isAuthenticated,\n }) as AuthState,\n onRehydrateStorage: () => (state, error) => {\n if (error) {\n console.warn('[AntzChat] Auth store rehydration failed:', error);\n }\n // Access via ref — safe even when called synchronously during create()\n ref.store?.setState({ isHydrated: true });\n },\n },\n ),\n );\n\n // Assign ref immediately after create() completes\n ref.store = store;\n\n // If already hydrated synchronously (web/localStorage rehydrated during create),\n // set the flag now since the closure above may have called ref.store when it was null.\n if (!store.getState().isHydrated) {\n // Fallback: async storage (RN) — force hydrated after 3s max\n const hydrationTimeout = setTimeout(() => {\n if (!store.getState().isHydrated) {\n store.setState({ isHydrated: true });\n }\n }, 3000);\n\n // Clean up if it resolves naturally before timeout\n const unsub = store.subscribe((s) => {\n if (s.isHydrated) {\n clearTimeout(hydrationTimeout);\n unsub();\n }\n });\n }\n\n const tokenStore = {\n getAccessToken: () => store.getState().tokens?.accessToken,\n getRefreshToken: () => store.getState().tokens?.refreshToken,\n setTokens: (tokens: AuthTokens) => store.getState().setTokens(tokens),\n clearTokens: () => store.getState().logout(),\n };\n\n return { useAuthStore: store, authTokenStore: tokenStore };\n}\n\n// ─── Singleton instances (set during SDK init) ────────────────────────────────\n// Each SDK (web / RN) calls createAuthStore once and exports these.\n// Direct import of these will throw until initAuthStore is called.\n\nlet _authStore: ReturnType<typeof createAuthStore> | null = null;\n\n/**\n * Initialize the auth store singleton.\n * Idempotent — subsequent calls with the same storage return the existing instance.\n * This ensures tokens persisted in localStorage/AsyncStorage survive re-renders\n * and React StrictMode double-invocations.\n */\nexport function initAuthStore(storage: PersistStorage) {\n if (!_authStore) {\n _authStore = createAuthStore(storage);\n }\n return _authStore;\n}\n\nexport function getAuthStore() {\n if (!_authStore) throw new Error('[AntzChat] Auth store not initialized. Call initAuthStore first.');\n return _authStore;\n}\n\n/** Reset the singleton — only for tests / SDK teardown. */\nexport function resetAuthStore() {\n _authStore = null;\n}\n","import type { AntzChatConfig, PlatformUploadFn } from './config/types.js';\nimport { resolveConfig } from './config/types.js';\nimport { initApiClient, setApiClientInstance } from './api/client.js';\nimport { initAuthStore } from './stores/auth.store.js';\nimport { authApi } from './api/auth.js';\nimport { messagesApi } from './api/messages.js';\nimport { conversationsApi } from './api/conversations.js';\nimport { storageApi, uploadBatch } from './api/storage.js';\nimport { usersApi } from './api/users.js';\nimport { connectSocket, disconnectSocket, getSocket } from './socket/socket.js';\nimport { socketEmit } from './socket/emitters.js';\nimport type { UploadableFile } from './types/index.js';\n\ninterface ClientSocketHandle {\n emit: typeof socketEmit;\n on(event: string, handler: (...args: unknown[]) => void): void;\n off(event: string, handler: (...args: unknown[]) => void): void;\n}\n\n/**\n * Headless client for non-React consumers or direct programmatic use.\n * Usage:\n * const client = new AntzChatClient({ apiUrl, authToken, platformUploadFn, persistStorage })\n * await client.connect()\n */\nexport class AntzChatClient {\n private _config;\n private _authStore;\n\n readonly auth = authApi;\n readonly messages = messagesApi;\n readonly conversations = conversationsApi;\n readonly storage = storageApi;\n readonly users = usersApi;\n readonly socket: ClientSocketHandle = {\n emit: socketEmit,\n on: (event, handler) => getSocket().on(event, handler as (...args: any[]) => void),\n off: (event, handler) => getSocket().off(event, handler as (...args: any[]) => void),\n };\n\n constructor(rawConfig: AntzChatConfig) {\n this._config = resolveConfig(rawConfig);\n this._authStore = initAuthStore(rawConfig.persistStorage);\n\n if (rawConfig.authToken) {\n this._authStore.authTokenStore.setTokens({\n accessToken: rawConfig.authToken,\n refreshToken: '',\n tokenType: 'Bearer',\n expiresIn: 0,\n });\n }\n\n const instance = initApiClient(this._config, this._authStore.authTokenStore);\n setApiClientInstance(instance);\n }\n\n async connect(): Promise<void> {\n await connectSocket(this._config, () => this._authStore.authTokenStore.getAccessToken());\n }\n\n disconnect(): void {\n disconnectSocket();\n }\n\n uploadFiles(files: UploadableFile[], conversationId?: string) {\n return uploadBatch(\n files,\n this._config.platformUploadFn,\n conversationId,\n this._config.upload.onProgress,\n this._config.platformCompressFn,\n this._config.compression,\n );\n }\n\n async uploadIcon(conversationId: string, file: UploadableFile) {\n const result = await this.uploadFiles([file], conversationId);\n const fileId = result.successful[0]?.id;\n if (!fileId) throw new Error('Icon upload failed');\n return conversationsApi.uploadIcon(conversationId, fileId);\n }\n}\n"],"mappings":"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","names":["axios","bufToB64","b64ToBuf","useChatStore","import_zustand"]}
|