@antzsoft/chat-core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,811 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ AntzChatClient: () => AntzChatClient,
34
+ authApi: () => authApi,
35
+ connectSocket: () => connectSocket,
36
+ conversationsApi: () => conversationsApi,
37
+ createAuthStore: () => createAuthStore,
38
+ devicesApi: () => devicesApi,
39
+ disconnectSocket: () => disconnectSocket,
40
+ getApiClient: () => getApiClient,
41
+ getAuthStore: () => getAuthStore,
42
+ getSocket: () => getSocket,
43
+ getSocketStatus: () => getSocketStatus,
44
+ initApiClient: () => initApiClient,
45
+ initAuthStore: () => initAuthStore,
46
+ messagesApi: () => messagesApi,
47
+ normalizeConversation: () => normalizeConversation,
48
+ onSocketStatus: () => onSocketStatus,
49
+ reconnectSocket: () => reconnectSocket,
50
+ resetAuthStore: () => resetAuthStore,
51
+ resolveConfig: () => resolveConfig,
52
+ setApiClientInstance: () => setApiClientInstance,
53
+ socketEmit: () => socketEmit,
54
+ storageApi: () => storageApi,
55
+ tryGetSocket: () => tryGetSocket,
56
+ uploadBatch: () => uploadBatch,
57
+ useChatStore: () => useChatStore
58
+ });
59
+ module.exports = __toCommonJS(src_exports);
60
+
61
+ // src/config/types.ts
62
+ function resolveConfig(config) {
63
+ const socketUrl = config.socketUrl ?? config.apiUrl.replace(/\/api\/v\d+\/?$/, "").replace(/\/$/, "");
64
+ const raw = config.upload?.maxFileSizeMB;
65
+ let limits;
66
+ if (typeof raw === "number") {
67
+ limits = { image: raw, video: raw, audio: raw, document: raw, default: raw };
68
+ } else {
69
+ const fallback = raw?.default ?? 25;
70
+ limits = {
71
+ image: raw?.image ?? 5,
72
+ video: raw?.video ?? 25,
73
+ audio: raw?.audio ?? 10,
74
+ document: raw?.document ?? 10,
75
+ default: fallback
76
+ };
77
+ }
78
+ return {
79
+ apiUrl: config.apiUrl.replace(/\/$/, ""),
80
+ socketUrl,
81
+ authToken: config.authToken,
82
+ authProvider: config.authProvider,
83
+ userId: config.userId,
84
+ tenantId: config.tenantId,
85
+ encryptionMode: config.encryptionMode ?? "none",
86
+ upload: {
87
+ maxFileSizeMB: limits,
88
+ maxFilesPerMessage: config.upload?.maxFilesPerMessage ?? 10,
89
+ allowedTypes: config.upload?.allowedTypes ?? ["image", "video", "audio", "document"],
90
+ onUploadError: config.upload?.onUploadError,
91
+ onProgress: config.upload?.onProgress
92
+ },
93
+ platformUploadFn: config.platformUploadFn,
94
+ persistStorage: config.persistStorage
95
+ };
96
+ }
97
+
98
+ // src/api/client.ts
99
+ var import_axios = __toESM(require("axios"), 1);
100
+ var _tokenStore = null;
101
+ var _config = null;
102
+ function initApiClient(config, tokenStore) {
103
+ _config = config;
104
+ _tokenStore = tokenStore;
105
+ const client = import_axios.default.create({
106
+ baseURL: config.apiUrl,
107
+ headers: { "Content-Type": "application/json" }
108
+ });
109
+ client.interceptors.request.use((req) => {
110
+ const token = _tokenStore?.getAccessToken();
111
+ if (token) req.headers["Authorization"] = `Bearer ${token}`;
112
+ if (_config?.userId) req.headers["x-user-id"] = _config.userId;
113
+ if (_config?.tenantId) req.headers["X-Tenant-ID"] = _config.tenantId;
114
+ return req;
115
+ });
116
+ let isRefreshing = false;
117
+ let refreshQueue = [];
118
+ client.interceptors.response.use(
119
+ (response) => {
120
+ if (response.data && typeof response.data === "object" && "success" in response.data && "data" in response.data) {
121
+ response.data = response.data.data;
122
+ }
123
+ return response;
124
+ },
125
+ async (error) => {
126
+ const original = error.config;
127
+ if (error.response?.status === 401 && !original._retry) {
128
+ const refreshToken = _tokenStore?.getRefreshToken();
129
+ if (!refreshToken) {
130
+ _tokenStore?.clearTokens();
131
+ return Promise.reject(error);
132
+ }
133
+ if (isRefreshing) {
134
+ return new Promise((resolve) => {
135
+ refreshQueue.push((newToken) => {
136
+ original.headers["Authorization"] = `Bearer ${newToken}`;
137
+ resolve(client(original));
138
+ });
139
+ });
140
+ }
141
+ original._retry = true;
142
+ isRefreshing = true;
143
+ try {
144
+ const { data } = await import_axios.default.post(
145
+ `${_config.apiUrl}/auth/refresh`,
146
+ { refreshToken }
147
+ );
148
+ const tokens = data.data ?? data;
149
+ _tokenStore?.setTokens(tokens);
150
+ refreshQueue.forEach((cb) => cb(tokens.accessToken));
151
+ refreshQueue = [];
152
+ original.headers["Authorization"] = `Bearer ${tokens.accessToken}`;
153
+ return client(original);
154
+ } catch {
155
+ _tokenStore?.clearTokens();
156
+ return Promise.reject(error);
157
+ } finally {
158
+ isRefreshing = false;
159
+ }
160
+ }
161
+ return Promise.reject(error);
162
+ }
163
+ );
164
+ return client;
165
+ }
166
+ var _instance = null;
167
+ function setApiClientInstance(instance) {
168
+ _instance = instance;
169
+ }
170
+ function getApiClient() {
171
+ if (!_instance) throw new Error("[AntzChat] API client not initialized. Call initApiClient first.");
172
+ return _instance;
173
+ }
174
+
175
+ // src/api/auth.ts
176
+ var authApi = {
177
+ async login(credentials) {
178
+ const { data } = await getApiClient().post("/auth/login", credentials);
179
+ return data;
180
+ },
181
+ async register(payload) {
182
+ const { data } = await getApiClient().post("/auth/register", payload);
183
+ return data;
184
+ },
185
+ async refresh(refreshToken) {
186
+ const { data } = await getApiClient().post("/auth/refresh", { refreshToken });
187
+ return data;
188
+ },
189
+ async logout(refreshToken) {
190
+ await getApiClient().post("/auth/logout", refreshToken ? { refreshToken } : {});
191
+ },
192
+ async logoutAll() {
193
+ await getApiClient().post("/auth/logout-all");
194
+ },
195
+ async getMe() {
196
+ const { data } = await getApiClient().get("/users/me");
197
+ return data;
198
+ }
199
+ };
200
+
201
+ // src/api/messages.ts
202
+ var messagesApi = {
203
+ async list(conversationId, params = {}) {
204
+ const { data } = await getApiClient().get(
205
+ `/conversations/${conversationId}/messages`,
206
+ { params }
207
+ );
208
+ return data;
209
+ },
210
+ async get(messageId) {
211
+ const { data } = await getApiClient().get(`/messages/${messageId}`);
212
+ return data;
213
+ },
214
+ async send(conversationId, payload) {
215
+ const { data } = await getApiClient().post(
216
+ `/conversations/${conversationId}/messages`,
217
+ payload
218
+ );
219
+ return data;
220
+ },
221
+ async update(messageId, text) {
222
+ const { data } = await getApiClient().put(`/messages/${messageId}`, { text });
223
+ return data;
224
+ },
225
+ async delete(messageId) {
226
+ await getApiClient().delete(`/messages/${messageId}`);
227
+ },
228
+ async addReaction(messageId, emoji) {
229
+ const { data } = await getApiClient().post(`/messages/${messageId}/reactions`, { emoji });
230
+ return data;
231
+ },
232
+ async removeReaction(messageId, emoji) {
233
+ const { data } = await getApiClient().delete(
234
+ `/messages/${messageId}/reactions/${encodeURIComponent(emoji)}`
235
+ );
236
+ return data;
237
+ },
238
+ async star(messageId) {
239
+ await getApiClient().post(`/messages/${messageId}/star`);
240
+ },
241
+ async unstar(messageId) {
242
+ await getApiClient().delete(`/messages/${messageId}/star`);
243
+ },
244
+ async getStarred(params = {}) {
245
+ const { data } = await getApiClient().get("/messages/starred", { params });
246
+ return data;
247
+ },
248
+ async search(params) {
249
+ const { data } = await getApiClient().get("/messages/search", { params });
250
+ return data;
251
+ },
252
+ async markAsRead(conversationId, messageId) {
253
+ await getApiClient().post(`/conversations/${conversationId}/read`, messageId ? { messageId } : {});
254
+ },
255
+ async pin(messageId) {
256
+ const { data } = await getApiClient().post(`/messages/${messageId}/pin`);
257
+ return data;
258
+ },
259
+ async unpin(messageId) {
260
+ const { data } = await getApiClient().delete(`/messages/${messageId}/pin`);
261
+ return data;
262
+ },
263
+ async getPinned(conversationId) {
264
+ const { data } = await getApiClient().get(`/conversations/${conversationId}/pinned-messages`);
265
+ return data;
266
+ }
267
+ };
268
+
269
+ // src/api/conversations.ts
270
+ function normalizeParticipant(p) {
271
+ const hasUserDetails = p.displayName || p.username || p.avatarUrl;
272
+ return {
273
+ userId: p.userId,
274
+ role: p.role,
275
+ joinedAt: p.joinedAt,
276
+ user: hasUserDetails ? {
277
+ id: p.userId,
278
+ tenantId: "",
279
+ email: "",
280
+ username: p.username ?? "",
281
+ displayName: p.displayName ?? p.username ?? "",
282
+ avatarUrl: p.avatarUrl,
283
+ status: "offline",
284
+ createdAt: p.joinedAt ?? "",
285
+ updatedAt: p.joinedAt ?? ""
286
+ } : p.user
287
+ };
288
+ }
289
+ function normalizeLastMessage(lastMsg) {
290
+ if (!lastMsg) return void 0;
291
+ if (lastMsg.content !== void 0) return lastMsg;
292
+ return {
293
+ id: lastMsg.messageId ?? "",
294
+ tenantId: "",
295
+ conversationId: "",
296
+ senderId: "",
297
+ content: {
298
+ type: lastMsg.hasAttachments ? "attachment" : "text",
299
+ text: lastMsg.contentPreview
300
+ },
301
+ reactions: [],
302
+ status: "sent",
303
+ isEdited: false,
304
+ sentAt: lastMsg.sentAt ?? "",
305
+ createdAt: lastMsg.sentAt ?? ""
306
+ };
307
+ }
308
+ function normalizeConversation(conv) {
309
+ return {
310
+ ...conv,
311
+ id: conv.id ?? conv.conversationId,
312
+ participants: (conv.participants ?? []).map(normalizeParticipant),
313
+ lastMessage: normalizeLastMessage(conv.lastMessage)
314
+ };
315
+ }
316
+ var conversationsApi = {
317
+ async list(params = {}) {
318
+ const { data } = await getApiClient().get("/conversations", { params });
319
+ return { ...data, data: data.data.map(normalizeConversation) };
320
+ },
321
+ async get(conversationId) {
322
+ const { data } = await getApiClient().get(`/conversations/${conversationId}`);
323
+ return normalizeConversation(data);
324
+ },
325
+ async createGroup(payload) {
326
+ const { data } = await getApiClient().post("/conversations", payload);
327
+ return normalizeConversation(data);
328
+ },
329
+ async createDirect(payload) {
330
+ const { data } = await getApiClient().post("/conversations/direct", payload);
331
+ return normalizeConversation(data);
332
+ },
333
+ async update(conversationId, payload) {
334
+ const { data } = await getApiClient().put(`/conversations/${conversationId}`, payload);
335
+ return normalizeConversation(data);
336
+ },
337
+ async delete(conversationId) {
338
+ await getApiClient().delete(`/conversations/${conversationId}`);
339
+ },
340
+ async addParticipants(conversationId, userIds) {
341
+ const { data } = await getApiClient().post(
342
+ `/conversations/${conversationId}/participants`,
343
+ { userIds }
344
+ );
345
+ return normalizeConversation(data);
346
+ },
347
+ async removeParticipant(conversationId, userId) {
348
+ const { data } = await getApiClient().delete(
349
+ `/conversations/${conversationId}/participants/${userId}`
350
+ );
351
+ return normalizeConversation(data);
352
+ },
353
+ async updateParticipantRole(conversationId, userId, role) {
354
+ const { data } = await getApiClient().put(
355
+ `/conversations/${conversationId}/participants/${userId}/role`,
356
+ { role }
357
+ );
358
+ return normalizeConversation(data);
359
+ },
360
+ async mute(conversationId, mutedUntil) {
361
+ await getApiClient().post(`/conversations/${conversationId}/mute`, mutedUntil ? { mutedUntil } : {});
362
+ },
363
+ async unmute(conversationId) {
364
+ await getApiClient().delete(`/conversations/${conversationId}/mute`);
365
+ },
366
+ async pin(conversationId) {
367
+ await getApiClient().post(`/conversations/${conversationId}/pin`);
368
+ },
369
+ async unpin(conversationId) {
370
+ await getApiClient().delete(`/conversations/${conversationId}/pin`);
371
+ },
372
+ async leave(conversationId) {
373
+ await getApiClient().delete(`/conversations/${conversationId}/leave`);
374
+ },
375
+ async getMembers(conversationId) {
376
+ const { data } = await getApiClient().get(`/conversations/${conversationId}/participants`);
377
+ return data;
378
+ },
379
+ async searchUsers(query) {
380
+ const { data } = await getApiClient().get("/users/search", { params: { query } });
381
+ return data.data;
382
+ }
383
+ };
384
+
385
+ // src/api/storage.ts
386
+ var storageApi = {
387
+ async requestPresignedUrl(payload) {
388
+ const { data } = await getApiClient().post("/storage/presigned-url", payload);
389
+ return data;
390
+ },
391
+ async requestPresignedUrlBatch(files) {
392
+ const { data } = await getApiClient().post("/storage/presigned-url/batch", { files });
393
+ return data;
394
+ },
395
+ async confirmUpload(fileId) {
396
+ const { data } = await getApiClient().post(`/storage/confirm/${fileId}`);
397
+ return data;
398
+ },
399
+ async getFile(fileId) {
400
+ const { data } = await getApiClient().get(`/storage/files/${fileId}`);
401
+ return data;
402
+ },
403
+ async getFileUrl(fileId, expiresIn) {
404
+ const { data } = await getApiClient().get(`/storage/files/${fileId}/url`, {
405
+ params: expiresIn ? { expiresIn } : {}
406
+ });
407
+ return data;
408
+ },
409
+ async deleteFile(fileId) {
410
+ await getApiClient().delete(`/storage/files/${fileId}`);
411
+ },
412
+ async getConversationFiles(conversationId, params = {}) {
413
+ const { data } = await getApiClient().get(
414
+ `/storage/conversations/${conversationId}/files`,
415
+ { params }
416
+ );
417
+ return data;
418
+ },
419
+ async getMyFiles(params = {}) {
420
+ const { data } = await getApiClient().get("/storage/my-files", { params });
421
+ return data;
422
+ }
423
+ };
424
+ async function uploadBatch(files, platformUploadFn, conversationId, onProgress) {
425
+ const requests = files.map((f) => ({
426
+ filename: f.name,
427
+ mimeType: f.type,
428
+ size: f.size,
429
+ conversationId
430
+ }));
431
+ const { urls, errors: requestErrors } = await storageApi.requestPresignedUrlBatch(requests);
432
+ const progressMap = {};
433
+ const reportProgress = () => {
434
+ if (!onProgress) return;
435
+ const vals = Object.values(progressMap);
436
+ const avg = vals.reduce((s, v) => s + v, 0) / Math.max(vals.length, 1);
437
+ onProgress(Math.round(avg));
438
+ };
439
+ const successful = [];
440
+ const failed = [...requestErrors];
441
+ await Promise.all(
442
+ urls.map(async (presigned, idx) => {
443
+ const file = files[idx];
444
+ progressMap[idx] = 0;
445
+ try {
446
+ await platformUploadFn(presigned, file, (pct) => {
447
+ progressMap[idx] = Math.round(pct * 0.9);
448
+ reportProgress();
449
+ });
450
+ const result = await storageApi.confirmUpload(presigned.fileId);
451
+ progressMap[idx] = 100;
452
+ reportProgress();
453
+ successful.push(result);
454
+ } catch (err) {
455
+ failed.push({ filename: file.name, error: err.message });
456
+ }
457
+ })
458
+ );
459
+ return { successful, failed };
460
+ }
461
+
462
+ // src/api/devices.ts
463
+ var devicesApi = {
464
+ /**
465
+ * Register or update a device push token with the chat server.
466
+ *
467
+ * Upserts by `deviceId` — calling this multiple times with the same deviceId
468
+ * simply refreshes the token value (tokens can rotate silently on some platforms).
469
+ *
470
+ * The SDK never calls this automatically. The parent app or the `tokenProvider`
471
+ * option in `pushNotifications` config is responsible for calling it after
472
+ * obtaining the token from the OS / browser.
473
+ */
474
+ async register(payload) {
475
+ await getApiClient().post("/users/me/devices", payload);
476
+ },
477
+ /**
478
+ * Remove a device token from the chat server.
479
+ * Call this on logout so the user stops receiving push notifications on this device.
480
+ */
481
+ async remove(deviceId) {
482
+ await getApiClient().delete(`/users/me/devices/${deviceId}`);
483
+ }
484
+ };
485
+
486
+ // src/socket/socket.ts
487
+ var import_socket = require("socket.io-client");
488
+ var _socket = null;
489
+ var _status = "disconnected";
490
+ var _statusListeners = /* @__PURE__ */ new Set();
491
+ function setStatus(s) {
492
+ _status = s;
493
+ _statusListeners.forEach((l) => l(s));
494
+ }
495
+ function getSocket() {
496
+ if (!_socket) throw new Error("[AntzChat] Socket not initialized. Call connectSocket first.");
497
+ return _socket;
498
+ }
499
+ function tryGetSocket() {
500
+ return _socket?.connected ? _socket : null;
501
+ }
502
+ function getSocketStatus() {
503
+ return _status;
504
+ }
505
+ function onSocketStatus(listener) {
506
+ _statusListeners.add(listener);
507
+ return () => _statusListeners.delete(listener);
508
+ }
509
+ async function connectSocket(config, getToken) {
510
+ if (_socket?.connected) return _socket;
511
+ const token = getToken();
512
+ _socket = (0, import_socket.io)(`${config.socketUrl}/chat`, {
513
+ auth: { token: token ? `Bearer ${token}` : "" },
514
+ transports: ["websocket", "polling"],
515
+ reconnection: true,
516
+ reconnectionAttempts: 10,
517
+ reconnectionDelay: 1e3,
518
+ reconnectionDelayMax: 5e3,
519
+ autoConnect: true
520
+ });
521
+ setStatus("connecting");
522
+ _socket.on("connect", () => setStatus("connected"));
523
+ _socket.on("disconnect", () => setStatus("disconnected"));
524
+ _socket.on("connect_error", () => setStatus("error"));
525
+ _socket.on("reconnecting", () => setStatus("reconnecting"));
526
+ _socket.on("reconnect", () => setStatus("connected"));
527
+ return _socket;
528
+ }
529
+ function disconnectSocket() {
530
+ if (_socket) {
531
+ _socket.disconnect();
532
+ _socket = null;
533
+ setStatus("disconnected");
534
+ }
535
+ }
536
+ function reconnectSocket(token) {
537
+ if (_socket) {
538
+ _socket.auth = { token: `Bearer ${token}` };
539
+ _socket.connect();
540
+ }
541
+ }
542
+
543
+ // src/socket/emitters.ts
544
+ var ACK_TIMEOUT = 5e3;
545
+ function withAck(event, payload) {
546
+ const socket = tryGetSocket();
547
+ if (!socket) return Promise.reject(new Error(`[AntzChat] Socket not connected (event: ${event})`));
548
+ return new Promise((resolve, reject) => {
549
+ const timer = setTimeout(() => reject(new Error(`Socket ack timeout: ${event}`)), ACK_TIMEOUT);
550
+ socket.emit(event, payload, (response) => {
551
+ clearTimeout(timer);
552
+ resolve(response);
553
+ });
554
+ });
555
+ }
556
+ function fireAndForget(event, payload) {
557
+ const socket = tryGetSocket();
558
+ if (!socket) return;
559
+ socket.emit(event, payload);
560
+ }
561
+ var socketEmit = {
562
+ joinRoom(conversationId) {
563
+ fireAndForget("join_room", { conversationId });
564
+ },
565
+ leaveRoom(conversationId) {
566
+ fireAndForget("leave_room", { conversationId });
567
+ },
568
+ sendMessage(payload) {
569
+ return withAck("send_message", payload);
570
+ },
571
+ updateMessage(messageId, text) {
572
+ return withAck("update_message", { messageId, text });
573
+ },
574
+ deleteMessage(messageId) {
575
+ return withAck("delete_message", { messageId });
576
+ },
577
+ addReaction(messageId, emoji) {
578
+ return withAck("add_reaction", { messageId, emoji });
579
+ },
580
+ removeReaction(messageId, emoji) {
581
+ return withAck("remove_reaction", { messageId, emoji });
582
+ },
583
+ pinMessage(messageId) {
584
+ return withAck("pin_message", { messageId });
585
+ },
586
+ unpinMessage(messageId) {
587
+ return withAck("unpin_message", { messageId });
588
+ },
589
+ // markRead and typing are best-effort — silently dropped if socket not ready
590
+ typing(conversationId, isTyping) {
591
+ fireAndForget("typing", { conversationId, isTyping });
592
+ },
593
+ markRead(conversationId, messageId) {
594
+ fireAndForget("mark_read", { conversationId, ...messageId ? { messageId } : {} });
595
+ },
596
+ getOnlineUsers(userIds) {
597
+ const socket = tryGetSocket();
598
+ if (!socket) return Promise.resolve([]);
599
+ return new Promise((resolve, reject) => {
600
+ const timer = setTimeout(() => reject(new Error("get_online_users timeout")), ACK_TIMEOUT);
601
+ socket.emit("get_online_users", { userIds }, (response) => {
602
+ clearTimeout(timer);
603
+ if (response && typeof response === "object" && "onlineStatus" in response) {
604
+ const status = response.onlineStatus;
605
+ resolve(Object.entries(status).filter(([, v]) => v).map(([k]) => k));
606
+ } else if (Array.isArray(response)) {
607
+ resolve(response);
608
+ } else {
609
+ resolve([]);
610
+ }
611
+ });
612
+ });
613
+ },
614
+ getTypingUsers(conversationId) {
615
+ return withAck("get_typing_users", { conversationId });
616
+ }
617
+ };
618
+
619
+ // src/stores/auth.store.ts
620
+ var import_zustand = require("zustand");
621
+ var import_middleware = require("zustand/middleware");
622
+ function createAuthStore(storage) {
623
+ if (!storage) throw new Error("[AntzChat] createAuthStore requires a valid PersistStorage \u2014 received undefined. Make sure the SDK config is fully resolved before initializing the store.");
624
+ const ref = { store: null };
625
+ const store = (0, import_zustand.create)()(
626
+ (0, import_middleware.persist)(
627
+ (set) => ({
628
+ user: null,
629
+ tokens: null,
630
+ isAuthenticated: false,
631
+ isLoading: false,
632
+ isHydrated: false,
633
+ setAuth: (user, tokens) => set({ user, tokens, isAuthenticated: true, isLoading: false }),
634
+ setTokens: (tokens) => set({ tokens }),
635
+ setUser: (user) => set({ user }),
636
+ logout: () => set({ user: null, tokens: null, isAuthenticated: false, isLoading: false }),
637
+ setLoading: (isLoading) => set({ isLoading }),
638
+ setHydrated: (isHydrated) => set({ isHydrated })
639
+ }),
640
+ {
641
+ name: "antz-chat-auth",
642
+ storage: {
643
+ getItem: (name) => {
644
+ const result = storage.getItem(name);
645
+ if (result instanceof Promise) {
646
+ return result.then((str) => str ? JSON.parse(str) : null);
647
+ }
648
+ return result ? JSON.parse(result) : null;
649
+ },
650
+ setItem: (name, value) => {
651
+ storage.setItem(name, JSON.stringify(value));
652
+ },
653
+ removeItem: (name) => storage.removeItem(name)
654
+ },
655
+ partialize: (state) => ({
656
+ user: state.user,
657
+ tokens: state.tokens,
658
+ isAuthenticated: state.isAuthenticated
659
+ }),
660
+ onRehydrateStorage: () => (state, error) => {
661
+ if (error) {
662
+ console.warn("[AntzChat] Auth store rehydration failed:", error);
663
+ }
664
+ ref.store?.setState({ isHydrated: true });
665
+ }
666
+ }
667
+ )
668
+ );
669
+ ref.store = store;
670
+ if (!store.getState().isHydrated) {
671
+ const hydrationTimeout = setTimeout(() => {
672
+ if (!store.getState().isHydrated) {
673
+ store.setState({ isHydrated: true });
674
+ }
675
+ }, 3e3);
676
+ const unsub = store.subscribe((s) => {
677
+ if (s.isHydrated) {
678
+ clearTimeout(hydrationTimeout);
679
+ unsub();
680
+ }
681
+ });
682
+ }
683
+ const tokenStore = {
684
+ getAccessToken: () => store.getState().tokens?.accessToken,
685
+ getRefreshToken: () => store.getState().tokens?.refreshToken,
686
+ setTokens: (tokens) => store.getState().setTokens(tokens),
687
+ clearTokens: () => store.getState().logout()
688
+ };
689
+ return { useAuthStore: store, authTokenStore: tokenStore };
690
+ }
691
+ var _authStore = null;
692
+ function initAuthStore(storage) {
693
+ if (!_authStore) {
694
+ _authStore = createAuthStore(storage);
695
+ }
696
+ return _authStore;
697
+ }
698
+ function getAuthStore() {
699
+ if (!_authStore) throw new Error("[AntzChat] Auth store not initialized. Call initAuthStore first.");
700
+ return _authStore;
701
+ }
702
+ function resetAuthStore() {
703
+ _authStore = null;
704
+ }
705
+
706
+ // src/stores/chat.store.ts
707
+ var import_zustand2 = require("zustand");
708
+ var useChatStore = (0, import_zustand2.create)((set) => ({
709
+ activeConversationId: null,
710
+ pendingTarget: null,
711
+ typingUsers: {},
712
+ onlineUsers: [],
713
+ replyingTo: null,
714
+ editingMessage: null,
715
+ isSidebarOpen: true,
716
+ isGroupInfoOpen: false,
717
+ isStarredPanelOpen: false,
718
+ setActiveConversation: (id) => set({ activeConversationId: id, replyingTo: null, editingMessage: null }),
719
+ setPendingTarget: (target) => set({ pendingTarget: target }),
720
+ addTypingUser: (conversationId, user) => set((state) => {
721
+ const existing = state.typingUsers[conversationId] ?? [];
722
+ const deduped = existing.filter((u) => u.userId !== user.userId);
723
+ return { typingUsers: { ...state.typingUsers, [conversationId]: [...deduped, user] } };
724
+ }),
725
+ removeTypingUser: (conversationId, userId) => set((state) => ({
726
+ typingUsers: {
727
+ ...state.typingUsers,
728
+ [conversationId]: (state.typingUsers[conversationId] ?? []).filter(
729
+ (u) => u.userId !== userId
730
+ )
731
+ }
732
+ })),
733
+ setUserOnline: (userId) => set((state) => ({
734
+ onlineUsers: state.onlineUsers.includes(userId) ? state.onlineUsers : [...state.onlineUsers, userId]
735
+ })),
736
+ setUserOffline: (userId) => set((state) => ({ onlineUsers: state.onlineUsers.filter((id) => id !== userId) })),
737
+ setOnlineUsers: (userIds) => set({ onlineUsers: userIds }),
738
+ setReplyingTo: (message) => set({ replyingTo: message, editingMessage: null }),
739
+ setEditingMessage: (message) => set({ editingMessage: message, replyingTo: null }),
740
+ toggleSidebar: () => set((state) => ({ isSidebarOpen: !state.isSidebarOpen })),
741
+ setSidebarOpen: (open) => set({ isSidebarOpen: open }),
742
+ toggleGroupInfo: () => set((state) => ({ isGroupInfoOpen: !state.isGroupInfoOpen })),
743
+ setGroupInfoOpen: (open) => set({ isGroupInfoOpen: open }),
744
+ toggleStarredPanel: () => set((state) => ({ isStarredPanelOpen: !state.isStarredPanelOpen })),
745
+ setStarredPanelOpen: (open) => set({ isStarredPanelOpen: open })
746
+ }));
747
+
748
+ // src/client-facade.ts
749
+ var AntzChatClient = class {
750
+ constructor(rawConfig) {
751
+ this.auth = authApi;
752
+ this.messages = messagesApi;
753
+ this.conversations = conversationsApi;
754
+ this.storage = storageApi;
755
+ this.socket = {
756
+ emit: socketEmit,
757
+ on: (event, handler) => getSocket().on(event, handler),
758
+ off: (event, handler) => getSocket().off(event, handler)
759
+ };
760
+ this._config = resolveConfig(rawConfig);
761
+ this._authStore = initAuthStore(rawConfig.persistStorage);
762
+ if (rawConfig.authToken) {
763
+ this._authStore.authTokenStore.setTokens({
764
+ accessToken: rawConfig.authToken,
765
+ refreshToken: "",
766
+ tokenType: "Bearer",
767
+ expiresIn: 0
768
+ });
769
+ }
770
+ const instance = initApiClient(this._config, this._authStore.authTokenStore);
771
+ setApiClientInstance(instance);
772
+ }
773
+ async connect() {
774
+ await connectSocket(this._config, () => this._authStore.authTokenStore.getAccessToken());
775
+ }
776
+ disconnect() {
777
+ disconnectSocket();
778
+ }
779
+ uploadFiles(files, conversationId) {
780
+ return uploadBatch(files, this._config.platformUploadFn, conversationId, this._config.upload.onProgress);
781
+ }
782
+ };
783
+ // Annotate the CommonJS export names for ESM import in node:
784
+ 0 && (module.exports = {
785
+ AntzChatClient,
786
+ authApi,
787
+ connectSocket,
788
+ conversationsApi,
789
+ createAuthStore,
790
+ devicesApi,
791
+ disconnectSocket,
792
+ getApiClient,
793
+ getAuthStore,
794
+ getSocket,
795
+ getSocketStatus,
796
+ initApiClient,
797
+ initAuthStore,
798
+ messagesApi,
799
+ normalizeConversation,
800
+ onSocketStatus,
801
+ reconnectSocket,
802
+ resetAuthStore,
803
+ resolveConfig,
804
+ setApiClientInstance,
805
+ socketEmit,
806
+ storageApi,
807
+ tryGetSocket,
808
+ uploadBatch,
809
+ useChatStore
810
+ });
811
+ //# sourceMappingURL=index.cjs.map