vanshi-sdk 1.0.6 → 1.2.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,131 +1,1040 @@
1
- // src/plugins/socketio/client.ts
2
- import io from "socket.io-client";
1
+ // src/sdk/storage/store.ts
2
+ import Dexie from "dexie";
3
+
4
+ // src/sdk/storage/sendState.ts
5
+ function numericSendState(value) {
6
+ if (value === 0 || value === 1 || value === 2)
7
+ return value;
8
+ if (value === "0" || value === "1" || value === "2")
9
+ return Number(value);
10
+ }
11
+ function messageSendState(data, fallback = 1) {
12
+ const state = numericSendState(data.sentStatus) ?? numericSendState(data.sendState);
13
+ if (state !== void 0)
14
+ return state;
15
+ switch (data.sendState) {
16
+ case "pending":
17
+ case "sending":
18
+ case "unknown":
19
+ return 0;
20
+ case "sent":
21
+ return 1;
22
+ case "failed":
23
+ return 2;
24
+ default:
25
+ return fallback;
26
+ }
27
+ }
28
+
29
+ // src/sdk/message/replyIdentity.ts
30
+ var INVALID_REPLY_RESPONSE = "INVALID_REPLY_RESPONSE";
31
+ function isSelfReferencingReply(row) {
32
+ const id = row?.messageID;
33
+ const quoted = row?.repliedInfo;
34
+ return !!id && [quoted?.messageID, quoted?.messageInfo?.messageID].some((originalID) => originalID && String(originalID) === String(id));
35
+ }
36
+ function filterInvalidReplyMessages(data) {
37
+ if (!data || typeof data !== "object")
38
+ return data;
39
+ if (Array.isArray(data)) {
40
+ const filtered2 = data.map(filterInvalidReplyMessages).filter((row) => row !== void 0);
41
+ return filtered2.length === data.length && filtered2.every((row, index) => row === data[index]) ? data : filtered2;
42
+ }
43
+ if (isSelfReferencingReply(data))
44
+ return void 0;
45
+ let filtered = data;
46
+ for (const key of ["messageList", "infos", "message"]) {
47
+ if (filtered[key] && typeof filtered[key] === "object") {
48
+ const value = filterInvalidReplyMessages(filtered[key]);
49
+ if (value === void 0)
50
+ return void 0;
51
+ if (value !== filtered[key])
52
+ filtered = { ...filtered, [key]: value };
53
+ }
54
+ }
55
+ return filtered;
56
+ }
57
+ var MESSAGE_EVENTS = /* @__PURE__ */ new Set(["receivePeerMessage", "receiveGroupMessage", "messageSentStatusChanged", "messageEdited", "messageRevokeReceived", "messageDeleted", "messageReceiptChanged"]);
58
+ function filterReplyPushArgs(event, args) {
59
+ if (!MESSAGE_EVENTS.has(event))
60
+ return args;
61
+ const filtered = args.map(filterInvalidReplyMessages);
62
+ return filtered.some((value, index) => value === void 0 && args[index] !== void 0) ? void 0 : filtered;
63
+ }
64
+
65
+ // src/sdk/storage/mediaPreview.ts
66
+ var previews = /* @__PURE__ */ new Map();
67
+ function preview(row, field) {
68
+ const blob = row[field];
69
+ if (typeof Blob === "undefined" || !(blob instanceof Blob) || typeof URL === "undefined" || typeof URL.createObjectURL !== "function")
70
+ return;
71
+ const key = `${row.localID}:${field}`;
72
+ const existing = previews.get(key);
73
+ if (existing?.version === row.updatedAt)
74
+ return existing.url;
75
+ if (existing)
76
+ URL.revokeObjectURL(existing.url);
77
+ const url = URL.createObjectURL(blob);
78
+ previews.set(key, { version: row.updatedAt, url });
79
+ return url;
80
+ }
81
+ function restoreMediaPreviews(row) {
82
+ if (!row.file && !row.thumbnailFile)
83
+ return row;
84
+ return {
85
+ ...row,
86
+ fileDownloadUrl: row.fileDownloadUrl || preview(row, "file"),
87
+ thumbnailDownloadUrl: row.thumbnailDownloadUrl || preview(row, "thumbnailFile") || (String(row.type) === "11" ? preview(row, "file") : void 0)
88
+ };
89
+ }
90
+ function clearMediaPreviews() {
91
+ for (const { url } of previews.values())
92
+ URL.revokeObjectURL(url);
93
+ previews.clear();
94
+ }
95
+ function invalidateMediaPreviews(localID) {
96
+ for (const field of ["file", "thumbnailFile"]) {
97
+ const key = `${localID}:${field}`;
98
+ const existing = previews.get(key);
99
+ if (existing)
100
+ URL.revokeObjectURL(existing.url);
101
+ previews.delete(key);
102
+ }
103
+ }
104
+
105
+ // src/sdk/storage/store.ts
106
+ var listeners = /* @__PURE__ */ new Map();
107
+ function onStorageEvent(event, handler) {
108
+ const set = listeners.get(event) || /* @__PURE__ */ new Set();
109
+ set.add(handler);
110
+ listeners.set(event, set);
111
+ }
112
+ function offStorageEvent(event, handler) {
113
+ if (handler)
114
+ listeners.get(event)?.delete(handler);
115
+ else
116
+ listeners.delete(event);
117
+ }
118
+ function emitStorageEvent(event, data) {
119
+ listeners.get(event)?.forEach((handler) => {
120
+ try {
121
+ const result = handler(data);
122
+ if (result && typeof result.then === "function")
123
+ Promise.resolve(result).catch((error) => console.error("[VanshiSdk] async storage event handler failed", error));
124
+ } catch (error) {
125
+ console.error("[VanshiSdk] storage event handler failed", error);
126
+ }
127
+ });
128
+ }
129
+ var STORAGE_EVENTS = /* @__PURE__ */ new Set(["localMessagesChanged", "localStorageError", "localSyncStateChanged"]);
130
+ function migrateLegacySendState(row) {
131
+ const unconfirmed = ["pending", "sending", "unknown"].includes(row.sendState);
132
+ row.sendState = unconfirmed ? 0 : messageSendState(row);
133
+ if (unconfirmed) {
134
+ row.sentStatus = 0;
135
+ row._deliveryUnconfirmed = true;
136
+ } else if (row.sentStatus === void 0)
137
+ row.sentStatus = row.sendState;
138
+ }
139
+ var MessageDatabase = class extends Dexie {
140
+ constructor(name) {
141
+ super(name);
142
+ const schema = {
143
+ messages: "&localID, &[conversationKey+messageID], &[conversationKey+clientBizId], [conversationKey+timestamp], timestamp, sendState",
144
+ meta: "&key"
145
+ };
146
+ this.version(1).stores(schema);
147
+ this.version(2).stores(schema).upgrade(async (tx) => {
148
+ await tx.table("messages").toCollection().modify(migrateLegacySendState);
149
+ await tx.table("meta").toCollection().modify((row) => {
150
+ if (row.patch && ("sendState" in row.patch || "sentStatus" in row.patch))
151
+ migrateLegacySendState(row.patch);
152
+ });
153
+ });
154
+ this.version(3).stores({ ...schema, messages: `${schema.messages}, [conversationKey+sendState], [conversationKey+hasServerID], conversationKey` }).upgrade((tx) => tx.table("messages").toCollection().modify((row) => {
155
+ row.hasServerID = row.messageID ? 1 : 0;
156
+ }));
157
+ }
158
+ };
159
+ var active;
160
+ var generation = 0;
161
+ var lastError;
162
+ var captureStorage = () => active;
163
+ var isCurrentStorage = (ctx) => !!ctx && ctx === active && ctx.active;
164
+ var conversationKey = (id, type) => JSON.stringify([String(type), String(id)]);
165
+ var newLocalID = () => `local_${Date.now()}_${Math.random().toString(36).slice(2)}_${Math.random().toString(36).slice(2)}`;
166
+ function reportStorageError(error, ctx) {
167
+ if (ctx && !isCurrentStorage(ctx))
168
+ return;
169
+ if (ctx)
170
+ ctx.failureCount = (ctx.failureCount || 0) + 1;
171
+ lastError = error instanceof Error ? error.message : String(error);
172
+ emitStorageEvent("localStorageError", { userId: ctx?.userId, message: lastError });
173
+ }
174
+ function closeLocalStorage() {
175
+ clearMediaPreviews();
176
+ generation++;
177
+ if (active) {
178
+ active.active = false;
179
+ active.channel?.close();
180
+ active.db.close();
181
+ }
182
+ active = void 0;
183
+ lastError = void 0;
184
+ }
185
+ function getLocalStorageStatus() {
186
+ return { enabled: !!active, userId: active?.userId, databaseName: active?.db.name, error: lastError };
187
+ }
188
+ async function openLocalStorage(userId, environment) {
189
+ closeLocalStorage();
190
+ const name = `vanshi_chat_${encodeURIComponent(environment)}_${encodeURIComponent(userId)}`;
191
+ const db = new MessageDatabase(name);
192
+ const ctx = { generation, epoch: newLocalID(), userId, db, active: true, ready: Promise.resolve() };
193
+ active = ctx;
194
+ ctx.ready = (async () => {
195
+ await db.open();
196
+ if (!isCurrentStorage(ctx)) {
197
+ db.close();
198
+ return;
199
+ }
200
+ await db.messages.where("sendState").equals(0).modify((row) => {
201
+ if (!row._deliveryUnconfirmed)
202
+ row.errorMessage = "Sending interrupted; delivery not confirmed";
203
+ row._deliveryUnconfirmed = true;
204
+ row.sentStatus = 0;
205
+ row.updatedAt = Date.now();
206
+ });
207
+ if (typeof BroadcastChannel !== "undefined" && isCurrentStorage(ctx)) {
208
+ ctx.channel = new BroadcastChannel(name);
209
+ ctx.channel.onmessage = ({ data }) => {
210
+ if (isCurrentStorage(ctx))
211
+ emitStorageEvent("localMessagesChanged", { ...data, userId });
212
+ };
213
+ }
214
+ })();
215
+ try {
216
+ await ctx.ready;
217
+ } catch (error) {
218
+ if (isCurrentStorage(ctx)) {
219
+ reportStorageError(error, ctx);
220
+ ctx.active = false;
221
+ db.close();
222
+ active = void 0;
223
+ }
224
+ }
225
+ }
226
+ async function storageWork(ctx, work) {
227
+ if (!isCurrentStorage(ctx))
228
+ return;
229
+ try {
230
+ await ctx.ready;
231
+ if (!isCurrentStorage(ctx))
232
+ return;
233
+ return await work(ctx);
234
+ } catch (error) {
235
+ reportStorageError(error, ctx);
236
+ }
237
+ }
238
+ function changed(ctx, key) {
239
+ if (!isCurrentStorage(ctx))
240
+ return;
241
+ const data = { userId: ctx.userId, conversationKey: key };
242
+ emitStorageEvent("localMessagesChanged", data);
243
+ ctx.channel?.postMessage(data);
244
+ }
245
+ function clean(input) {
246
+ const output = {};
247
+ for (const [key, value] of Object.entries(input)) {
248
+ if (value !== void 0 && typeof value !== "function" && key !== "localPreviewUrl") {
249
+ output[key] = value;
250
+ }
251
+ }
252
+ for (const key of ["fileDownloadUrl", "thumbnailDownloadUrl"]) {
253
+ if (typeof output[key] === "string" && output[key].startsWith("blob:"))
254
+ delete output[key];
255
+ }
256
+ return output;
257
+ }
258
+ function version(row) {
259
+ return Number(row.editedTime || row.editedAt || 0) || 0;
260
+ }
261
+ async function saveMessages(ctx, messages, fallback = {}, source = "server") {
262
+ const fromServer = source === "server" || source === "receive";
263
+ return storageWork(ctx, async (ctx2) => {
264
+ const released = /* @__PURE__ */ new Set();
265
+ await ctx2.db.transaction("rw", ctx2.db.messages, ctx2.db.meta, async () => {
266
+ for (const input of messages) {
267
+ if (!input || typeof input !== "object")
268
+ continue;
269
+ const data = clean({ ...fallback, ...input });
270
+ if (isSelfReferencingReply(data))
271
+ continue;
272
+ const id = data.conversationID ?? fallback.toConversationID;
273
+ const type = data.conversationType;
274
+ if (id === void 0 || type === void 0)
275
+ continue;
276
+ const key = conversationKey(id, type);
277
+ const messageID = data.messageID ? String(data.messageID) : void 0;
278
+ let clientBizId = data.clientBizId ? String(data.clientBizId) : void 0;
279
+ const byServer = messageID ? await ctx2.db.messages.where("[conversationKey+messageID]").equals([key, messageID]).first() : void 0;
280
+ let byClient = clientBizId ? await ctx2.db.messages.where("[conversationKey+clientBizId]").equals([key, clientBizId]).first() : void 0;
281
+ if (messageID && byClient?.messageID && byClient.messageID !== messageID) {
282
+ byClient = void 0;
283
+ clientBizId = void 0;
284
+ delete data.clientBizId;
285
+ delete data.localID;
286
+ delete data.localMessageID;
287
+ }
288
+ let previous = byClient && byServer && byClient.localID !== byServer.localID ? { ...byServer, ...byClient, deleted: byServer.deleted || byClient.deleted, revoked: byServer.revoked || byClient.revoked } : byClient || byServer;
289
+ if (!previous && data.localID) {
290
+ const candidate = await ctx2.db.messages.get(data.localID);
291
+ if (candidate?.conversationKey === key)
292
+ previous = candidate;
293
+ }
294
+ if (byServer && byClient && byServer.localID !== byClient.localID)
295
+ await ctx2.db.messages.delete(byServer.localID);
296
+ if (source === "receive" && data.direction == null) {
297
+ data.direction = data.senderUserID != null ? String(data.senderUserID) === ctx2.userId ? 0 : 1 : previous?.direction ?? 1;
298
+ }
299
+ const globalClear = await ctx2.db.meta.get("clear:all");
300
+ const cleared = await ctx2.db.meta.get(`clear:${key}`);
301
+ const timestamp = Number(data.timestamp ?? previous?.timestamp ?? (source === "patch" ? 0 : Date.now()));
302
+ const row = {
303
+ type: 0,
304
+ ...previous,
305
+ ...data,
306
+ localID: previous?.localID || data.localID || newLocalID(),
307
+ conversationKey: key,
308
+ conversationID: String(id),
309
+ conversationType: String(type),
310
+ hasServerID: messageID || previous?.messageID ? 1 : 0,
311
+ timestamp: Number.isFinite(timestamp) ? timestamp : Date.now(),
312
+ sendState: messageSendState(data, fromServer ? 1 : previous?.sendState ?? (source === "patch" ? 1 : 0)),
313
+ updatedAt: Date.now()
314
+ };
315
+ if (fromServer)
316
+ row.sentStatus = data.sentStatus ?? row.sendState;
317
+ if (fromServer)
318
+ delete row._deliveryUnconfirmed;
319
+ if (messageID) {
320
+ row.messageID = messageID;
321
+ const deferred = await ctx2.db.meta.get(`patch:${messageID}`);
322
+ if (deferred?.patch) {
323
+ const patch = { ...deferred.patch };
324
+ if (fromServer && data._fetchSequence !== void 0) {
325
+ const newer = (epoch, sequence2) => patch[epoch] === ctx2.epoch && patch[sequence2] > data._fetchSequence;
326
+ if (!newer("_sendEpoch", "_sendSequence")) {
327
+ for (const field of ["sentStatus", "sendState", "_sendEpoch", "_sendSequence", "_deliveryUnconfirmed"])
328
+ delete patch[field];
329
+ }
330
+ if (!newer("_mutationEpoch", "_receiptSequence")) {
331
+ for (const field of ["receiptStatus", "_receiptSequence"])
332
+ delete patch[field];
333
+ }
334
+ if (version(patch) <= version(data) && !newer("_mutationEpoch", "_contentSequence")) {
335
+ for (const field of ["message", "massage", "extendedData", "editedTime", "editedAt", "_contentSequence"])
336
+ delete patch[field];
337
+ }
338
+ }
339
+ Object.assign(row, patch, { localID: row.localID, conversationKey: key, conversationID: String(id), conversationType: String(type) });
340
+ await ctx2.db.meta.delete(`patch:${messageID}`);
341
+ }
342
+ }
343
+ row.sendState = messageSendState(row, row.sendState);
344
+ if (clientBizId)
345
+ row.clientBizId = clientBizId;
346
+ if (!row.messageID)
347
+ delete row.messageID;
348
+ if (!row.clientBizId)
349
+ delete row.clientBizId;
350
+ if (previous?.sendState === 1 && source === "local") {
351
+ row.sendState = 1;
352
+ row.sentStatus = previous.sentStatus ?? 1;
353
+ delete row.errorCode;
354
+ delete row.errorMessage;
355
+ }
356
+ if (fromServer) {
357
+ delete row.errorCode;
358
+ delete row.errorMessage;
359
+ }
360
+ const staleContent = previous && (version(previous) > version(data) || // A replay of the creation event is not a new edit, even after reopening the DB.
361
+ source === "receive" && previous._contentSequence > 0 && version(data) <= version(previous) || fromServer && data._fetchSequence !== void 0 && previous._mutationEpoch === ctx2.epoch && previous._contentSequence > data._fetchSequence && version(data) <= version(previous) || fromServer && data._fetchedAt !== void 0 && previous._mutationAt > data._fetchedAt && version(data) <= version(previous));
362
+ if (staleContent) {
363
+ for (const field of ["message", "massage", "editedTime", "editedAt", "extendedData", "repliedInfo", "fileName", "fileSize", "fileDownloadUrl", "thumbnailDownloadUrl"]) {
364
+ if (field in previous)
365
+ row[field] = previous[field];
366
+ else
367
+ delete row[field];
368
+ }
369
+ row._contentSequence = previous._contentSequence;
370
+ row._mutationEpoch = previous._mutationEpoch;
371
+ }
372
+ if (previous && (source === "receive" && previous._receiptSequence > 0 || fromServer && data._fetchSequence !== void 0 && previous._mutationEpoch === ctx2.epoch && previous._receiptSequence > data._fetchSequence)) {
373
+ row.receiptStatus = previous.receiptStatus;
374
+ row._receiptSequence = previous._receiptSequence;
375
+ }
376
+ if (previous && fromServer && data._fetchSequence !== void 0 && previous._sendEpoch === ctx2.epoch && previous._sendSequence > data._fetchSequence) {
377
+ row.sendState = previous.sendState;
378
+ row.sentStatus = previous.sentStatus;
379
+ row._sendSequence = previous._sendSequence;
380
+ row._sendEpoch = previous._sendEpoch;
381
+ if (previous._deliveryUnconfirmed)
382
+ row._deliveryUnconfirmed = true;
383
+ }
384
+ if (row.sendState !== 0)
385
+ delete row._deliveryUnconfirmed;
386
+ if (row.sendState === 1) {
387
+ delete row.errorCode;
388
+ delete row.errorMessage;
389
+ if (fromServer) {
390
+ delete row.file;
391
+ delete row.thumbnailFile;
392
+ }
393
+ }
394
+ if (previous?.deleted || data.deleted || (globalClear || cleared) && row.timestamp <= Math.max(globalClear?.at || 0, cleared?.at || 0))
395
+ row.deleted = true;
396
+ if (row.deleted) {
397
+ row.message = "";
398
+ row.massage = "";
399
+ delete row.file;
400
+ delete row.thumbnailFile;
401
+ delete row.fileDownloadUrl;
402
+ delete row.thumbnailDownloadUrl;
403
+ }
404
+ if (row.revoked || previous?.revoked || data.revoked) {
405
+ row.revoked = true;
406
+ row.message = "";
407
+ row.massage = "";
408
+ delete row.file;
409
+ delete row.thumbnailFile;
410
+ delete row.fileDownloadUrl;
411
+ delete row.thumbnailDownloadUrl;
412
+ }
413
+ delete row._fetchedAt;
414
+ delete row._fetchSequence;
415
+ await ctx2.db.messages.put(row);
416
+ if (!row.file && !row.thumbnailFile)
417
+ released.add(row.localID);
418
+ else
419
+ released.delete(row.localID);
420
+ }
421
+ });
422
+ for (const localID of released)
423
+ invalidateMediaPreviews(localID);
424
+ changed(ctx2);
425
+ });
426
+ }
427
+ async function markSendError(ctx, clientBizId, fallback, error) {
428
+ const unknown = error?.code === INVALID_REPLY_RESPONSE || /timeout|disconnected|reconnect|socket not connected|socket not initialized/i.test(error?.message || String(error));
429
+ await saveMessages(ctx, [{
430
+ clientBizId,
431
+ sendState: unknown ? 0 : 2,
432
+ _deliveryUnconfirmed: unknown,
433
+ sentStatus: unknown ? 0 : 2,
434
+ errorCode: error?.code,
435
+ errorMessage: error?.message || String(error)
436
+ }], fallback, "local");
437
+ }
438
+ async function deleteStoredMessages(ctx, id, type, ids) {
439
+ if (id === void 0 !== (type === void 0))
440
+ throw new Error("Both conversationID and conversationType are required");
441
+ await storageWork(ctx, async (ctx2) => {
442
+ const released = [];
443
+ const key = id !== void 0 && type !== void 0 ? conversationKey(id, type) : void 0;
444
+ await ctx2.db.transaction("rw", ctx2.db.messages, ctx2.db.meta, async () => {
445
+ const rows = key ? await ctx2.db.messages.where("[conversationKey+timestamp]").between([key, Dexie.minKey], [key, Dexie.maxKey]).toArray() : await ctx2.db.messages.toArray();
446
+ const selected = ids ? rows.filter((row) => ids.includes(row.messageID || "") || ids.includes(row.localID)) : rows;
447
+ for (const row of selected) {
448
+ const removed = { ...row, deleted: true, message: "", massage: "", updatedAt: Date.now() };
449
+ delete removed.file;
450
+ delete removed.thumbnailFile;
451
+ delete removed.fileDownloadUrl;
452
+ delete removed.thumbnailDownloadUrl;
453
+ await ctx2.db.messages.put(removed);
454
+ released.push(row.localID);
455
+ }
456
+ if (!ids)
457
+ await ctx2.db.meta.put({ key: key ? `clear:${key}` : "clear:all", at: Date.now() });
458
+ if (ids && key)
459
+ for (const messageID of ids) {
460
+ if (!selected.some((row) => row.messageID === messageID))
461
+ await ctx2.db.messages.put({ localID: newLocalID(), conversationID: String(id), conversationType: String(type), conversationKey: key, messageID, hasServerID: 1, type: 0, timestamp: 0, sendState: 1, sentStatus: 1, deleted: true, updatedAt: Date.now() });
462
+ }
463
+ });
464
+ for (const localID of released)
465
+ invalidateMediaPreviews(localID);
466
+ changed(ctx2, key);
467
+ });
468
+ }
469
+ async function queryStoredMessages(query, failedOnly = false) {
470
+ const ctx = active;
471
+ if (!ctx)
472
+ throw new Error("Local message storage is not enabled; login with userId first");
473
+ await ctx.ready;
474
+ await ctx.pending;
475
+ if (!isCurrentStorage(ctx))
476
+ throw new Error("Local storage account changed");
477
+ const count = Math.min(500, Math.max(1, Math.floor(query.count || 30)));
478
+ const key = query.conversationID !== void 0 && query.conversationType !== void 0 ? conversationKey(query.conversationID, query.conversationType) : void 0;
479
+ let collection = key ? ctx.db.messages.where("[conversationKey+timestamp]").between([key, Dexie.minKey], [key, Dexie.maxKey]) : ctx.db.messages.orderBy("timestamp");
480
+ if (query.order !== "asc")
481
+ collection = collection.reverse();
482
+ const rows = await collection.filter((row) => {
483
+ if (row.deleted)
484
+ return false;
485
+ if (failedOnly && row.sendState !== 2 && !(row.sendState === 0 && row._deliveryUnconfirmed))
486
+ return false;
487
+ if (query.conversationID !== void 0 && row.conversationID !== String(query.conversationID))
488
+ return false;
489
+ if (query.conversationType !== void 0 && row.conversationType !== String(query.conversationType))
490
+ return false;
491
+ if (query.sendStates?.length && !query.sendStates.includes(row.sendState))
492
+ return false;
493
+ if (query.startTime !== void 0 && row.timestamp < query.startTime)
494
+ return false;
495
+ if (query.endTime !== void 0 && row.timestamp > query.endTime)
496
+ return false;
497
+ if (query.messageTypes?.length && !query.messageTypes.some((type) => String(type) === String(row.type)))
498
+ return false;
499
+ if (query.senderUserIDs?.length && !query.senderUserIDs.includes(row.senderUserID))
500
+ return false;
501
+ if (query.keywords?.some((word) => !String(row.message ?? row.massage ?? "").toLocaleLowerCase().includes(word.toLocaleLowerCase())))
502
+ return false;
503
+ if (query.before) {
504
+ const cmp = row.timestamp - query.before.timestamp || (row.localID < query.before.localID ? -1 : row.localID > query.before.localID ? 1 : 0);
505
+ if (query.order === "asc" ? cmp <= 0 : cmp >= 0)
506
+ return false;
507
+ }
508
+ return true;
509
+ }).limit(count + 1).toArray();
510
+ rows.sort((a, b) => (query.order === "asc" ? 1 : -1) * (a.timestamp - b.timestamp || (a.localID < b.localID ? -1 : a.localID > b.localID ? 1 : 0)));
511
+ if (!isCurrentStorage(ctx))
512
+ throw new Error("Local storage account changed");
513
+ const messageList = rows.slice(0, count);
514
+ const last = messageList[messageList.length - 1];
515
+ return { messageList, hasMore: rows.length > count, nextCursor: last ? { timestamp: last.timestamp, localID: last.localID } : void 0 };
516
+ }
517
+ function queryLocalMessages(query = {}) {
518
+ return queryStoredMessages(query);
519
+ }
520
+ function queryFailedMessages(query = {}) {
521
+ return queryStoredMessages({ ...query, sendStates: void 0 }, true);
522
+ }
523
+ async function clearLocalMessages(query = {}) {
524
+ if (!active)
525
+ throw new Error("Local message storage is not enabled");
526
+ await deleteStoredMessages(active, query.conversationID, query.conversationType);
527
+ }
528
+
529
+ // src/sdk/sessionGuard.ts
530
+ var generation2 = 0;
531
+ var authenticating = false;
532
+ var listeners2 = /* @__PURE__ */ new Set();
533
+ var authenticationWaiters = /* @__PURE__ */ new Set();
534
+ function releaseAuthenticationWaiters() {
535
+ for (const resolve of authenticationWaiters)
536
+ resolve();
537
+ authenticationWaiters.clear();
538
+ }
539
+ var captureSession = () => generation2;
540
+ var isCurrentSession = (value) => value === generation2;
541
+ var isAuthenticating = () => authenticating;
542
+ function invalidateSession() {
543
+ generation2++;
544
+ authenticating = false;
545
+ releaseAuthenticationWaiters();
546
+ for (const listener of listeners2)
547
+ listener();
548
+ return generation2;
549
+ }
550
+ function beginAuthentication() {
551
+ authenticating = true;
552
+ return generation2;
553
+ }
554
+ function finishAuthentication(value) {
555
+ if (isCurrentSession(value)) {
556
+ authenticating = false;
557
+ releaseAuthenticationWaiters();
558
+ }
559
+ }
560
+ async function waitForAuthentication(value) {
561
+ if (authenticating && isCurrentSession(value))
562
+ await new Promise((resolve) => authenticationWaiters.add(resolve));
563
+ assertCurrentSession(value);
564
+ }
565
+ function onSessionInvalidated(listener) {
566
+ listeners2.add(listener);
567
+ return () => {
568
+ listeners2.delete(listener);
569
+ };
570
+ }
571
+ function assertCurrentSession(value) {
572
+ if (!isCurrentSession(value))
573
+ throw new Error("SDK session changed; operation canceled");
574
+ }
575
+
576
+ // src/sdk/storage/bridge.ts
577
+ function extractMessages(data) {
578
+ if (!data)
579
+ return [];
580
+ if (Array.isArray(data))
581
+ return data.flatMap((item) => extractMessages(item));
582
+ if (typeof data !== "object")
583
+ return [];
584
+ if (Array.isArray(data.messageList))
585
+ return data.messageList;
586
+ if (Array.isArray(data.infos))
587
+ return data.infos.flatMap((info) => extractMessages(info));
588
+ if (data.message && typeof data.message === "object")
589
+ return [data.message];
590
+ if (data.messageID || data.clientBizId)
591
+ return [data];
592
+ return [];
593
+ }
594
+ function makeClientBizId() {
595
+ return `cbiz_${newLocalID()}`;
596
+ }
597
+ function outgoingFallback(payload, ctx) {
598
+ return {
599
+ conversationID: payload.toConversationID ?? payload.conversationID ?? payload.toOriginalMessage?.conversationID ?? payload.message?.conversationID,
600
+ conversationType: payload.conversationType ?? payload.toOriginalMessage?.conversationType ?? payload.message?.conversationType,
601
+ senderUserID: ctx?.userId,
602
+ direction: 0
603
+ };
604
+ }
605
+ async function persistOutgoing(ctx, payload) {
606
+ await saveMessages(ctx, [{ ...payload.message, sendState: 0, sentStatus: 0, _deliveryUnconfirmed: false }], outgoingFallback(payload, ctx), "local");
607
+ }
608
+ async function cacheApiResult(ctx, method, payload, data, startedAt = Date.now(), fetchSequence = ctx?.mutationSequence || 0) {
609
+ const fallback = { ...outgoingFallback(payload || {}, ctx), _fetchedAt: startedAt, _fetchSequence: fetchSequence };
610
+ if (!/^(sendMessage|sendMediaMessage|replyMessage)$/.test(method)) {
611
+ delete fallback.senderUserID;
612
+ delete fallback.direction;
613
+ }
614
+ if (/^(sendMessage|sendMediaMessage|replyMessage)$/.test(method)) {
615
+ const confirmation = { _sendSequence: nextMutation(ctx), _sendEpoch: ctx?.epoch };
616
+ const messages = extractMessages(data).map((row) => {
617
+ const sendState = messageSendState(row);
618
+ const normalized = { ...row, ...confirmation, sentStatus: row.sentStatus ?? sendState, sendState, clientBizId: row.clientBizId || payload.message?.clientBizId };
619
+ return normalized;
620
+ });
621
+ await saveMessages(ctx, messages.length ? messages : [{ ...payload.message, ...confirmation, sentStatus: 1, sendState: 1 }], fallback);
622
+ } else if (/^(queryHistoryMessage|queryMessages|queryMessageRepliedList|queryCombineMessageDetail|editMessage)$/.test(method)) {
623
+ await saveMessages(ctx, extractMessages(data).map((row) => ({
624
+ ...row,
625
+ _sendSequence: fetchSequence,
626
+ _sendEpoch: ctx?.epoch,
627
+ ...method === "editMessage" ? { _mutationAt: Date.now(), _contentSequence: nextMutation(ctx), _mutationEpoch: ctx?.epoch } : {}
628
+ })), { ...fallback, conversationID: data?.conversationID ?? fallback.conversationID, conversationType: data?.conversationType ?? fallback.conversationType });
629
+ } else if (method === "queryMessageReceiptsInfo") {
630
+ for (const row of extractMessages(data))
631
+ await applyMessagePatch(ctx, row, fallback);
632
+ } else if (method === "sendMessageReceiptsRead") {
633
+ for (const messageID of payload.messageList || [])
634
+ await applyMessagePatch(ctx, { messageID, locallyRead: true }, fallback);
635
+ } else if (method === "deleteMessages") {
636
+ await deleteStoredMessages(ctx, payload.conversationID, payload.conversationType, payload.messageList?.map((m) => m.messageID));
637
+ } else if (method === "deleteAllMessage") {
638
+ await deleteStoredMessages(ctx, payload.conversationID, payload.conversationType);
639
+ } else if (method === "deleteAllConversationMessages") {
640
+ await deleteStoredMessages(ctx);
641
+ } else if (method === "revokeMessage") {
642
+ await applyMessagePatch(ctx, { ...payload.message, revoked: true }, {});
643
+ }
644
+ }
645
+ function nextMutation(ctx) {
646
+ if (!ctx)
647
+ return 0;
648
+ return ctx.mutationSequence = (ctx.mutationSequence || 0) + 1;
649
+ }
650
+ function mergeDeferred(previous = {}, patch) {
651
+ const merged = { ...previous, ...patch };
652
+ if (previous.revoked || patch.revoked)
653
+ merged.revoked = true;
654
+ if (previous.deleted || patch.deleted)
655
+ merged.deleted = true;
656
+ const oldVersion = Number(previous.editedTime || previous.editedAt || 0);
657
+ const newVersion = Number(patch.editedTime || patch.editedAt || 0);
658
+ if (oldVersion > newVersion && newVersion > 0) {
659
+ for (const field of ["message", "massage", "editedTime", "editedAt", "extendedData"]) {
660
+ if (field in previous)
661
+ merged[field] = previous[field];
662
+ else
663
+ delete merged[field];
664
+ }
665
+ }
666
+ return merged;
667
+ }
668
+ async function applyMessagePatch(ctx, patch, fallback) {
669
+ if (!patch?.messageID && !patch?.clientBizId)
670
+ return;
671
+ patch = { ...patch, _mutationAt: Date.now(), _mutationEpoch: ctx?.epoch };
672
+ const sequence2 = nextMutation(ctx);
673
+ if ("message" in patch || "massage" in patch || "editedTime" in patch || "extendedData" in patch)
674
+ patch._contentSequence = sequence2;
675
+ if ("receiptStatus" in patch)
676
+ patch._receiptSequence = sequence2;
677
+ if ("sentStatus" in patch || "sendState" in patch) {
678
+ patch._sendSequence = sequence2;
679
+ patch._sendEpoch = ctx?.epoch;
680
+ }
681
+ if ((patch.conversationID ?? fallback.conversationID) !== void 0 && (patch.conversationType ?? fallback.conversationType) !== void 0) {
682
+ await saveMessages(ctx, [{ ...patch, _mutationAt: Date.now() }], fallback, "patch");
683
+ return;
684
+ }
685
+ await storageWork(ctx, async (ctx2) => {
686
+ const rows = await ctx2.db.messages.filter((row) => patch.messageID ? row.messageID === String(patch.messageID) : row.clientBizId === String(patch.clientBizId)).toArray();
687
+ if (!rows.length && patch.messageID) {
688
+ await ctx2.db.transaction("rw", ctx2.db.meta, async () => {
689
+ const key = `patch:${String(patch.messageID)}`;
690
+ const prior = await ctx2.db.meta.get(key);
691
+ await ctx2.db.meta.put({ key, patch: mergeDeferred(prior?.patch, patch) });
692
+ });
693
+ }
694
+ for (const row of rows)
695
+ await saveMessages(ctx2, [patch], { conversationID: row.conversationID, conversationType: row.conversationType }, "patch");
696
+ });
697
+ }
698
+ var PERSISTED_PUSH_EVENTS = /* @__PURE__ */ new Set(["receivePeerMessage", "receiveGroupMessage", "messageSentStatusChanged", "messageDeleted", "messageRevokeReceived", "messageEdited", "messageReceiptChanged"]);
699
+ var loginBuffer;
700
+ onSessionInvalidated(() => {
701
+ loginBuffer = void 0;
702
+ });
703
+ function beginLoginMessageBuffer(session) {
704
+ loginBuffer = { session, events: [] };
705
+ }
706
+ function discardLoginMessageBuffer(session) {
707
+ if (loginBuffer?.session === session)
708
+ loginBuffer = void 0;
709
+ }
710
+ async function flushLoginMessageBuffer(session) {
711
+ const buffer = loginBuffer;
712
+ if (buffer?.session !== session || !isCurrentSession(session))
713
+ return;
714
+ loginBuffer = void 0;
715
+ for (const { event, args } of buffer.events)
716
+ observeMessagePush(event, args);
717
+ await captureStorage()?.pending;
718
+ }
719
+ function snapshotPushData(value) {
720
+ if (typeof structuredClone === "function") {
721
+ try {
722
+ return structuredClone(value);
723
+ } catch {
724
+ }
725
+ }
726
+ if (Array.isArray(value))
727
+ return value.map(snapshotPushData);
728
+ if (value && Object.prototype.toString.call(value) === "[object Object]") {
729
+ return Object.fromEntries(Object.entries(value).filter(([, item]) => typeof item !== "function").map(([key, item]) => [key, snapshotPushData(item)]));
730
+ }
731
+ if (ArrayBuffer.isView(value)) {
732
+ const bytes = value.buffer.slice(value.byteOffset, value.byteOffset + value.byteLength);
733
+ return new value.constructor(bytes);
734
+ }
735
+ if (Object.prototype.toString.call(value) === "[object ArrayBuffer]")
736
+ return value.slice(0);
737
+ if (Object.prototype.toString.call(value) === "[object Date]")
738
+ return new Date(value.getTime());
739
+ return value;
740
+ }
741
+ function observeMessagePush(event, args) {
742
+ if (!PERSISTED_PUSH_EVENTS.has(event))
743
+ return;
744
+ if (loginBuffer && isCurrentSession(loginBuffer.session)) {
745
+ loginBuffer.events.push({ event, args: snapshotPushData(args.filter((arg) => typeof arg !== "function")) });
746
+ return;
747
+ }
748
+ const filtered = filterReplyPushArgs(event, args);
749
+ if (!filtered)
750
+ return;
751
+ args = filtered;
752
+ const ctx = captureStorage();
753
+ if (!ctx)
754
+ return;
755
+ const received = event === "receivePeerMessage" || event === "receiveGroupMessage";
756
+ const payload = args.find((arg) => arg && typeof arg === "object" && !Array.isArray(arg) && (arg.messageList || arg.message || arg.infos || arg.conversationID || arg.fromConversationID)) || args[0];
757
+ const data = snapshotPushData(payload);
758
+ const run = async () => {
759
+ const fallback = {
760
+ _fetchedAt: Date.now(),
761
+ _fetchSequence: ctx.mutationSequence || 0,
762
+ conversationID: data?.conversationID ?? data?.fromConversationID,
763
+ conversationType: data?.conversationType ?? (event === "receivePeerMessage" ? 0 : event === "receiveGroupMessage" ? 2 : void 0)
764
+ };
765
+ if (event === "messageDeleted") {
766
+ if (data?.isDeleteConversationAllMessage && fallback.conversationID !== void 0 && fallback.conversationType === void 0) {
767
+ for (const type of [0, 1, 2, 3])
768
+ await deleteStoredMessages(ctx, fallback.conversationID, type);
769
+ return;
770
+ }
771
+ if (fallback.conversationID === void 0 || fallback.conversationType === void 0) {
772
+ for (const message of extractMessages(data))
773
+ await applyMessagePatch(ctx, { ...message, deleted: true }, fallback);
774
+ return;
775
+ }
776
+ await deleteStoredMessages(
777
+ ctx,
778
+ fallback.conversationID,
779
+ fallback.conversationType,
780
+ data?.isDeleteConversationAllMessage ? void 0 : (data?.messageList || []).map((row) => row.messageID)
781
+ );
782
+ return;
783
+ }
784
+ const messages = extractMessages(data);
785
+ if (received) {
786
+ await saveMessages(ctx, messages.filter((message) => message && typeof message === "object" && (message.messageID || message.clientBizId)).map((message) => ({
787
+ ...message,
788
+ sentStatus: message.sentStatus ?? messageSendState(message),
789
+ sendState: messageSendState(message),
790
+ _sendSequence: nextMutation(ctx),
791
+ _sendEpoch: ctx.epoch
792
+ })), fallback, "receive");
793
+ } else {
794
+ for (const message of messages) {
795
+ const patch = { ...message };
796
+ if (event === "messageRevokeReceived")
797
+ patch.revoked = true;
798
+ if (event === "messageSentStatusChanged") {
799
+ const info = data?.infos?.find((item) => item.message?.messageID === message.messageID);
800
+ const status = info?.status ?? message.sentStatus;
801
+ if (status !== void 0) {
802
+ patch.sentStatus = status;
803
+ patch.sendState = Number(status) === 1 ? 1 : Number(status) === 2 ? 2 : 0;
804
+ }
805
+ }
806
+ await applyMessagePatch(ctx, patch, fallback);
807
+ }
808
+ }
809
+ };
810
+ ctx.pending = (ctx.pending || Promise.resolve()).then(run).catch((error) => {
811
+ console.error("[VanshiSdk] message persistence failed", error);
812
+ });
813
+ }
3
814
 
4
815
  // src/sdk/runtime.ts
5
816
  var SDK_TOKEN_KEY = "VanshiSdk_Token";
6
817
  var SDK_LOGIN_KEY = "VanshiSdk_isLogin";
818
+ var inMemorySdkToken = "";
819
+ var sessionTokenInitialized = false;
820
+ var inMemorySdkUserID = "";
821
+ function setSdkUserID(userID) {
822
+ inMemorySdkUserID = userID == null ? "" : String(userID).trim();
823
+ }
824
+ function getSdkUserID() {
825
+ return inMemorySdkUserID;
826
+ }
7
827
  function getBrowserStorage() {
8
- if (typeof localStorage === "undefined") {
828
+ try {
829
+ if (typeof localStorage === "undefined") {
830
+ return void 0;
831
+ }
832
+ return localStorage;
833
+ } catch {
9
834
  return void 0;
10
835
  }
11
- return localStorage;
836
+ }
837
+ function getSdkErrorMessage(result) {
838
+ return result.message || `SDK request failed${result.code !== void 0 ? `: ${String(result.code)}` : ""}`;
12
839
  }
13
840
  var SdkRuntimeError = class extends Error {
14
841
  constructor(result) {
15
- super(result.message || `SDK request failed${result.code !== void 0 ? `: ${String(result.code)}` : ""}`);
842
+ super(getSdkErrorMessage(result));
16
843
  this.name = "SdkRuntimeError";
844
+ this.success = false;
17
845
  this.code = result.code;
18
846
  this.data = result.data;
19
847
  this.raw = result.raw;
20
- this.parsed = result.parsed;
848
+ Object.defineProperties(this, {
849
+ name: { value: this.name, enumerable: true },
850
+ message: { value: this.message, enumerable: true },
851
+ success: { value: this.success, enumerable: true },
852
+ code: { value: this.code, enumerable: true },
853
+ data: { value: this.data, enumerable: true },
854
+ raw: { value: this.raw, enumerable: true }
855
+ });
856
+ }
857
+ toObject() {
858
+ return {
859
+ name: "SdkRuntimeError",
860
+ success: false,
861
+ code: this.code,
862
+ message: this.message,
863
+ data: this.data,
864
+ raw: this.raw
865
+ };
866
+ }
867
+ toJSON() {
868
+ return this.toObject();
21
869
  }
22
870
  };
23
- function parseJsonSafely(value) {
24
- if (value == null) {
25
- return void 0;
871
+ function isRecord(value) {
872
+ return Boolean(value) && typeof value === "object";
873
+ }
874
+ function getErrorField(error, field) {
875
+ return isRecord(error) ? error[field] : void 0;
876
+ }
877
+ function getSdkErrorCode(error) {
878
+ const code = getErrorField(error, "code");
879
+ return typeof code === "string" || typeof code === "number" ? code : void 0;
880
+ }
881
+ function getSdkErrorMessageFromUnknown(error, fallback = "SDK request failed") {
882
+ const message = getErrorField(error, "message");
883
+ if (typeof message === "string" && message) {
884
+ return message;
26
885
  }
27
- if (typeof value !== "string") {
28
- return value;
886
+ if (error instanceof Error && error.message) {
887
+ return error.message;
29
888
  }
30
- try {
31
- return JSON.parse(value);
32
- } catch {
33
- return void 0;
889
+ if (typeof error === "string" && error) {
890
+ return error;
34
891
  }
892
+ return fallback;
35
893
  }
36
- function isSuccessCode(code) {
37
- return code === 0 || code === "0" || code === true;
894
+ function isSdkRuntimeErrorObject(value) {
895
+ return isRecord(value) && value.name === "SdkRuntimeError" && value.success === false && typeof value.message === "string";
38
896
  }
39
- function toSdkEnvelope(response) {
40
- if (response && typeof response === "object") {
41
- return response;
897
+ function isAuthFailureCode(code) {
898
+ return code === 3 || code === "3" || code === 4 || code === "4" || code === 303 || code === "303";
899
+ }
900
+ function hasSdkCode(value) {
901
+ return isRecord(value) && "code" in value;
902
+ }
903
+ function logSdkRuntimeError(result) {
904
+ if (typeof console === "undefined" || typeof console.error !== "function") {
905
+ return;
906
+ }
907
+ console.error("[VanshiSdk] API request failed", {
908
+ code: result.code,
909
+ message: result.message,
910
+ data: result.data,
911
+ raw: result.raw
912
+ });
913
+ }
914
+ function throwSdkRuntimeError(result) {
915
+ const detailedResult = {
916
+ ...result,
917
+ success: false
918
+ };
919
+ logSdkRuntimeError(detailedResult);
920
+ if (isAuthFailureCode(detailedResult.code)) {
921
+ clearSdkSession();
922
+ }
923
+ throw new SdkRuntimeError(detailedResult).toObject();
924
+ }
925
+ function throwSdkTransportError(error, fallbackMessage = "SDK request failed") {
926
+ if (isSdkRuntimeErrorObject(error)) {
927
+ throw error;
42
928
  }
43
- return {};
929
+ throwSdkRuntimeError({
930
+ code: getSdkErrorCode(error),
931
+ message: getSdkErrorMessageFromUnknown(error, fallbackMessage),
932
+ raw: error
933
+ });
934
+ }
935
+ function isSuccessCode(code) {
936
+ return code === 0 || code === "0" || code === true;
44
937
  }
45
938
  function unwrapSdkData(response) {
939
+ const result = assertSdkSuccess(response);
940
+ return result.data;
941
+ }
942
+ function assertSdkSuccess(response) {
46
943
  const result = unwrapSdkDetailedData(response);
47
944
  if (!result.success) {
48
- throw new SdkRuntimeError(result);
945
+ throwSdkRuntimeError(result);
49
946
  }
50
- return result.data;
947
+ return result;
51
948
  }
52
949
  function unwrapSdkResponse(response) {
950
+ const result = assertSdkSuccess(response);
53
951
  const envelope = toSdkEnvelope(response);
54
- const parsed = parseJsonSafely(envelope.data);
55
- if (parsed && typeof parsed === "object" && "code" in parsed) {
56
- return parsed;
57
- }
58
952
  return {
59
953
  code: envelope.code,
60
954
  message: envelope.message,
61
- data: parsed !== void 0 ? parsed : envelope.data
955
+ data: result.data
62
956
  };
63
957
  }
64
958
  function unwrapSdkDetailedData(response) {
65
959
  const envelope = toSdkEnvelope(response);
66
- const parsed = parseJsonSafely(envelope.data);
67
- if (parsed && typeof parsed === "object" && "code" in parsed) {
68
- const success = isSuccessCode(parsed.code);
69
- return {
70
- success,
71
- code: parsed.code,
72
- message: parsed.message || envelope.message,
73
- data: parsed.data,
74
- raw: response,
75
- parsed
76
- };
77
- }
78
960
  if (envelope.code !== void 0 || envelope.message !== void 0) {
79
961
  return {
80
962
  success: isSuccessCode(envelope.code),
81
963
  code: envelope.code,
82
964
  message: envelope.message,
83
- data: parsed !== void 0 ? parsed : envelope.data,
84
- raw: response,
85
- parsed
965
+ data: envelope.data,
966
+ raw: response
86
967
  };
87
968
  }
88
969
  return {
89
970
  success: true,
90
- data: parsed !== void 0 ? parsed : envelope.data,
91
- raw: response,
92
- parsed
971
+ data: envelope.data,
972
+ raw: response
93
973
  };
94
974
  }
975
+ function toSdkEnvelope(response) {
976
+ return isRecord(response) ? response : {};
977
+ }
95
978
  function getStoredToken() {
96
- return getBrowserStorage()?.getItem(SDK_TOKEN_KEY) || "";
979
+ const memoryToken = inMemorySdkToken;
980
+ try {
981
+ return sessionTokenInitialized ? memoryToken : getBrowserStorage()?.getItem(SDK_TOKEN_KEY) || "";
982
+ } catch {
983
+ return memoryToken;
984
+ }
97
985
  }
98
986
  function setSdkSession(token) {
99
- const storage = getBrowserStorage();
100
- if (!storage) {
101
- return;
987
+ inMemorySdkToken = token;
988
+ sessionTokenInitialized = true;
989
+ try {
990
+ const storage = getBrowserStorage();
991
+ if (!storage) {
992
+ return;
993
+ }
994
+ storage.setItem(SDK_TOKEN_KEY, token);
995
+ storage.setItem(SDK_LOGIN_KEY, "true");
996
+ } catch {
102
997
  }
103
- storage.setItem(SDK_TOKEN_KEY, token);
104
- storage.setItem(SDK_LOGIN_KEY, "true");
105
998
  }
106
999
  function clearSdkSession() {
107
- const storage = getBrowserStorage();
108
- if (!storage) {
109
- return;
1000
+ const ownedToken = inMemorySdkToken;
1001
+ const hadOwnedSession = sessionTokenInitialized;
1002
+ sessionTokenInitialized = true;
1003
+ invalidateSession();
1004
+ closeLocalStorage();
1005
+ inMemorySdkToken = "";
1006
+ inMemorySdkUserID = "";
1007
+ try {
1008
+ const storage = getBrowserStorage();
1009
+ if (!storage) {
1010
+ return;
1011
+ }
1012
+ if (!hadOwnedSession || ownedToken && storage.getItem(SDK_TOKEN_KEY) === ownedToken) {
1013
+ storage.removeItem(SDK_TOKEN_KEY);
1014
+ storage.removeItem(SDK_LOGIN_KEY);
1015
+ }
1016
+ } catch {
110
1017
  }
111
- storage.removeItem(SDK_TOKEN_KEY);
112
- storage.removeItem(SDK_LOGIN_KEY);
113
1018
  }
114
1019
  function getSocketUrl() {
115
1020
  const globalUrl = typeof window !== "undefined" ? window.__VANSHI_SOCKET_URL : void 0;
116
- return globalUrl || "wss://ws-vanshi-chat-sdk.xchatclub.com";
1021
+ return globalUrl || "wss://ws-baobaoshibu.bnrfdv.com";
117
1022
  }
118
1023
  function getUploadUrl() {
119
1024
  const globalHttpUrl = typeof window !== "undefined" ? window.__VANSHI_HTTP_URL : void 0;
120
- return globalHttpUrl || "https://vanshi-chat-api.xchatclub.com/api/files/sdkUpload";
1025
+ return globalHttpUrl || "https://baobaoshibu.bnrfdv.com/api/files/sdkUpload";
121
1026
  }
122
1027
 
1028
+ // src/plugins/socketio/client.ts
1029
+ import io from "socket.io-client";
1030
+
123
1031
  // src/sdk/session.ts
124
1032
  var reconnectLoginHandler = null;
125
1033
  function registerReconnectLoginHandler(handler) {
126
1034
  reconnectLoginHandler = handler;
127
1035
  }
128
1036
  async function restoreSessionAfterReconnect() {
1037
+ const session = captureSession();
129
1038
  if (!reconnectLoginHandler) {
130
1039
  return false;
131
1040
  }
@@ -134,7 +1043,8 @@ async function restoreSessionAfterReconnect() {
134
1043
  return false;
135
1044
  }
136
1045
  try {
137
- return await reconnectLoginHandler(token);
1046
+ const success = await reconnectLoginHandler(token);
1047
+ return isCurrentSession(session) && success;
138
1048
  } catch {
139
1049
  return false;
140
1050
  }
@@ -143,7 +1053,99 @@ function clearSessionState() {
143
1053
  clearSdkSession();
144
1054
  }
145
1055
 
1056
+ // src/constants/eventListenerOn.ts
1057
+ var ZIMConnectionState = /* @__PURE__ */ ((ZIMConnectionState2) => {
1058
+ ZIMConnectionState2[ZIMConnectionState2["Disconnected"] = 0] = "Disconnected";
1059
+ ZIMConnectionState2[ZIMConnectionState2["Connecting"] = 1] = "Connecting";
1060
+ ZIMConnectionState2[ZIMConnectionState2["Connected"] = 2] = "Connected";
1061
+ ZIMConnectionState2[ZIMConnectionState2["Reconnecting"] = 3] = "Reconnecting";
1062
+ return ZIMConnectionState2;
1063
+ })(ZIMConnectionState || {});
1064
+ var ZIMConnectionEvent = /* @__PURE__ */ ((ZIMConnectionEvent2) => {
1065
+ ZIMConnectionEvent2[ZIMConnectionEvent2["Success"] = 0] = "Success";
1066
+ ZIMConnectionEvent2[ZIMConnectionEvent2["ActiveLogin"] = 1] = "ActiveLogin";
1067
+ ZIMConnectionEvent2[ZIMConnectionEvent2["LoginTimeout"] = 2] = "LoginTimeout";
1068
+ ZIMConnectionEvent2[ZIMConnectionEvent2["LoginInterrupted"] = 3] = "LoginInterrupted";
1069
+ ZIMConnectionEvent2[ZIMConnectionEvent2["KickedOut"] = 4] = "KickedOut";
1070
+ ZIMConnectionEvent2[ZIMConnectionEvent2["TokenExpired"] = 5] = "TokenExpired";
1071
+ return ZIMConnectionEvent2;
1072
+ })(ZIMConnectionEvent || {});
1073
+ var ZIMEventEnum = /* @__PURE__ */ ((ZIMEventEnum2) => {
1074
+ ZIMEventEnum2["connect"] = "connect";
1075
+ ZIMEventEnum2["disconnect"] = "disconnect";
1076
+ ZIMEventEnum2["connectError"] = "connect_error";
1077
+ ZIMEventEnum2["connectionStateChanged"] = "connectionStateChanged";
1078
+ ZIMEventEnum2["reconnect"] = "reconnect";
1079
+ ZIMEventEnum2["reconnectAttempt"] = "reconnect_attempt";
1080
+ ZIMEventEnum2["reconnectError"] = "reconnect_error";
1081
+ ZIMEventEnum2["reconnectFailed"] = "reconnect_failed";
1082
+ ZIMEventEnum2["reconnectLoginSuccess"] = "reconnectLoginSuccess";
1083
+ ZIMEventEnum2["reconnectLoginFailed"] = "reconnectLoginFailed";
1084
+ ZIMEventEnum2["receiveGroupMessage"] = "receiveGroupMessage";
1085
+ ZIMEventEnum2["receivePeerMessage"] = "receivePeerMessage";
1086
+ ZIMEventEnum2["sendSingLing"] = "sendSingLing";
1087
+ ZIMEventEnum2["conversationChanged"] = "conversationChanged";
1088
+ ZIMEventEnum2["messageSentStatusChanged"] = "messageSentStatusChanged";
1089
+ ZIMEventEnum2["messageDeleted"] = "messageDeleted";
1090
+ ZIMEventEnum2["messageRevokeReceived"] = "messageRevokeReceived";
1091
+ ZIMEventEnum2["messageEdited"] = "messageEdited";
1092
+ ZIMEventEnum2["messageReceiptChanged"] = "messageReceiptChanged";
1093
+ ZIMEventEnum2["tokenWillExpire"] = "tokenWillExpire";
1094
+ ZIMEventEnum2["error"] = "error";
1095
+ ZIMEventEnum2["userInfoUpdated"] = "userInfoUpdated";
1096
+ ZIMEventEnum2["userStatusUpdated"] = "userStatusUpdated";
1097
+ ZIMEventEnum2["groupStateChanged"] = "groupStateChanged";
1098
+ ZIMEventEnum2["friendListChanged"] = "friendListChanged";
1099
+ ZIMEventEnum2["blacklistChanged"] = "blacklistChanged";
1100
+ ZIMEventEnum2["callInvitationReceived"] = "callInvitationReceived";
1101
+ ZIMEventEnum2["callAccepted"] = "callAccepted";
1102
+ ZIMEventEnum2["callRejected"] = "callRejected";
1103
+ ZIMEventEnum2["callCanceled"] = "callCanceled";
1104
+ ZIMEventEnum2["callEnded"] = "callEnded";
1105
+ ZIMEventEnum2["callTimeout"] = "callTimeout";
1106
+ ZIMEventEnum2["callParticipantJoined"] = "callParticipantJoined";
1107
+ ZIMEventEnum2["callParticipantLeft"] = "callParticipantLeft";
1108
+ ZIMEventEnum2["callMediaStateChanged"] = "callMediaStateChanged";
1109
+ ZIMEventEnum2["callConnectionStateChanged"] = "callConnectionStateChanged";
1110
+ ZIMEventEnum2["callMediaTrackSubscribed"] = "callMediaTrackSubscribed";
1111
+ ZIMEventEnum2["callMediaTrackUnsubscribed"] = "callMediaTrackUnsubscribed";
1112
+ return ZIMEventEnum2;
1113
+ })(ZIMEventEnum || {});
1114
+
146
1115
  // src/plugins/socketio/client.ts
1116
+ var SOCKET_NAMESPACE = "/im/user";
1117
+ var DEFAULT_RECONNECT_DELAY_MS = 1e4;
1118
+ var DEFAULT_CONNECT_TIMEOUT_MS = 6e4;
1119
+ var RECONNECT_LOGIN_DELAY_MS = 200;
1120
+ var HEARTBEAT_INTERVAL_MS = 3e4;
1121
+ var HEARTBEAT_TIMEOUT_MS = 9e4;
1122
+ var HEARTBEAT_PACKET_PREFIX = "ping:";
1123
+ var MAX_OPERATION_QUEUE_SIZE = 1e3;
1124
+ var CALL_PUSH_EVENT = "push:call";
1125
+ var AUTO_ACK_EVENTS = /* @__PURE__ */ new Set(["conversationChanged", "receivePeerMessage", "receiveGroupMessage"]);
1126
+ var CALL_PUSH_EVENTS = /* @__PURE__ */ new Set([
1127
+ "callInvitationReceived",
1128
+ "callAccepted",
1129
+ "callRejected",
1130
+ "callCanceled",
1131
+ "callEnded",
1132
+ "callTimeout",
1133
+ "callParticipantJoined",
1134
+ "callParticipantLeft",
1135
+ "callMediaStateChanged"
1136
+ ]);
1137
+ var LOCAL_CONNECTION_EVENTS = /* @__PURE__ */ new Set([
1138
+ "connect",
1139
+ "disconnect",
1140
+ "connect_error",
1141
+ "connectionStateChanged",
1142
+ "reconnect",
1143
+ "reconnect_attempt",
1144
+ "reconnect_error",
1145
+ "reconnect_failed",
1146
+ "reconnectLoginSuccess",
1147
+ "reconnectLoginFailed"
1148
+ ]);
147
1149
  function parseSocketConnectErrorPayload(value) {
148
1150
  if (!value) {
149
1151
  return void 0;
@@ -163,27 +1165,71 @@ function parseSocketConnectErrorPayload(value) {
163
1165
  }
164
1166
  return void 0;
165
1167
  }
166
- function normalizeSocketConnectError(error) {
1168
+ function getErrorRecord(error) {
1169
+ return error && typeof error === "object" ? error : void 0;
1170
+ }
1171
+ function getSocketConnectErrorDetails(error, url) {
1172
+ const record = getErrorRecord(error);
1173
+ const message = typeof record?.message === "string" && record.message ? record.message : typeof error === "string" && error ? error : "";
1174
+ return {
1175
+ message,
1176
+ type: typeof record?.type === "string" ? record.type : void 0,
1177
+ description: record?.description,
1178
+ transport: typeof record?.transport === "string" ? record.transport : void 0,
1179
+ url
1180
+ };
1181
+ }
1182
+ function normalizeSocketConnectError(error, url) {
167
1183
  const rawMessage = error && typeof error === "object" && "message" in error ? error.message : void 0;
168
1184
  const rawData = error && typeof error === "object" && "data" in error ? error.data : void 0;
169
1185
  const payload = parseSocketConnectErrorPayload(rawData) || parseSocketConnectErrorPayload(rawMessage);
1186
+ const details = getSocketConnectErrorDetails(error, url);
170
1187
  const normalized = new Error(
171
- payload?.message || (typeof rawMessage === "string" ? rawMessage : "") || "socket connect failed"
1188
+ payload?.message || details.message || (details.type ? `socket connect failed: ${details.type}` : "") || (typeof rawMessage === "string" ? rawMessage : "") || "socket connect failed"
172
1189
  );
173
1190
  normalized.name = "SocketConnectError";
174
1191
  normalized.code = payload?.code;
175
1192
  normalized.data = payload;
1193
+ normalized.details = details;
176
1194
  normalized.requestId = payload?.requestId;
177
1195
  normalized.raw = error;
178
1196
  return normalized;
179
1197
  }
1198
+ function debugSocketLog(...args) {
1199
+ if (typeof window !== "undefined" && window.__VANSHI_SDK_DEBUG__) {
1200
+ console.log(...args);
1201
+ }
1202
+ }
1203
+ function normalizeTimeoutMs(timeoutMs, fallback) {
1204
+ return typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0 ? timeoutMs : fallback;
1205
+ }
180
1206
  var SocketClient = class {
181
1207
  constructor() {
182
1208
  this.pending = /* @__PURE__ */ new Map();
183
- this.emitQueue = [];
1209
+ this.pendingAcks = /* @__PURE__ */ new Map();
1210
+ this.eventHandlers = /* @__PURE__ */ new Map();
1211
+ this.operationQueue = [];
1212
+ this.operationQueueProcessing = false;
1213
+ this.operationQueueRunId = 0;
184
1214
  this.connectPromise = null;
185
- this.url = "";
1215
+ this.connectReject = null;
1216
+ this.intentionallyDisconnected = false;
1217
+ this.latestReconnectAttempt = 0;
1218
+ this.reconnectRestorePromise = null;
1219
+ this.reconnectRestoreGeneration = 0;
1220
+ this.reconnecting = false;
1221
+ this.restoringSession = false;
1222
+ this.hasEstablishedConnection = false;
1223
+ this.heartbeatSequence = 0;
1224
+ this.connectionStatePayload = {
1225
+ state: 0 /* Disconnected */,
1226
+ event: 3 /* LoginInterrupted */,
1227
+ extendedData: ""
1228
+ };
1229
+ this.socketHandlers = /* @__PURE__ */ new WeakMap();
186
1230
  this.handleResponse = (res) => {
1231
+ if (!res || typeof res.id !== "string")
1232
+ return;
187
1233
  const entry = this.pending.get(res.id);
188
1234
  if (!entry)
189
1235
  return;
@@ -191,141 +1237,492 @@ var SocketClient = class {
191
1237
  this.pending.delete(res.id);
192
1238
  res.error ? entry.reject(res.error) : entry.resolve(res.payload);
193
1239
  };
1240
+ this.handleCallPush = (message, ackCallback) => {
1241
+ const valid = typeof message?.event === "string" && message.event.length > 0 && message.payload !== null && typeof message.payload === "object" && !Array.isArray(message.payload);
1242
+ if (typeof ackCallback === "function") {
1243
+ const ack = {
1244
+ success: valid,
1245
+ receivedAt: Date.now(),
1246
+ platform: "web",
1247
+ uuids: [message?.wsId],
1248
+ appId: message?.appid,
1249
+ userId: message?.userId,
1250
+ ...valid ? {
1251
+ event: message.event,
1252
+ ...typeof message.payload.callID === "string" ? { callId: message.payload.callID } : {}
1253
+ } : { error: "Invalid data format" }
1254
+ };
1255
+ try {
1256
+ ackCallback(ack);
1257
+ } catch (error) {
1258
+ console.error("[SocketClient] call push ACK failed:", error);
1259
+ }
1260
+ }
1261
+ if (!valid)
1262
+ return;
1263
+ const handlers = this.eventHandlers.get(message.event) || /* @__PURE__ */ new Set();
1264
+ handlers.forEach((handler) => {
1265
+ try {
1266
+ this.invokeHandler(message.event, handler, [message.payload]);
1267
+ } catch (err) {
1268
+ console.error("[SocketClient] call event handler failed:", message.event, err);
1269
+ }
1270
+ });
1271
+ };
1272
+ }
1273
+ watchSession() {
1274
+ if (!this.removeSessionListener)
1275
+ this.removeSessionListener = onSessionInvalidated(() => {
1276
+ this.reconnectRestoreGeneration++;
1277
+ this.reconnectRestorePromise = null;
1278
+ this.restoringSession = false;
1279
+ this.invalidateOperationQueueProcessor();
1280
+ this.cleanupAllPending(new Error("SDK session changed; operation canceled"), true);
1281
+ });
194
1282
  }
195
1283
  connect(url, auth, opts = {}) {
1284
+ this.watchSession();
196
1285
  if (this.connectPromise)
197
1286
  return this.connectPromise;
198
1287
  if (this.isConnected())
199
1288
  return Promise.resolve();
1289
+ if (this.socket && !this.intentionallyDisconnected && (this.hasEstablishedConnection || this.reconnecting || this.restoringSession)) {
1290
+ return this.waitForSocketConnected(
1291
+ normalizeTimeoutMs(opts.timeout, DEFAULT_CONNECT_TIMEOUT_MS)
1292
+ );
1293
+ }
1294
+ this.intentionallyDisconnected = false;
1295
+ this.latestReconnectAttempt = 0;
1296
+ this.reconnecting = false;
1297
+ this.restoringSession = false;
1298
+ this.hasEstablishedConnection = false;
1299
+ this.reconnectRestoreGeneration += 1;
1300
+ this.reconnectRestorePromise = null;
1301
+ this.invalidateOperationQueueProcessor();
1302
+ this.stopHeartbeat();
1303
+ this.cleanupAllPending(new Error("socket reinitialized"), true);
200
1304
  if (this.socket) {
1305
+ const previousSocket = this.socket;
1306
+ this.socket = void 0;
201
1307
  try {
202
- this.socket.off();
203
- this.socket.io.off();
204
- this.socket = void 0;
1308
+ previousSocket.off();
1309
+ previousSocket.io.off();
1310
+ previousSocket.disconnect();
205
1311
  } catch {
206
1312
  }
207
1313
  }
208
1314
  if (this.isConnected())
209
1315
  return Promise.resolve();
210
1316
  const { baseUrl, namespace } = this.parseUrl(url);
211
- this.url = url;
1317
+ const connectTimeoutMs = normalizeTimeoutMs(
1318
+ opts.timeout,
1319
+ DEFAULT_CONNECT_TIMEOUT_MS
1320
+ );
212
1321
  const ioOpts = {
213
1322
  ...opts,
1323
+ auth,
214
1324
  transports: ["websocket"],
215
- // 🔥 只保留 websocket,不会多条连接
216
1325
  query: { namespace },
217
1326
  forceNew: true,
218
1327
  reconnection: true,
219
1328
  reconnectionAttempts: 9999,
220
- reconnectionDelay: 1e4,
221
- timeout: 15e3
1329
+ reconnectionDelay: DEFAULT_RECONNECT_DELAY_MS,
1330
+ timeout: connectTimeoutMs
222
1331
  };
223
1332
  this.connectPromise = new Promise((resolve, reject) => {
224
- const socket = io(`${baseUrl}/im/user`, ioOpts);
1333
+ this.connectReject = reject;
1334
+ const socket = io(`${baseUrl}${SOCKET_NAMESPACE}`, ioOpts);
225
1335
  this.socket = socket;
226
- console.log("[SocketClient] \u65B0\u8FDE\u63A5\u521B\u5EFA\uFF1A", `${baseUrl}/im/user`);
227
- const handleConnect = () => {
228
- socket.off("connect_error", handleError);
229
- socket.on("api:response", this.handleResponse);
230
- this.processEmitQueue();
231
- resolve();
232
- };
233
- const handleError = (err) => {
234
- const normalizedError = normalizeSocketConnectError(err);
235
- console.error("[SocketClient] \u8FDE\u63A5\u5931\u8D25\uFF1A:", normalizedError);
236
- socket.off("connect", handleConnect);
237
- reject(normalizedError);
238
- };
239
- socket.once("connect", handleConnect);
240
- socket.once("connect_error", handleError);
241
- socket.on("disconnect", (reason) => {
242
- this.cleanupAllPending("disconnected: " + reason);
243
- this.connectPromise = null;
1336
+ socket.onAny((event, ...args) => {
1337
+ if (this.socket !== socket)
1338
+ return;
1339
+ if (AUTO_ACK_EVENTS.has(event))
1340
+ this.ackPush(socket, event, args);
1341
+ observeMessagePush(event, args);
244
1342
  });
245
- socket.io.off("reconnect");
246
- socket.io.on("reconnect", async () => {
247
- console.log("[SocketClient] \u91CD\u8FDE\u6210\u529F \u2192 \u7B49\u5F85 socket \u5B8C\u5168\u5C31\u7EEA", this.socket?.connected);
248
- await new Promise(
249
- (resolveReconnect) => setTimeout(resolveReconnect, 200)
1343
+ this.emitConnectionStateChanged(
1344
+ 1 /* Connecting */,
1345
+ 1 /* ActiveLogin */
1346
+ );
1347
+ debugSocketLog(
1348
+ "[SocketClient] connected target:",
1349
+ `${baseUrl}${SOCKET_NAMESPACE}`
1350
+ );
1351
+ let isInitialConnect = true;
1352
+ let initialConnectSettled = false;
1353
+ const handleConnect = () => {
1354
+ if (this.socket !== socket) {
1355
+ return;
1356
+ }
1357
+ if (isInitialConnect) {
1358
+ this.hasEstablishedConnection = true;
1359
+ this.reconnecting = false;
1360
+ this.restoringSession = false;
1361
+ socket.on("api:response", this.handleResponse);
1362
+ socket.on(CALL_PUSH_EVENT, this.handleCallPush);
1363
+ this.bindEventHandlers(socket);
1364
+ this.processOperationQueue();
1365
+ isInitialConnect = false;
1366
+ if (!initialConnectSettled) {
1367
+ initialConnectSettled = true;
1368
+ resolve();
1369
+ }
1370
+ } else {
1371
+ this.reconnecting = false;
1372
+ this.restoringSession = true;
1373
+ this.emitLocalEvent("reconnect", this.latestReconnectAttempt);
1374
+ const generation3 = ++this.reconnectRestoreGeneration;
1375
+ void this.restoreAfterReconnect(generation3);
1376
+ }
1377
+ this.startHeartbeat(socket);
1378
+ this.emitLocalEvent("connect");
1379
+ this.emitConnectionStateChanged(
1380
+ 2 /* Connected */,
1381
+ 0 /* Success */
250
1382
  );
251
- if (!this.isConnected() || !this.socket) {
252
- console.log("[SocketClient] socket \u672A\u5C31\u7EEA\uFF0C\u8DF3\u8FC7\u767B\u5F55");
1383
+ };
1384
+ const handleError = (err) => {
1385
+ if (this.socket !== socket) {
253
1386
  return;
254
1387
  }
255
- try {
256
- console.log("[SocketClient] \u5F00\u59CB\u81EA\u52A8\u767B\u5F55 \u2705");
257
- const res = await restoreSessionAfterReconnect();
258
- if (res) {
259
- this.processEmitQueue();
260
- } else {
261
- clearSessionState();
1388
+ const normalizedError = normalizeSocketConnectError(
1389
+ err,
1390
+ `${baseUrl}${SOCKET_NAMESPACE}`
1391
+ );
1392
+ console.error("[SocketClient] connect failed:", normalizedError);
1393
+ this.emitLocalEvent("connect_error", normalizedError);
1394
+ this.emitConnectionStateChanged(
1395
+ 0 /* Disconnected */,
1396
+ 2 /* LoginTimeout */,
1397
+ normalizedError.message
1398
+ );
1399
+ if (isInitialConnect && !initialConnectSettled) {
1400
+ initialConnectSettled = true;
1401
+ this.connectReject = null;
1402
+ this.rejectQueuedOperations(normalizedError);
1403
+ try {
1404
+ socket.io.opts.reconnection = false;
1405
+ socket.disconnect();
1406
+ } catch {
262
1407
  }
263
- } catch (err) {
264
- console.error("[SocketClient] \u81EA\u52A8\u767B\u5F55\u5931\u8D25", err);
265
- clearSessionState();
1408
+ reject(normalizedError);
1409
+ }
1410
+ };
1411
+ socket.on("connect", handleConnect);
1412
+ socket.on("connect_error", handleError);
1413
+ socket.on("disconnect", (reason) => {
1414
+ if (this.socket !== socket) {
1415
+ return;
1416
+ }
1417
+ const hadEstablishedConnection = this.hasEstablishedConnection;
1418
+ const shouldReconnect = !this.intentionallyDisconnected && hadEstablishedConnection;
1419
+ const disconnectError = new Error("disconnected: " + reason);
1420
+ this.stopHeartbeat();
1421
+ this.reconnectRestoreGeneration += 1;
1422
+ this.reconnectRestorePromise = null;
1423
+ this.restoringSession = false;
1424
+ this.reconnecting = shouldReconnect;
1425
+ this.cleanupAllPending(disconnectError, !shouldReconnect);
1426
+ if (!hadEstablishedConnection && !initialConnectSettled) {
1427
+ initialConnectSettled = true;
1428
+ this.connectReject?.(disconnectError);
1429
+ this.connectReject = null;
1430
+ this.rejectQueuedOperations(disconnectError);
1431
+ }
1432
+ this.emitLocalEvent("disconnect", reason);
1433
+ this.emitConnectionStateChanged(
1434
+ 0 /* Disconnected */,
1435
+ this.getDisconnectEvent(reason),
1436
+ String(reason || "")
1437
+ );
1438
+ if (reason === "io server disconnect" && !this.intentionallyDisconnected) {
1439
+ this.latestReconnectAttempt += 1;
1440
+ this.emitLocalEvent("reconnect_attempt", this.latestReconnectAttempt);
1441
+ this.emitConnectionStateChanged(
1442
+ 3 /* Reconnecting */,
1443
+ 3 /* LoginInterrupted */,
1444
+ String(this.latestReconnectAttempt)
1445
+ );
1446
+ socket.connect();
266
1447
  }
267
1448
  });
1449
+ socket.io.off("reconnect");
1450
+ socket.io.off("reconnect_attempt");
1451
+ socket.io.off("reconnect_error");
1452
+ socket.io.off("reconnect_failed");
1453
+ socket.io.on("reconnect_attempt", (attempt) => {
1454
+ if (this.socket !== socket) {
1455
+ return;
1456
+ }
1457
+ this.latestReconnectAttempt = attempt;
1458
+ this.emitLocalEvent("reconnect_attempt", attempt);
1459
+ this.emitConnectionStateChanged(
1460
+ 3 /* Reconnecting */,
1461
+ 3 /* LoginInterrupted */,
1462
+ String(attempt)
1463
+ );
1464
+ });
1465
+ socket.io.on("reconnect_error", (err) => {
1466
+ if (this.socket !== socket) {
1467
+ return;
1468
+ }
1469
+ this.emitLocalEvent("reconnect_error", err);
1470
+ });
1471
+ socket.io.on("reconnect_failed", () => {
1472
+ if (this.socket !== socket) {
1473
+ return;
1474
+ }
1475
+ this.reconnecting = false;
1476
+ this.restoringSession = false;
1477
+ this.hasEstablishedConnection = false;
1478
+ this.rejectQueuedOperations(new Error("reconnect failed"));
1479
+ this.emitConnectionStateChanged(
1480
+ 0 /* Disconnected */,
1481
+ 3 /* LoginInterrupted */,
1482
+ "reconnect failed"
1483
+ );
1484
+ this.emitLocalEvent("reconnect_failed");
1485
+ });
1486
+ socket.io.on("reconnect", (attempt) => {
1487
+ if (this.socket !== socket) {
1488
+ return;
1489
+ }
1490
+ debugSocketLog("[SocketClient] reconnect:", this.socket?.connected);
1491
+ this.latestReconnectAttempt = attempt;
1492
+ });
268
1493
  });
269
- this.connectPromise.finally(() => {
270
- this.connectPromise = null;
271
- });
1494
+ const trackedConnectPromise = this.connectPromise;
1495
+ trackedConnectPromise.then(
1496
+ () => {
1497
+ if (this.connectPromise === trackedConnectPromise) {
1498
+ this.connectPromise = null;
1499
+ this.connectReject = null;
1500
+ }
1501
+ },
1502
+ () => {
1503
+ if (this.connectPromise === trackedConnectPromise) {
1504
+ this.connectPromise = null;
1505
+ this.connectReject = null;
1506
+ }
1507
+ }
1508
+ );
272
1509
  return this.connectPromise;
273
1510
  }
274
1511
  disconnect() {
275
- if (!this.socket)
276
- return;
277
- try {
278
- this.socket.off("api:response", this.handleResponse);
279
- this.socket.io.off("reconnect");
280
- this.socket.disconnect();
281
- } catch {
1512
+ this.intentionallyDisconnected = true;
1513
+ this.reconnecting = false;
1514
+ this.restoringSession = false;
1515
+ this.hasEstablishedConnection = false;
1516
+ this.reconnectRestoreGeneration += 1;
1517
+ this.reconnectRestorePromise = null;
1518
+ this.invalidateOperationQueueProcessor();
1519
+ this.stopHeartbeat();
1520
+ const disconnectError = new Error("client disconnected");
1521
+ this.connectReject?.(disconnectError);
1522
+ this.connectReject = null;
1523
+ if (this.socket) {
1524
+ const currentSocket = this.socket;
1525
+ try {
1526
+ currentSocket.off("api:response", this.handleResponse);
1527
+ currentSocket.off(CALL_PUSH_EVENT, this.handleCallPush);
1528
+ currentSocket.io.off("reconnect");
1529
+ currentSocket.io.off("reconnect_attempt");
1530
+ currentSocket.io.off("reconnect_error");
1531
+ currentSocket.io.off("reconnect_failed");
1532
+ currentSocket.disconnect();
1533
+ } catch {
1534
+ } finally {
1535
+ try {
1536
+ currentSocket.off();
1537
+ currentSocket.io.off();
1538
+ } catch {
1539
+ }
1540
+ }
282
1541
  }
283
1542
  this.socket = void 0;
284
- this.cleanupAllPending("client disconnected");
1543
+ this.connectPromise = null;
1544
+ this.cleanupAllPending(disconnectError, true);
1545
+ this.removeSessionListener?.();
1546
+ this.removeSessionListener = void 0;
285
1547
  }
286
1548
  emitWithAck(event, payload, timeoutMs = 1e4) {
287
- if (this.isConnected()) {
1549
+ this.watchSession();
1550
+ timeoutMs = normalizeTimeoutMs(timeoutMs, 1e4);
1551
+ const canSendDuringRestore = event === "login";
1552
+ const hasQueuedOperations = this.operationQueueProcessing || this.operationQueue.length > 0;
1553
+ if (this.isConnected() && (!hasQueuedOperations || canSendDuringRestore) && (!this.restoringSession && !isAuthenticating() || canSendDuringRestore)) {
288
1554
  return this.createEmitPromise(event, payload, timeoutMs);
289
1555
  }
290
- if (this.connectPromise) {
291
- return new Promise((resolve, reject) => {
292
- this.emitQueue.push({ event, payload, timeoutMs, resolve, reject });
293
- });
1556
+ if (this.connectPromise || this.reconnecting || isAuthenticating() && !canSendDuringRestore || hasQueuedOperations || this.restoringSession && !canSendDuringRestore) {
1557
+ return this.queueOperation(
1558
+ () => this.createEmitPromise(event, payload, timeoutMs),
1559
+ canSendDuringRestore ? "login" : "request"
1560
+ );
294
1561
  }
295
- return Promise.reject(new Error("socket not initialized"));
1562
+ return Promise.reject(new Error("socket not connected"));
296
1563
  }
297
1564
  request(path, payload, timeoutMs = 1e4) {
298
- if (!this.socket) {
299
- return Promise.reject(new Error("socket not initialized"));
1565
+ this.watchSession();
1566
+ timeoutMs = normalizeTimeoutMs(timeoutMs, 1e4);
1567
+ const hasQueuedOperations = this.operationQueueProcessing || this.operationQueue.length > 0;
1568
+ if (this.isConnected() && !this.restoringSession && !isAuthenticating() && !hasQueuedOperations) {
1569
+ return this.createRequestPromise(path, payload, timeoutMs);
300
1570
  }
301
- const id = this.generateId();
302
- const envelope = { id, path, payload };
303
- return new Promise((resolve, reject) => {
304
- const timer = setTimeout(() => {
305
- this.pending.delete(id);
306
- reject(new Error("request timeout"));
307
- }, timeoutMs);
308
- this.pending.set(id, { resolve, reject, timer });
309
- try {
310
- this.socket.emit("api:request", envelope);
311
- } catch (err) {
312
- clearTimeout(timer);
313
- this.pending.delete(id);
314
- reject(err);
315
- }
316
- });
1571
+ if (this.connectPromise || this.reconnecting || this.restoringSession || isAuthenticating() || hasQueuedOperations) {
1572
+ return this.queueOperation(
1573
+ () => this.createRequestPromise(path, payload, timeoutMs)
1574
+ );
1575
+ }
1576
+ return Promise.reject(new Error("socket not connected"));
1577
+ }
1578
+ resumeQueuedRequestsAfterLogin() {
1579
+ if (!this.isConnected()) {
1580
+ return;
1581
+ }
1582
+ this.reconnectRestoreGeneration += 1;
1583
+ this.reconnecting = false;
1584
+ this.restoringSession = false;
1585
+ this.processOperationQueue();
1586
+ }
1587
+ rejectQueuedRequestsAfterLoginFailure(reason = new Error("login failed")) {
1588
+ this.rejectQueuedOperations(reason, true);
317
1589
  }
318
1590
  on(event, handler) {
319
- this.socket?.on(event, handler);
1591
+ const handlers = this.eventHandlers.get(event) || /* @__PURE__ */ new Set();
1592
+ if (handlers.has(handler))
1593
+ return;
1594
+ handlers.add(handler);
1595
+ this.eventHandlers.set(event, handlers);
1596
+ if (!LOCAL_CONNECTION_EVENTS.has(event) && !CALL_PUSH_EVENTS.has(event)) {
1597
+ this.socket?.on(event, this.socketHandler(event, handler));
1598
+ } else if (event === "connect" && this.isConnected()) {
1599
+ queueMicrotask(() => {
1600
+ if (this.eventHandlers.get(event) === handlers && handlers.has(handler))
1601
+ this.invokeHandler(event, handler, []);
1602
+ });
1603
+ } else if (event === "connectionStateChanged") {
1604
+ queueMicrotask(() => {
1605
+ if (this.eventHandlers.get(event) === handlers && handlers.has(handler))
1606
+ this.invokeHandler(event, handler, [this.connectionStatePayload]);
1607
+ });
1608
+ }
320
1609
  }
321
1610
  off(event, handler) {
322
- if (!this.socket)
1611
+ if (handler) {
1612
+ const handlers = this.eventHandlers.get(event);
1613
+ handlers?.delete(handler);
1614
+ if (handlers?.size === 0) {
1615
+ this.eventHandlers.delete(event);
1616
+ }
1617
+ } else {
1618
+ this.eventHandlers.delete(event);
1619
+ }
1620
+ if (!this.socket || LOCAL_CONNECTION_EVENTS.has(event) || CALL_PUSH_EVENTS.has(event)) {
323
1621
  return;
324
- handler ? this.socket.off(event, handler) : this.socket.off(event);
1622
+ }
1623
+ handler ? this.socket.off(event, this.socketHandler(event, handler)) : this.socket.off(event);
1624
+ }
1625
+ socketHandler(event, handler) {
1626
+ let events = this.socketHandlers.get(handler);
1627
+ if (!events) {
1628
+ events = /* @__PURE__ */ new Map();
1629
+ this.socketHandlers.set(handler, events);
1630
+ }
1631
+ let wrapped = events.get(event);
1632
+ if (!wrapped) {
1633
+ wrapped = (...args) => {
1634
+ const publicArgs = AUTO_ACK_EVENTS.has(event) ? args.filter((arg) => typeof arg !== "function") : args;
1635
+ const filtered = filterReplyPushArgs(event, publicArgs);
1636
+ if (filtered)
1637
+ this.invokeHandler(event, handler, filtered);
1638
+ };
1639
+ events.set(event, wrapped);
1640
+ }
1641
+ return wrapped;
325
1642
  }
326
1643
  isConnected() {
327
1644
  return !!this.socket?.connected;
328
1645
  }
1646
+ invokeHandler(event, handler, args) {
1647
+ try {
1648
+ const result = handler.apply(this.socket, args);
1649
+ if (result && typeof result.then === "function")
1650
+ Promise.resolve(result).catch((error) => {
1651
+ console.error("[SocketClient] async event handler failed:", event, error);
1652
+ });
1653
+ } catch (error) {
1654
+ console.error("[SocketClient] event handler failed:", event, error);
1655
+ }
1656
+ }
1657
+ startHeartbeat(socket) {
1658
+ this.stopHeartbeat();
1659
+ const engine = socket?.io?.engine;
1660
+ if (!engine || typeof engine._sendPacket !== "function") {
1661
+ return;
1662
+ }
1663
+ const handlePacket = (packet) => {
1664
+ if (packet?.type !== "pong" || typeof packet.data !== "string" || packet.data !== this.pendingHeartbeatID) {
1665
+ return;
1666
+ }
1667
+ debugSocketLog("[SocketClient] heartbeat pong:", packet.data);
1668
+ this.pendingHeartbeatID = void 0;
1669
+ if (this.heartbeatTimeout !== void 0) {
1670
+ clearTimeout(this.heartbeatTimeout);
1671
+ this.heartbeatTimeout = void 0;
1672
+ }
1673
+ };
1674
+ const sendHeartbeat = () => {
1675
+ if (this.socket !== socket || socket.io?.engine !== engine || !socket.connected || engine.readyState !== "open" || this.pendingHeartbeatID) {
1676
+ return;
1677
+ }
1678
+ const heartbeatID = `${HEARTBEAT_PACKET_PREFIX}${++this.heartbeatSequence}`;
1679
+ this.pendingHeartbeatID = heartbeatID;
1680
+ debugSocketLog("[SocketClient] heartbeat ping:", heartbeatID);
1681
+ try {
1682
+ engine._sendPacket("ping", heartbeatID);
1683
+ } catch (error) {
1684
+ debugSocketLog("[SocketClient] heartbeat send failed:", error);
1685
+ this.stopHeartbeat();
1686
+ engine.close();
1687
+ return;
1688
+ }
1689
+ this.heartbeatTimeout = setTimeout(() => {
1690
+ this.heartbeatTimeout = void 0;
1691
+ if (this.socket !== socket || socket.io?.engine !== engine || !socket.connected || this.pendingHeartbeatID !== heartbeatID) {
1692
+ return;
1693
+ }
1694
+ debugSocketLog(
1695
+ `[SocketClient] heartbeat timeout after ${HEARTBEAT_TIMEOUT_MS}ms`
1696
+ );
1697
+ this.stopHeartbeat();
1698
+ engine.close();
1699
+ }, HEARTBEAT_TIMEOUT_MS);
1700
+ };
1701
+ this.heartbeatEngine = engine;
1702
+ this.heartbeatPacketHandler = handlePacket;
1703
+ engine.on("packet", handlePacket);
1704
+ sendHeartbeat();
1705
+ this.heartbeatInterval = setInterval(
1706
+ sendHeartbeat,
1707
+ HEARTBEAT_INTERVAL_MS
1708
+ );
1709
+ }
1710
+ stopHeartbeat() {
1711
+ if (this.heartbeatInterval !== void 0) {
1712
+ clearInterval(this.heartbeatInterval);
1713
+ this.heartbeatInterval = void 0;
1714
+ }
1715
+ if (this.heartbeatTimeout !== void 0) {
1716
+ clearTimeout(this.heartbeatTimeout);
1717
+ this.heartbeatTimeout = void 0;
1718
+ }
1719
+ if (this.heartbeatEngine && this.heartbeatPacketHandler) {
1720
+ this.heartbeatEngine.off("packet", this.heartbeatPacketHandler);
1721
+ }
1722
+ this.heartbeatEngine = void 0;
1723
+ this.heartbeatPacketHandler = void 0;
1724
+ this.pendingHeartbeatID = void 0;
1725
+ }
329
1726
  parseUrl(url) {
330
1727
  let baseUrl = url;
331
1728
  let namespace = "/im/user";
@@ -336,95 +1733,601 @@ var SocketClient = class {
336
1733
  namespace = queryNs;
337
1734
  baseUrl = urlObj.origin + urlObj.pathname.replace(/\/$/, "");
338
1735
  } catch {
339
- console.warn("[Socket] URL parse failed:", url);
1736
+ console.warn("[SocketClient] url parse failed:", url);
340
1737
  }
341
1738
  return { baseUrl, namespace };
342
1739
  }
1740
+ ackPush(socket, event, args) {
1741
+ const ack = args.find((arg) => typeof arg === "function");
1742
+ if (typeof ack !== "function" || !socket.id)
1743
+ return;
1744
+ const data = args[0];
1745
+ let receipt;
1746
+ const normalizeIds = (ids) => [...new Set(ids.filter((id) => typeof id === "string" && id.length > 0 || typeof id === "number" && Number.isFinite(id)).map(String))];
1747
+ if (event === "conversationChanged") {
1748
+ const infos = Array.isArray(data) ? data : Array.isArray(data?.infoList) ? data.infoList : [];
1749
+ const ackIds = normalizeIds(infos.map((info) => info?.conversation?.ackId));
1750
+ const userId = getSdkUserID();
1751
+ receipt = { clientId: socket.id, platform: "web", ...ackIds.length ? { ackIds } : {}, ...userId ? { userId } : {} };
1752
+ } else {
1753
+ const messages = Array.isArray(data?.messageList) ? data.messageList : [];
1754
+ const msgid = normalizeIds(messages.map((message) => message?.messageID));
1755
+ receipt = { clientId: socket.id, platform: "web", ...msgid.length ? { msgid } : {} };
1756
+ }
1757
+ try {
1758
+ ack(receipt);
1759
+ } catch (error) {
1760
+ console.error("[SocketClient] push ACK failed:", event, error);
1761
+ }
1762
+ }
1763
+ bindEventHandlers(socket) {
1764
+ this.eventHandlers.forEach((handlers, event) => {
1765
+ if (LOCAL_CONNECTION_EVENTS.has(event) || CALL_PUSH_EVENTS.has(event)) {
1766
+ return;
1767
+ }
1768
+ handlers.forEach((handler) => {
1769
+ socket.on(event, this.socketHandler(event, handler));
1770
+ });
1771
+ });
1772
+ }
1773
+ emitLocalEvent(event, ...args) {
1774
+ const handlers = this.eventHandlers.get(event);
1775
+ if (!handlers)
1776
+ return;
1777
+ handlers.forEach((handler) => {
1778
+ try {
1779
+ this.invokeHandler(event, handler, args);
1780
+ } catch (err) {
1781
+ console.error("[SocketClient] event handler failed:", event, err);
1782
+ }
1783
+ });
1784
+ }
1785
+ emitConnectionStateChanged(state, event, extendedData = "") {
1786
+ const payload = {
1787
+ state,
1788
+ event,
1789
+ extendedData
1790
+ };
1791
+ this.connectionStatePayload = payload;
1792
+ this.emitLocalEvent("connectionStateChanged", payload);
1793
+ }
1794
+ getDisconnectEvent(reason) {
1795
+ if (reason === "io server disconnect") {
1796
+ return 4 /* KickedOut */;
1797
+ }
1798
+ return 3 /* LoginInterrupted */;
1799
+ }
1800
+ restoreAfterReconnect(generation3) {
1801
+ if (this.reconnectRestorePromise) {
1802
+ return this.reconnectRestorePromise;
1803
+ }
1804
+ const restorePromise = (async () => {
1805
+ await new Promise(
1806
+ (resolveReconnect) => setTimeout(resolveReconnect, RECONNECT_LOGIN_DELAY_MS)
1807
+ );
1808
+ if (generation3 !== this.reconnectRestoreGeneration || !this.isConnected() || !this.socket) {
1809
+ debugSocketLog("[SocketClient] skip reconnect login: socket not ready");
1810
+ return;
1811
+ }
1812
+ try {
1813
+ debugSocketLog("[SocketClient] reconnect login start");
1814
+ const res = await restoreSessionAfterReconnect();
1815
+ if (generation3 !== this.reconnectRestoreGeneration) {
1816
+ return;
1817
+ }
1818
+ if (res) {
1819
+ this.restoringSession = false;
1820
+ this.processOperationQueue();
1821
+ this.emitLocalEvent("reconnectLoginSuccess");
1822
+ } else {
1823
+ this.restoringSession = false;
1824
+ clearSessionState();
1825
+ this.rejectQueuedOperations(new Error("restore login failed"), true);
1826
+ this.processOperationQueue();
1827
+ this.emitLocalEvent("reconnectLoginFailed", {
1828
+ message: "restore login failed"
1829
+ });
1830
+ }
1831
+ } catch (err) {
1832
+ if (generation3 !== this.reconnectRestoreGeneration) {
1833
+ return;
1834
+ }
1835
+ console.error("[SocketClient] reconnect login failed:", err);
1836
+ this.restoringSession = false;
1837
+ clearSessionState();
1838
+ this.rejectQueuedOperations(err, true);
1839
+ this.processOperationQueue();
1840
+ this.emitLocalEvent("reconnectLoginFailed", err);
1841
+ }
1842
+ })();
1843
+ const trackedRestorePromise = restorePromise.finally(() => {
1844
+ if (this.reconnectRestorePromise === trackedRestorePromise) {
1845
+ this.reconnectRestorePromise = null;
1846
+ }
1847
+ });
1848
+ this.reconnectRestorePromise = trackedRestorePromise;
1849
+ return this.reconnectRestorePromise;
1850
+ }
343
1851
  generateId() {
344
1852
  return Date.now().toString(36) + Math.random().toString(36).slice(2, 9);
345
1853
  }
1854
+ waitForSocketConnected(timeoutMs) {
1855
+ if (this.isConnected()) {
1856
+ return Promise.resolve();
1857
+ }
1858
+ return new Promise((resolve, reject) => {
1859
+ const cleanup = () => {
1860
+ clearTimeout(timer);
1861
+ this.off("connect", handleConnect);
1862
+ this.off("reconnect_failed", handleReconnectFailed);
1863
+ this.off("disconnect", handleDisconnect);
1864
+ };
1865
+ const handleConnect = () => {
1866
+ cleanup();
1867
+ resolve();
1868
+ };
1869
+ const handleReconnectFailed = () => {
1870
+ cleanup();
1871
+ reject(new Error("reconnect failed"));
1872
+ };
1873
+ const handleDisconnect = (reason) => {
1874
+ if (!this.reconnecting && !this.restoringSession) {
1875
+ cleanup();
1876
+ reject(new Error("disconnected: " + String(reason || "")));
1877
+ }
1878
+ };
1879
+ const timer = setTimeout(() => {
1880
+ cleanup();
1881
+ reject(new Error("connect timeout"));
1882
+ }, timeoutMs);
1883
+ this.on("connect", handleConnect);
1884
+ this.on("reconnect_failed", handleReconnectFailed);
1885
+ this.on("disconnect", handleDisconnect);
1886
+ });
1887
+ }
1888
+ createRequestPromise(path, payload, timeoutMs) {
1889
+ const id = this.generateId();
1890
+ const envelope = { id, path, payload };
1891
+ return new Promise((resolve, reject) => {
1892
+ if (!this.isConnected() || this.restoringSession) {
1893
+ reject(new Error("socket not connected"));
1894
+ return;
1895
+ }
1896
+ const timer = setTimeout(() => {
1897
+ this.pending.delete(id);
1898
+ reject(new Error("request timeout"));
1899
+ }, timeoutMs);
1900
+ this.pending.set(id, { resolve, reject, timer });
1901
+ try {
1902
+ if (!this.socket) {
1903
+ throw new Error("socket not initialized");
1904
+ }
1905
+ this.socket.emit("api:request", envelope);
1906
+ } catch (err) {
1907
+ clearTimeout(timer);
1908
+ this.pending.delete(id);
1909
+ reject(err);
1910
+ }
1911
+ });
1912
+ }
346
1913
  createEmitPromise(event, payload, timeoutMs) {
1914
+ const id = this.generateId();
347
1915
  return new Promise((resolve, reject) => {
348
- let timedOut = false;
1916
+ if (!this.isConnected()) {
1917
+ reject(new Error("socket not connected"));
1918
+ return;
1919
+ }
349
1920
  const timer = setTimeout(() => {
350
- timedOut = true;
1921
+ this.pendingAcks.delete(id);
351
1922
  reject(new Error("ack timeout"));
352
1923
  }, timeoutMs);
1924
+ this.pendingAcks.set(id, { resolve, reject, timer });
353
1925
  try {
1926
+ if (!this.socket) {
1927
+ throw new Error("socket not initialized");
1928
+ }
354
1929
  this.socket.emit(event, payload, (response) => {
355
- if (timedOut)
1930
+ const entry = this.pendingAcks.get(id);
1931
+ if (!entry)
356
1932
  return;
357
- clearTimeout(timer);
358
- if (response?.code !== void 0) {
359
- isSuccessCode(response.code) ? resolve(response) : reject(new Error(response.message || `code=${response.code}`));
360
- } else {
361
- resolve(response);
362
- }
1933
+ clearTimeout(entry.timer);
1934
+ this.pendingAcks.delete(id);
1935
+ entry.resolve(response);
363
1936
  });
364
1937
  } catch (err) {
365
1938
  clearTimeout(timer);
1939
+ this.pendingAcks.delete(id);
366
1940
  reject(err);
367
1941
  }
368
1942
  });
369
1943
  }
370
- processEmitQueue() {
371
- if (!this.isConnected())
1944
+ queueOperation(execute, kind = "request") {
1945
+ return new Promise((resolve, reject) => {
1946
+ if (this.operationQueue.length >= MAX_OPERATION_QUEUE_SIZE) {
1947
+ reject(new Error("socket operation queue is full"));
1948
+ return;
1949
+ }
1950
+ const operation = {
1951
+ session: captureSession(),
1952
+ execute,
1953
+ resolve,
1954
+ reject,
1955
+ kind
1956
+ };
1957
+ if (kind === "login") {
1958
+ const firstRequestIndex = this.operationQueue.findIndex(
1959
+ (item) => item.kind !== "login"
1960
+ );
1961
+ if (firstRequestIndex < 0) {
1962
+ this.operationQueue.push(operation);
1963
+ } else {
1964
+ this.operationQueue.splice(firstRequestIndex, 0, operation);
1965
+ }
1966
+ } else {
1967
+ this.operationQueue.push(operation);
1968
+ }
1969
+ this.processOperationQueue();
1970
+ });
1971
+ }
1972
+ processOperationQueue() {
1973
+ if (!this.isConnected() || (this.restoringSession || isAuthenticating()) && this.operationQueue[0]?.kind !== "login" || this.operationQueueProcessing) {
372
1974
  return;
373
- while (this.emitQueue.length) {
374
- const item = this.emitQueue.shift();
375
- this.createEmitPromise(item.event, item.payload, item.timeoutMs).then(item.resolve).catch(item.reject);
376
1975
  }
1976
+ this.operationQueueProcessing = true;
1977
+ const runId = ++this.operationQueueRunId;
1978
+ const processNext = () => {
1979
+ if (runId !== this.operationQueueRunId) {
1980
+ return;
1981
+ }
1982
+ if (!this.isConnected() || (this.restoringSession || isAuthenticating()) && this.operationQueue[0]?.kind !== "login") {
1983
+ this.operationQueueProcessing = false;
1984
+ return;
1985
+ }
1986
+ const item = this.operationQueue.shift();
1987
+ if (!item) {
1988
+ this.operationQueueProcessing = false;
1989
+ return;
1990
+ }
1991
+ Promise.resolve().then(() => {
1992
+ assertCurrentSession(item.session);
1993
+ return item.execute();
1994
+ }).then(
1995
+ (value) => {
1996
+ item.resolve(value);
1997
+ },
1998
+ (error) => {
1999
+ item.reject(error);
2000
+ if (item.kind === "login" && runId === this.operationQueueRunId) {
2001
+ this.rejectQueuedOperations(error);
2002
+ }
2003
+ }
2004
+ ).finally(processNext);
2005
+ };
2006
+ processNext();
2007
+ }
2008
+ invalidateOperationQueueProcessor() {
2009
+ this.operationQueueRunId += 1;
2010
+ this.operationQueueProcessing = false;
377
2011
  }
378
- cleanupAllPending(reason) {
2012
+ rejectQueuedOperations(reason, preserveLoginOperations = false) {
2013
+ const queue = this.operationQueue.splice(0);
2014
+ const preserved = [];
2015
+ queue.forEach((item) => {
2016
+ if (preserveLoginOperations && item.kind === "login") {
2017
+ preserved.push(item);
2018
+ } else {
2019
+ item.reject(reason);
2020
+ }
2021
+ });
2022
+ this.operationQueue.unshift(...preserved);
2023
+ }
2024
+ cleanupAllPending(reason, clearQueue) {
379
2025
  for (const [, entry] of this.pending) {
380
2026
  clearTimeout(entry.timer);
381
- entry.reject(new Error(reason));
2027
+ entry.reject(reason);
382
2028
  }
383
2029
  this.pending.clear();
384
- for (const item of this.emitQueue) {
385
- item.reject(new Error(reason));
2030
+ for (const [, entry] of this.pendingAcks) {
2031
+ clearTimeout(entry.timer);
2032
+ entry.reject(reason);
2033
+ }
2034
+ this.pendingAcks.clear();
2035
+ if (clearQueue) {
2036
+ this.rejectQueuedOperations(reason);
386
2037
  }
387
- this.emitQueue = [];
388
2038
  }
389
2039
  };
390
2040
  var socketClient = new SocketClient();
391
2041
  var client_default = socketClient;
392
2042
 
2043
+ // src/sdk/message/replyResult.ts
2044
+ function normalizeReplyResult(result, payload) {
2045
+ const sent = payload.message || {};
2046
+ const original = payload.toOriginalMessage || {};
2047
+ const received = result?.message && typeof result.message === "object" ? result.message : result;
2048
+ const hasReplyIdentity = received?.messageID && (!original.messageID || String(received.messageID) !== String(original.messageID)) && !isSelfReferencingReply(received);
2049
+ const row = hasReplyIdentity ? received : {};
2050
+ const normalize = (row2) => {
2051
+ const reply = {
2052
+ ...sent,
2053
+ ...row2,
2054
+ clientBizId: sent.clientBizId,
2055
+ localMessageID: sent.localMessageID || sent.clientBizId,
2056
+ conversationID: sent.conversationID ?? original.conversationID,
2057
+ conversationType: sent.conversationType ?? original.conversationType,
2058
+ direction: 0,
2059
+ sentStatus: 1,
2060
+ sendState: 1,
2061
+ repliedInfo: {
2062
+ ...sent.repliedInfo,
2063
+ ...row2.repliedInfo,
2064
+ messageInfo: row2.repliedInfo?.messageInfo ?? sent.repliedInfo?.messageInfo
2065
+ }
2066
+ };
2067
+ delete reply.localID;
2068
+ delete reply.errorCode;
2069
+ delete reply.errorMessage;
2070
+ return reply;
2071
+ };
2072
+ if (result?.message && typeof result.message === "object")
2073
+ return { ...result, message: normalize(row) };
2074
+ return normalize(row);
2075
+ }
2076
+
2077
+ // src/sdk/message/sendResult.ts
2078
+ function normalizeSendResult(method, result, payload) {
2079
+ if (method === "replyMessage")
2080
+ return normalizeReplyResult(result, payload);
2081
+ const sent = payload.message || {};
2082
+ const normalize = (row = {}) => {
2083
+ const sendState = messageSendState(row);
2084
+ return {
2085
+ ...row,
2086
+ clientBizId: sent.clientBizId || row.clientBizId,
2087
+ localMessageID: row.localMessageID || sent.localMessageID || sent.clientBizId,
2088
+ sendState,
2089
+ sentStatus: row.sentStatus ?? sendState
2090
+ };
2091
+ };
2092
+ if (result?.message && typeof result.message === "object")
2093
+ return { ...result, message: normalize(result.message) };
2094
+ return normalize(result && typeof result === "object" ? result : {});
2095
+ }
2096
+
393
2097
  // src/plugins/request/index.ts
2098
+ var DEFAULT_TIMEOUT_MS = 1e4;
2099
+ function getTimeoutMs(config) {
2100
+ return typeof config.timeoutMs === "number" && Number.isFinite(config.timeoutMs) && config.timeoutMs > 0 ? config.timeoutMs : DEFAULT_TIMEOUT_MS;
2101
+ }
394
2102
  async function request(config) {
395
2103
  if (!config || !config.path) {
396
- return Promise.reject(new Error("invalid request config"));
2104
+ throwSdkTransportError(new Error("invalid request config"));
397
2105
  }
398
- const path = config.path;
399
- const isSdkApi = path.startsWith("/sdk/") || path.startsWith("sdk/");
400
- if (isSdkApi) {
401
- const eventName = path.replace(/^\/?sdk\//, "");
402
- const timeoutMs2 = typeof config.timeoutMs === "number" ? config.timeoutMs : 1e4;
403
- if (!client_default.isConnected()) {
404
- return Promise.reject(new Error("socket not connected"));
405
- }
406
- try {
407
- const res = await client_default.emitWithAck(
2106
+ const session = captureSession();
2107
+ const method = config.path.replace(/^\/?sdk\//, "");
2108
+ if (method !== "login" && method !== "logout" && isAuthenticating())
2109
+ await waitForAuthentication(session);
2110
+ const storage = captureStorage();
2111
+ const sending = ["sendMessage", "sendMediaMessage", "replyMessage"].includes(method);
2112
+ if (storage && sending && config.data?.message) {
2113
+ config = { ...config, data: { ...config.data, message: {
2114
+ ...config.data.message,
2115
+ clientBizId: config.data.message.clientBizId || makeClientBizId()
2116
+ } } };
2117
+ await persistOutgoing(storage, config.data);
2118
+ }
2119
+ const startedAt = Date.now();
2120
+ const fetchSequence = storage ? storage.mutationSequence = (storage.mutationSequence || 0) + 1 : 0;
2121
+ try {
2122
+ assertCurrentSession(session);
2123
+ if (storage && !isCurrentStorage(storage))
2124
+ throw new Error("Local storage account changed before request");
2125
+ const { path } = config;
2126
+ const isSdkApi = path.startsWith("/sdk/") || path.startsWith("sdk/");
2127
+ const timeoutMs = getTimeoutMs(config);
2128
+ if (isSdkApi) {
2129
+ const eventName = path.replace(/^\/?sdk\//, "");
2130
+ const response2 = await client_default.emitWithAck(
408
2131
  eventName,
409
2132
  config.data,
410
- timeoutMs2
2133
+ timeoutMs
411
2134
  );
412
- if (res && typeof res === "object" && typeof res.code === "number") {
413
- return res;
2135
+ assertCurrentSession(session);
2136
+ const envelope = hasSdkCode(response2) ? response2 : { code: 0, message: "", data: response2 };
2137
+ const normalizedEnvelope = sending && isSuccessCode(envelope.code) ? { ...envelope, data: normalizeSendResult(method, envelope.data, config.data || {}) } : envelope;
2138
+ if (isSuccessCode(envelope.code)) {
2139
+ await cacheApiResult(storage, method, config.data || {}, normalizedEnvelope.data, startedAt, fetchSequence);
2140
+ } else if (sending) {
2141
+ await markSendError(storage, config.data?.message?.clientBizId, outgoingFallback(config.data || {}, storage), envelope);
414
2142
  }
415
- return { code: 0, message: "", data: res };
416
- } catch (err) {
417
- return Promise.reject(err);
2143
+ assertCurrentSession(session);
2144
+ return normalizedEnvelope;
418
2145
  }
2146
+ const response = await client_default.request(path, config.data, timeoutMs);
2147
+ assertCurrentSession(session);
2148
+ return response;
2149
+ } catch (error) {
2150
+ if (sending)
2151
+ await markSendError(storage, config.data?.message?.clientBizId, outgoingFallback(config.data || {}, storage), error);
2152
+ assertCurrentSession(session);
2153
+ if (isSdkRuntimeErrorObject(error)) {
2154
+ throw error;
2155
+ }
2156
+ throwSdkTransportError(error);
419
2157
  }
420
- if (!client_default.isConnected()) {
421
- return Promise.reject(new Error("socket not connected"));
2158
+ }
2159
+
2160
+ // src/sdk/storage/types.ts
2161
+ var LocalSyncError = class extends Error {
2162
+ constructor(results, failures) {
2163
+ super(`Local synchronization incomplete: ${failures.length} failure(s)`);
2164
+ this.results = results;
2165
+ this.failures = failures;
2166
+ this.name = "LocalSyncError";
2167
+ this.fullyReconciled = false;
2168
+ }
2169
+ toJSON() {
2170
+ return { name: this.name, message: this.message, results: this.results, failures: this.failures, fullyReconciled: false };
2171
+ }
2172
+ };
2173
+
2174
+ // src/sdk/storage/sync.ts
2175
+ var running = /* @__PURE__ */ new WeakMap();
2176
+ async function syncConversationMessages(options) {
2177
+ const ctx = captureStorage();
2178
+ if (!ctx)
2179
+ throw new Error("Local message storage is not enabled");
2180
+ const { conversationID } = options;
2181
+ const conversationType = String(options.conversationType);
2182
+ const key = JSON.stringify([conversationType, conversationID]);
2183
+ let jobs = running.get(ctx);
2184
+ if (!jobs) {
2185
+ jobs = /* @__PURE__ */ new Map();
2186
+ running.set(ctx, jobs);
2187
+ }
2188
+ const existing = jobs.get(key);
2189
+ if (existing)
2190
+ return existing;
2191
+ const job = (async () => {
2192
+ emitStorageEvent("localSyncStateChanged", { state: "syncing", conversationID, conversationType });
2193
+ try {
2194
+ await ctx.ready;
2195
+ const failuresBefore = ctx.failureCount || 0;
2196
+ let nextMessage;
2197
+ let exhausted = false;
2198
+ const cursors = /* @__PURE__ */ new Set();
2199
+ for (let page = 0; page < 100; page++) {
2200
+ if (!isCurrentStorage(ctx))
2201
+ throw new Error("Local storage account changed");
2202
+ const response = await request({ path: "/sdk/queryHistoryMessage", data: {
2203
+ conversationID,
2204
+ conversationType,
2205
+ config: { count: "100", ...nextMessage ? { nextMessage } : {} }
2206
+ } });
2207
+ const data = unwrapSdkData(response);
2208
+ if (!isCurrentStorage(ctx))
2209
+ throw new Error("Local storage account changed");
2210
+ const list = data?.messageList;
2211
+ if (!Array.isArray(list))
2212
+ throw new Error("Invalid history response during local synchronization");
2213
+ if (list.length < 100) {
2214
+ exhausted = true;
2215
+ break;
2216
+ }
2217
+ const last = list[list.length - 1];
2218
+ if (!last.messageID)
2219
+ throw new Error("History cursor is missing messageID");
2220
+ const cursor = String(last.messageID);
2221
+ if (cursors.has(cursor))
2222
+ throw new Error("History pagination cursor did not advance");
2223
+ cursors.add(cursor);
2224
+ nextMessage = { messageID: last.messageID, id: last.id || "", type: String(last.type) };
2225
+ }
2226
+ if (!exhausted)
2227
+ throw new Error("History synchronization page limit reached; synchronization is incomplete");
2228
+ if ((ctx.failureCount || 0) !== failuresBefore)
2229
+ throw new Error("Messages could not be persisted during synchronization");
2230
+ const result = { conversationID, conversationType, syncedAt: Date.now(), fullyReconciled: false };
2231
+ await storageWork(ctx, async (ctx2) => {
2232
+ await ctx2.db.meta.put({ key: `sync:${key}`, ...result });
2233
+ });
2234
+ if ((ctx.failureCount || 0) !== failuresBefore)
2235
+ throw new Error("Synchronization metadata could not be persisted");
2236
+ if (isCurrentStorage(ctx))
2237
+ emitStorageEvent("localSyncStateChanged", { state: "synced", ...result });
2238
+ return result;
2239
+ } catch (error) {
2240
+ if (isCurrentStorage(ctx))
2241
+ emitStorageEvent("localSyncStateChanged", { state: "failed", conversationID, conversationType, message: error instanceof Error ? error.message : String(error), fullyReconciled: false });
2242
+ throw error;
2243
+ }
2244
+ })();
2245
+ jobs.set(key, job);
2246
+ try {
2247
+ return await job;
2248
+ } finally {
2249
+ jobs.delete(key);
2250
+ }
2251
+ }
2252
+ async function syncLocalMessages() {
2253
+ const ctx = captureStorage();
2254
+ if (!ctx)
2255
+ return [];
2256
+ const conversations = await storageWork(ctx, async (ctx2) => {
2257
+ const keys = await ctx2.db.messages.orderBy("conversationKey").uniqueKeys();
2258
+ return keys.map((key) => {
2259
+ const [conversationType, conversationID] = JSON.parse(String(key));
2260
+ return { conversationType: String(conversationType), conversationID: String(conversationID) };
2261
+ });
2262
+ });
2263
+ const failures = [];
2264
+ if (!conversations)
2265
+ failures.push({ conversationID: "", conversationType: "", message: "Cached conversations could not be read" });
2266
+ const known = new Map((conversations || []).map((row) => [JSON.stringify([String(row.conversationType), row.conversationID]), row]));
2267
+ try {
2268
+ let nextConversation;
2269
+ const seen = /* @__PURE__ */ new Set();
2270
+ let exhausted = false;
2271
+ for (let page = 0; page < 100 && isCurrentStorage(ctx); page++) {
2272
+ const data = unwrapSdkData(await request({ path: "/sdk/queryConversationList", data: { count: 100, ...nextConversation ? { nextConversation } : {} } }));
2273
+ if (!isCurrentStorage(ctx))
2274
+ throw new Error("Local storage account changed");
2275
+ const list = Array.isArray(data) ? data : data?.conversationList;
2276
+ if (!Array.isArray(list))
2277
+ throw new Error("Invalid conversation list during local synchronization");
2278
+ for (const row of list) {
2279
+ const conversation = row.conversation || row;
2280
+ if (conversation.conversationID === void 0 || (conversation.conversationType ?? conversation.type) === void 0)
2281
+ continue;
2282
+ const entry = { conversationID: String(conversation.conversationID), conversationType: String(conversation.conversationType ?? conversation.type) };
2283
+ known.set(JSON.stringify([entry.conversationType, entry.conversationID]), entry);
2284
+ }
2285
+ if (list.length < 100) {
2286
+ exhausted = true;
2287
+ break;
2288
+ }
2289
+ nextConversation = list[list.length - 1].conversation || list[list.length - 1];
2290
+ const key = JSON.stringify([nextConversation.conversationType ?? nextConversation.type, nextConversation.conversationID]);
2291
+ if (seen.has(key))
2292
+ throw new Error("Conversation pagination cursor did not advance");
2293
+ seen.add(key);
2294
+ }
2295
+ if (!exhausted)
2296
+ throw new Error("Conversation synchronization page limit reached; synchronization is incomplete");
2297
+ } catch (error) {
2298
+ failures.push({ conversationID: "", conversationType: "", message: error instanceof Error ? error.message : String(error) });
2299
+ if (isCurrentStorage(ctx))
2300
+ emitStorageEvent("localSyncStateChanged", { state: "failed", conversationID: "", conversationType: "", message: error instanceof Error ? error.message : String(error), fullyReconciled: false });
422
2301
  }
423
- const timeoutMs = typeof config.timeoutMs === "number" ? config.timeoutMs : 1e4;
424
- return client_default.request(config.path, config.data, timeoutMs);
2302
+ const results = [];
2303
+ for (const conversation of known.values()) {
2304
+ if (!isCurrentStorage(ctx)) {
2305
+ failures.push({ ...conversation, message: "Local storage account changed" });
2306
+ break;
2307
+ }
2308
+ try {
2309
+ results.push(await syncConversationMessages(conversation));
2310
+ } catch (error) {
2311
+ failures.push({ ...conversation, message: error instanceof Error ? error.message : String(error) });
2312
+ }
2313
+ }
2314
+ if (failures.length)
2315
+ throw new LocalSyncError(results, failures);
2316
+ return results;
425
2317
  }
426
2318
 
2319
+ // src/sdk/storage/index.ts
2320
+ var LocalStorageApis = { queryLocalMessages, queryFailedMessages, clearLocalMessages, getLocalStorageStatus, syncConversationMessages, syncLocalMessages };
2321
+ var storage_default = LocalStorageApis;
2322
+
427
2323
  // src/apis/SdkQueryConversationPostApi.ts
2324
+ var postSdkQueryConversationListApiRequest = (data) => {
2325
+ return request({
2326
+ path: "/sdk/queryConversationList",
2327
+ method: "POST",
2328
+ data
2329
+ });
2330
+ };
428
2331
  var postSdkQueryConversationApiRequest = (data) => {
429
2332
  return request({
430
2333
  path: "/sdk/queryConversation",
@@ -469,11 +2372,24 @@ var postSdkClearConversationUnreadMessageCountApiRequest = (data) => {
469
2372
  });
470
2373
  };
471
2374
 
472
- // src/sdk/conversation.ts
473
- async function queryConversation(payload) {
474
- const response = await postSdkQueryConversationApiRequest(payload);
2375
+ // src/sdk/conversation/conversation.ts
2376
+ function isConversationQueriedResult(value) {
2377
+ return !!value && typeof value === "object" && "conversation" in value;
2378
+ }
2379
+ async function queryConversationList(payload) {
2380
+ const response = await postSdkQueryConversationListApiRequest(
2381
+ payload
2382
+ );
475
2383
  return unwrapSdkData(response) ?? [];
476
2384
  }
2385
+ async function queryConversation(conversationID, conversationType) {
2386
+ const response = await postSdkQueryConversationApiRequest({
2387
+ conversationID,
2388
+ conversationType
2389
+ });
2390
+ const data = unwrapSdkData(response);
2391
+ return isConversationQueriedResult(data) ? data : { conversation: data };
2392
+ }
477
2393
  async function deleteAllConversations() {
478
2394
  const response = await postSdkDeleteAllConversationsApiRequest(
479
2395
  { config: true }
@@ -500,20 +2416,131 @@ async function clearConversationUnreadMessageCount(conversationID, conversationT
500
2416
  return unwrapSdkResponse(response);
501
2417
  }
502
2418
 
2419
+ // src/constants/call/ZIMCall.ts
2420
+ var ZIMCallType = /* @__PURE__ */ ((ZIMCallType2) => {
2421
+ ZIMCallType2["Audio"] = "audio";
2422
+ ZIMCallType2["Video"] = "video";
2423
+ return ZIMCallType2;
2424
+ })(ZIMCallType || {});
2425
+ var ZIMCallMode = /* @__PURE__ */ ((ZIMCallMode2) => {
2426
+ ZIMCallMode2["OneOnOne"] = "one_on_one";
2427
+ ZIMCallMode2["Group"] = "group";
2428
+ return ZIMCallMode2;
2429
+ })(ZIMCallMode || {});
2430
+ var ZIMCallConversationType = /* @__PURE__ */ ((ZIMCallConversationType2) => {
2431
+ ZIMCallConversationType2[ZIMCallConversationType2["Peer"] = 0] = "Peer";
2432
+ ZIMCallConversationType2[ZIMCallConversationType2["Group"] = 1] = "Group";
2433
+ return ZIMCallConversationType2;
2434
+ })(ZIMCallConversationType || {});
2435
+ var ZIMCallAction = /* @__PURE__ */ ((ZIMCallAction2) => {
2436
+ ZIMCallAction2["Invite"] = "invite";
2437
+ ZIMCallAction2["Accept"] = "accept";
2438
+ ZIMCallAction2["Reject"] = "reject";
2439
+ ZIMCallAction2["Cancel"] = "cancel";
2440
+ ZIMCallAction2["Hangup"] = "hangup";
2441
+ ZIMCallAction2["Timeout"] = "timeout";
2442
+ ZIMCallAction2["MemberJoined"] = "member_joined";
2443
+ ZIMCallAction2["MemberLeft"] = "member_left";
2444
+ ZIMCallAction2["MediaStateChanged"] = "media_state_changed";
2445
+ return ZIMCallAction2;
2446
+ })(ZIMCallAction || {});
2447
+ var ZIMCallEventEnum = /* @__PURE__ */ ((ZIMCallEventEnum2) => {
2448
+ ZIMCallEventEnum2["CallInvitationReceived"] = "callInvitationReceived";
2449
+ ZIMCallEventEnum2["CallAccepted"] = "callAccepted";
2450
+ ZIMCallEventEnum2["CallRejected"] = "callRejected";
2451
+ ZIMCallEventEnum2["CallCanceled"] = "callCanceled";
2452
+ ZIMCallEventEnum2["CallEnded"] = "callEnded";
2453
+ ZIMCallEventEnum2["CallTimeout"] = "callTimeout";
2454
+ ZIMCallEventEnum2["CallParticipantJoined"] = "callParticipantJoined";
2455
+ ZIMCallEventEnum2["CallParticipantLeft"] = "callParticipantLeft";
2456
+ ZIMCallEventEnum2["CallMediaStateChanged"] = "callMediaStateChanged";
2457
+ ZIMCallEventEnum2["CallConnectionStateChanged"] = "callConnectionStateChanged";
2458
+ ZIMCallEventEnum2["CallMediaTrackSubscribed"] = "callMediaTrackSubscribed";
2459
+ ZIMCallEventEnum2["CallMediaTrackUnsubscribed"] = "callMediaTrackUnsubscribed";
2460
+ return ZIMCallEventEnum2;
2461
+ })(ZIMCallEventEnum || {});
2462
+ var ZIM_LOCAL_CALL_EVENTS = /* @__PURE__ */ new Set([
2463
+ "callMediaStateChanged" /* CallMediaStateChanged */,
2464
+ "callConnectionStateChanged" /* CallConnectionStateChanged */,
2465
+ "callMediaTrackSubscribed" /* CallMediaTrackSubscribed */,
2466
+ "callMediaTrackUnsubscribed" /* CallMediaTrackUnsubscribed */
2467
+ ]);
2468
+
2469
+ // src/sdk/call/callEvents.ts
2470
+ var listeners3 = /* @__PURE__ */ new Map();
2471
+ function onCallEvent(event, handler) {
2472
+ const handlers = listeners3.get(event) || /* @__PURE__ */ new Set();
2473
+ handlers.add(handler);
2474
+ listeners3.set(event, handlers);
2475
+ }
2476
+ function offCallEvent(event, handler) {
2477
+ if (!handler) {
2478
+ listeners3.delete(event);
2479
+ return;
2480
+ }
2481
+ const handlers = listeners3.get(event);
2482
+ if (!handlers)
2483
+ return;
2484
+ handlers.delete(handler);
2485
+ if (handlers.size === 0) {
2486
+ listeners3.delete(event);
2487
+ }
2488
+ }
2489
+ function emitCallEvent(event, ...args) {
2490
+ listeners3.get(event)?.forEach((handler) => {
2491
+ try {
2492
+ const result = handler(...args);
2493
+ if (result && typeof result.then === "function")
2494
+ Promise.resolve(result).catch((error) => {
2495
+ console.error("[VanshiSdk] async call event handler failed:", event, error);
2496
+ });
2497
+ } catch (error) {
2498
+ console.error("[VanshiSdk] call event handler failed:", event, error);
2499
+ }
2500
+ });
2501
+ }
2502
+
503
2503
  // src/sdk/initSocket.ts
504
2504
  async function initSocket() {
505
2505
  const baseUrl = getSocketUrl();
506
- if (!client_default.isConnected()) {
507
- await client_default.connect(baseUrl);
2506
+ try {
2507
+ if (!client_default.isConnected()) {
2508
+ await client_default.connect(baseUrl);
2509
+ }
2510
+ } catch (error) {
2511
+ throwSdkTransportError(error, "socket connect failed");
508
2512
  }
509
2513
  }
2514
+ function disconnectSocket() {
2515
+ client_default.disconnect();
2516
+ }
510
2517
  function isSocketConnected() {
511
2518
  return client_default.isConnected();
512
2519
  }
2520
+ function resumeQueuedRequestsAfterLogin() {
2521
+ client_default.resumeQueuedRequestsAfterLogin();
2522
+ }
2523
+ function rejectQueuedRequestsAfterLoginFailure(reason) {
2524
+ client_default.rejectQueuedRequestsAfterLoginFailure(reason);
2525
+ }
513
2526
  function on(event, handler) {
2527
+ if (STORAGE_EVENTS.has(event)) {
2528
+ onStorageEvent(event, handler);
2529
+ return;
2530
+ }
2531
+ if (ZIM_LOCAL_CALL_EVENTS.has(event)) {
2532
+ onCallEvent(event, handler);
2533
+ }
514
2534
  client_default.on(event, handler);
515
2535
  }
516
2536
  function off(event, handler) {
2537
+ if (STORAGE_EVENTS.has(event)) {
2538
+ offStorageEvent(event, handler);
2539
+ return;
2540
+ }
2541
+ if (ZIM_LOCAL_CALL_EVENTS.has(event)) {
2542
+ offCallEvent(event, handler);
2543
+ }
517
2544
  client_default.off(event, handler);
518
2545
  }
519
2546
 
@@ -534,14 +2561,21 @@ var postSdkLogoutApiRequest = (data) => {
534
2561
  };
535
2562
 
536
2563
  // src/sdk/loginCore.ts
537
- async function performLogin(payload, { requestLogin, setSession = setSdkSession, clearSession = clearSdkSession }) {
2564
+ async function performLogin(payload, { requestLogin, setSession = setSdkSession, clearSession = clearSdkSession, isCurrent = () => true }) {
538
2565
  try {
539
2566
  const resp = await requestLogin(payload);
540
- if (unwrapSdkData(resp)) {
2567
+ if (!isCurrent())
2568
+ return false;
2569
+ const result = unwrapSdkDetailedData(resp);
2570
+ if (!result.success)
2571
+ throw new SdkRuntimeError(result).toObject();
2572
+ if (result.data) {
541
2573
  setSession(payload.token);
542
2574
  return true;
543
2575
  }
544
2576
  } catch (error) {
2577
+ if (!isCurrent())
2578
+ return false;
545
2579
  clearSession();
546
2580
  throw error;
547
2581
  }
@@ -549,28 +2583,685 @@ async function performLogin(payload, { requestLogin, setSession = setSdkSession,
549
2583
  return false;
550
2584
  }
551
2585
 
2586
+ // src/sdk/call/callMedia.ts
2587
+ import {
2588
+ Room,
2589
+ RoomEvent,
2590
+ Track,
2591
+ createLocalAudioTrack,
2592
+ createLocalScreenTracks,
2593
+ createLocalVideoTrack
2594
+ } from "livekit-client";
2595
+ var DEFAULT_JOIN_MEDIA_CONFIG = {
2596
+ audio: true,
2597
+ video: true
2598
+ };
2599
+ var LiveKitCallMediaAdapter = class {
2600
+ constructor() {
2601
+ this.localTracks = [];
2602
+ this.screenTracks = [];
2603
+ this.generation = 0;
2604
+ this.screenGeneration = 0;
2605
+ this.controls = /* @__PURE__ */ new Map();
2606
+ this.disconnecting = /* @__PURE__ */ new Set();
2607
+ }
2608
+ assertRoom(room, generation3) {
2609
+ if (this.room !== room || this.generation !== generation3)
2610
+ throw new Error("Media operation canceled");
2611
+ }
2612
+ stopTracks(tracks) {
2613
+ for (const track of tracks) {
2614
+ try {
2615
+ track.stop();
2616
+ } catch {
2617
+ }
2618
+ try {
2619
+ track.detach();
2620
+ } catch {
2621
+ }
2622
+ }
2623
+ }
2624
+ async releaseRoom() {
2625
+ const room = this.room;
2626
+ this.room = void 0;
2627
+ this.callID = void 0;
2628
+ this.stopTracks([...this.localTracks, ...this.screenTracks]);
2629
+ this.localTracks = [];
2630
+ this.screenTracks = [];
2631
+ this.controls.clear();
2632
+ if (room) {
2633
+ room.removeAllListeners();
2634
+ const completion = Promise.resolve(room.disconnect());
2635
+ this.disconnecting.add(completion);
2636
+ try {
2637
+ await completion;
2638
+ } finally {
2639
+ this.disconnecting.delete(completion);
2640
+ }
2641
+ }
2642
+ await Promise.all([...this.disconnecting]);
2643
+ }
2644
+ control(key, operation) {
2645
+ const previous = this.controls.get(key) || Promise.resolve();
2646
+ const result = previous.catch(() => {
2647
+ }).then(operation);
2648
+ this.controls.set(key, result);
2649
+ void result.finally(() => {
2650
+ if (this.controls.get(key) === result)
2651
+ this.controls.delete(key);
2652
+ }).catch(() => {
2653
+ });
2654
+ return result;
2655
+ }
2656
+ async joinMedia(config, connectOptions) {
2657
+ const generation3 = ++this.generation;
2658
+ this.screenGeneration++;
2659
+ await this.releaseRoom();
2660
+ if (generation3 !== this.generation)
2661
+ throw new Error("Media operation canceled");
2662
+ const liveKit = normalizeLiveKitInfo(config);
2663
+ if (!liveKit.token) {
2664
+ throwSdkRuntimeError({
2665
+ message: "LiveKit token is required",
2666
+ raw: config
2667
+ });
2668
+ }
2669
+ const liveKitUrl = liveKit.livekitUrl || liveKit.liveKitUrl || config.livekitUrl || config.liveKitUrl;
2670
+ if (!liveKitUrl) {
2671
+ throwSdkRuntimeError({
2672
+ message: "LiveKit URL is required",
2673
+ raw: config
2674
+ });
2675
+ }
2676
+ const room = new Room({
2677
+ adaptiveStream: true,
2678
+ dynacast: true
2679
+ });
2680
+ this.callID = config.callID;
2681
+ this.room = room;
2682
+ this.bindRoomEvents(room);
2683
+ try {
2684
+ await room.connect(liveKitUrl, liveKit.token, connectOptions);
2685
+ this.assertRoom(room, generation3);
2686
+ await this.publishInitialTracks(config, room, generation3);
2687
+ this.assertRoom(room, generation3);
2688
+ return { callID: config.callID, room, localTracks: [...this.localTracks] };
2689
+ } catch (error) {
2690
+ try {
2691
+ if (this.room === room && this.generation === generation3)
2692
+ await this.leaveMedia();
2693
+ else {
2694
+ room.removeAllListeners();
2695
+ await room.disconnect();
2696
+ }
2697
+ } catch {
2698
+ }
2699
+ throw error;
2700
+ }
2701
+ }
2702
+ async leaveMedia() {
2703
+ this.generation++;
2704
+ this.screenGeneration++;
2705
+ await this.releaseRoom();
2706
+ }
2707
+ getRoom() {
2708
+ return this.room;
2709
+ }
2710
+ getLocalTracks() {
2711
+ return [...this.localTracks];
2712
+ }
2713
+ getMediaState() {
2714
+ return {
2715
+ microphoneEnabled: this.localTracks.some((track) => track.kind === Track.Kind.Audio && !track.isMuted),
2716
+ cameraEnabled: this.localTracks.some((track) => track.kind === Track.Kind.Video && track.source !== Track.Source.ScreenShare && !track.isMuted),
2717
+ screenShareEnabled: this.screenTracks.length > 0
2718
+ };
2719
+ }
2720
+ setMicrophoneEnabled(enabled) {
2721
+ return this.setDeviceEnabled("microphone", enabled);
2722
+ }
2723
+ setCameraEnabled(enabled) {
2724
+ return this.setDeviceEnabled("camera", enabled);
2725
+ }
2726
+ setDeviceEnabled(device, enabled) {
2727
+ const room = this.room;
2728
+ const generation3 = this.generation;
2729
+ return this.control(device, async () => {
2730
+ if (!room)
2731
+ throw new Error("LiveKit room is not joined");
2732
+ this.assertRoom(room, generation3);
2733
+ const kind = device === "microphone" ? Track.Kind.Audio : Track.Kind.Video;
2734
+ let track = this.localTracks.find((item) => item.kind === kind && item.source !== Track.Source.ScreenShare);
2735
+ if (!track && enabled) {
2736
+ track = await this.acquireTrack(device === "microphone" ? createLocalAudioTrack : createLocalVideoTrack, room, generation3);
2737
+ }
2738
+ if (track)
2739
+ await setLocalTrackEnabled(track, enabled);
2740
+ this.assertRoom(room, generation3);
2741
+ return this.emitMediaState();
2742
+ });
2743
+ }
2744
+ startScreenShare() {
2745
+ const room = this.room;
2746
+ const generation3 = this.generation;
2747
+ const screenGeneration = this.screenGeneration;
2748
+ return this.control("screen", async () => {
2749
+ if (!room)
2750
+ throw new Error("LiveKit room is not joined");
2751
+ this.assertRoom(room, generation3);
2752
+ if (this.screenTracks.length)
2753
+ return { tracks: [...this.screenTracks] };
2754
+ let tracks = [];
2755
+ const assertActive = () => {
2756
+ this.assertRoom(room, generation3);
2757
+ if (screenGeneration !== this.screenGeneration)
2758
+ throw new Error("Screen share canceled");
2759
+ };
2760
+ try {
2761
+ assertActive();
2762
+ tracks = await createLocalScreenTracks({ audio: true, video: true });
2763
+ assertActive();
2764
+ this.screenTracks = tracks;
2765
+ for (const track of tracks) {
2766
+ await room.localParticipant.publishTrack(track);
2767
+ assertActive();
2768
+ }
2769
+ this.emitMediaState();
2770
+ return { tracks: [...tracks] };
2771
+ } catch (error) {
2772
+ this.stopTracks(tracks);
2773
+ await Promise.allSettled(tracks.map((track) => room.localParticipant.unpublishTrack(track)));
2774
+ if (this.room === room)
2775
+ this.screenTracks = this.screenTracks.filter((track) => !tracks.includes(track));
2776
+ throw error;
2777
+ }
2778
+ });
2779
+ }
2780
+ async stopScreenShare() {
2781
+ this.screenGeneration++;
2782
+ const room = this.room;
2783
+ const tracks = this.screenTracks;
2784
+ this.screenTracks = [];
2785
+ this.stopTracks(tracks);
2786
+ await Promise.all(tracks.map((track) => room?.localParticipant.unpublishTrack(track)));
2787
+ return this.emitMediaState();
2788
+ }
2789
+ bindRoomEvents(room) {
2790
+ room.on(RoomEvent.ConnectionStateChanged, (state) => {
2791
+ emitCallEvent("callConnectionStateChanged" /* CallConnectionStateChanged */, {
2792
+ callID: this.callID,
2793
+ state
2794
+ });
2795
+ }).on(RoomEvent.TrackSubscribed, (track, publication, participant) => {
2796
+ emitCallEvent("callMediaTrackSubscribed" /* CallMediaTrackSubscribed */, {
2797
+ callID: this.callID,
2798
+ track,
2799
+ publication,
2800
+ participantID: participant.identity,
2801
+ participantName: participant.name
2802
+ });
2803
+ }).on(RoomEvent.TrackUnsubscribed, (track, publication, participant) => {
2804
+ emitCallEvent("callMediaTrackUnsubscribed" /* CallMediaTrackUnsubscribed */, {
2805
+ callID: this.callID,
2806
+ track,
2807
+ publication,
2808
+ participantID: participant.identity,
2809
+ participantName: participant.name
2810
+ });
2811
+ });
2812
+ }
2813
+ async publishInitialTracks(config, room, generation3) {
2814
+ const merged = { ...DEFAULT_JOIN_MEDIA_CONFIG, ...config };
2815
+ const ensureTrack = (device) => this.control(device, async () => {
2816
+ this.assertRoom(room, generation3);
2817
+ const kind = device === "microphone" ? Track.Kind.Audio : Track.Kind.Video;
2818
+ if (!this.localTracks.some((track) => track.kind === kind && track.source !== Track.Source.ScreenShare)) {
2819
+ await this.acquireTrack(device === "microphone" ? createLocalAudioTrack : createLocalVideoTrack, room, generation3);
2820
+ }
2821
+ });
2822
+ if (merged.audio)
2823
+ await ensureTrack("microphone");
2824
+ if (merged.video)
2825
+ await ensureTrack("camera");
2826
+ this.assertRoom(room, generation3);
2827
+ this.emitMediaState();
2828
+ }
2829
+ async acquireTrack(create, room, generation3) {
2830
+ this.assertRoom(room, generation3);
2831
+ const track = await create();
2832
+ try {
2833
+ this.assertRoom(room, generation3);
2834
+ this.localTracks.push(track);
2835
+ await room.localParticipant.publishTrack(track);
2836
+ this.assertRoom(room, generation3);
2837
+ return track;
2838
+ } catch (error) {
2839
+ this.stopTracks([track]);
2840
+ this.localTracks = this.localTracks.filter((item) => item !== track);
2841
+ try {
2842
+ await room.localParticipant.unpublishTrack(track);
2843
+ } catch {
2844
+ }
2845
+ throw error;
2846
+ }
2847
+ }
2848
+ emitMediaState() {
2849
+ const state = this.getMediaState();
2850
+ emitCallEvent("callMediaStateChanged" /* CallMediaStateChanged */, {
2851
+ callID: this.callID,
2852
+ ...state
2853
+ });
2854
+ return state;
2855
+ }
2856
+ };
2857
+ function normalizeLiveKitInfo(config) {
2858
+ return {
2859
+ ...config.liveKit,
2860
+ token: config.token || config.liveKit?.token || "",
2861
+ livekitUrl: config.livekitUrl || config.liveKit?.livekitUrl,
2862
+ liveKitUrl: config.liveKitUrl || config.liveKit?.liveKitUrl,
2863
+ roomName: config.roomName || config.liveKit?.roomName
2864
+ };
2865
+ }
2866
+ function setLocalTrackEnabled(track, enabled) {
2867
+ return enabled ? track.unmute() : track.mute();
2868
+ }
2869
+ var liveKitCallMediaAdapter = new LiveKitCallMediaAdapter();
2870
+ onSessionInvalidated(() => {
2871
+ void liveKitCallMediaAdapter.leaveMedia().catch((error) => console.error("[VanshiSdk] media cleanup failed:", error));
2872
+ });
2873
+
2874
+ // src/sdk/call/call.ts
2875
+ var CALL_SOCKET_EVENTS = {
2876
+ create: "call/create",
2877
+ inviteMembers: "call/inviteMembers",
2878
+ accept: "call/accept",
2879
+ reject: "call/reject",
2880
+ cancel: "call/cancel",
2881
+ hangup: "call/hangup",
2882
+ members: "call/members",
2883
+ token: "call/token",
2884
+ hasOngoingCall: "call/hasOngoingCall"
2885
+ };
2886
+ function callInvite(config) {
2887
+ const conversationType = normalizeCallConversationType(
2888
+ config.conversationType
2889
+ );
2890
+ return callRequest(CALL_SOCKET_EVENTS.create, {
2891
+ conversationID: config.conversationID,
2892
+ conversationType,
2893
+ invitees: config.invitees,
2894
+ callType: config.callType,
2895
+ mode: config.mode || (conversationType === 1 /* Group */ ? "group" /* Group */ : "one_on_one" /* OneOnOne */),
2896
+ ...config.timeout !== void 0 ? { timeout: config.timeout } : {},
2897
+ ...config.extendedData !== void 0 ? { extendedData: config.extendedData } : {}
2898
+ });
2899
+ }
2900
+ function callInviteMembers(params) {
2901
+ return callRequest(CALL_SOCKET_EVENTS.inviteMembers, {
2902
+ ...getCallBasePayload(params),
2903
+ invitees: params.invitees
2904
+ });
2905
+ }
2906
+ function callAccept(params) {
2907
+ return callRequest(
2908
+ CALL_SOCKET_EVENTS.accept,
2909
+ getCallBasePayload(params)
2910
+ );
2911
+ }
2912
+ function callReject(params) {
2913
+ return callRequest(
2914
+ CALL_SOCKET_EVENTS.reject,
2915
+ getCallBasePayload(params)
2916
+ );
2917
+ }
2918
+ function callCancel(params) {
2919
+ return callRequest(
2920
+ CALL_SOCKET_EVENTS.cancel,
2921
+ getCallBasePayload(params)
2922
+ );
2923
+ }
2924
+ async function callHangup(params) {
2925
+ try {
2926
+ const result = await callRequest(
2927
+ CALL_SOCKET_EVENTS.hangup,
2928
+ getCallBasePayload(params)
2929
+ );
2930
+ await liveKitCallMediaAdapter.leaveMedia();
2931
+ return result;
2932
+ } catch (error) {
2933
+ throwCallTransportError(error, "call hangup failed");
2934
+ }
2935
+ }
2936
+ function callQueryMembers(params) {
2937
+ return callRequest(
2938
+ CALL_SOCKET_EVENTS.members,
2939
+ getCallBasePayload(params)
2940
+ );
2941
+ }
2942
+ function callHasOngoingCall(params) {
2943
+ return callRequest(CALL_SOCKET_EVENTS.hasOngoingCall, {
2944
+ groupId: params.groupId
2945
+ });
2946
+ }
2947
+ function callGetLiveKitToken(params) {
2948
+ return callRequest(CALL_SOCKET_EVENTS.token, {
2949
+ ...getCallBasePayload(params),
2950
+ ...params.roomName !== void 0 ? { roomName: params.roomName } : {}
2951
+ });
2952
+ }
2953
+ async function callJoinMedia(config, connectOptions) {
2954
+ const session = captureSession();
2955
+ try {
2956
+ const liveKit = hasLiveKitToken(config) ? {
2957
+ ...config.liveKit,
2958
+ token: config.token || config.liveKit?.token || "",
2959
+ livekitUrl: config.livekitUrl || config.liveKit?.livekitUrl,
2960
+ liveKitUrl: config.liveKitUrl || config.liveKit?.liveKitUrl,
2961
+ roomName: config.roomName || config.liveKit?.roomName
2962
+ } : await callGetLiveKitToken(config);
2963
+ assertCurrentSession(session);
2964
+ return await liveKitCallMediaAdapter.joinMedia(
2965
+ { ...config, liveKit },
2966
+ connectOptions
2967
+ );
2968
+ } catch (error) {
2969
+ throwCallTransportError(error, "call media join failed");
2970
+ }
2971
+ }
2972
+ async function callLeaveMedia() {
2973
+ try {
2974
+ await liveKitCallMediaAdapter.leaveMedia();
2975
+ } catch (error) {
2976
+ throwCallTransportError(error, "call media leave failed");
2977
+ }
2978
+ }
2979
+ async function callSetMicrophoneEnabled(enabled) {
2980
+ try {
2981
+ return await liveKitCallMediaAdapter.setMicrophoneEnabled(enabled);
2982
+ } catch (error) {
2983
+ throwCallTransportError(error, "microphone state update failed");
2984
+ }
2985
+ }
2986
+ async function callSetCameraEnabled(enabled) {
2987
+ try {
2988
+ return await liveKitCallMediaAdapter.setCameraEnabled(enabled);
2989
+ } catch (error) {
2990
+ throwCallTransportError(error, "camera state update failed");
2991
+ }
2992
+ }
2993
+ async function callStartScreenShare() {
2994
+ try {
2995
+ return await liveKitCallMediaAdapter.startScreenShare();
2996
+ } catch (error) {
2997
+ throwCallTransportError(error, "screen share start failed");
2998
+ }
2999
+ }
3000
+ async function callStopScreenShare() {
3001
+ try {
3002
+ return await liveKitCallMediaAdapter.stopScreenShare();
3003
+ } catch (error) {
3004
+ throwCallTransportError(error, "screen share stop failed");
3005
+ }
3006
+ }
3007
+ function callGetMediaState() {
3008
+ return liveKitCallMediaAdapter.getMediaState();
3009
+ }
3010
+ function callGetRoom() {
3011
+ return liveKitCallMediaAdapter.getRoom();
3012
+ }
3013
+ function hasLiveKitToken(config) {
3014
+ return Boolean(config.liveKit?.token || config.token);
3015
+ }
3016
+ function normalizeCallConversationType(conversationType) {
3017
+ return Number(conversationType) === 2 ? 1 /* Group */ : Number(conversationType);
3018
+ }
3019
+ function getCallBasePayload(params) {
3020
+ return {
3021
+ callID: params.callID,
3022
+ ...params.extendedData !== void 0 ? { extendedData: params.extendedData } : {}
3023
+ };
3024
+ }
3025
+ async function callRequest(event, payload) {
3026
+ try {
3027
+ return unwrapSdkData(
3028
+ await request({
3029
+ path: `/sdk/${event}`,
3030
+ method: "POST",
3031
+ data: payload
3032
+ })
3033
+ );
3034
+ } catch (error) {
3035
+ throwCallTransportError(error);
3036
+ }
3037
+ }
3038
+ function throwCallTransportError(error, fallbackMessage = "call request failed") {
3039
+ if (isSdkRuntimeErrorObject(error)) {
3040
+ throw error;
3041
+ }
3042
+ throwSdkTransportError(error, fallbackMessage);
3043
+ }
3044
+ var call_default = {
3045
+ callInvite,
3046
+ callInviteMembers,
3047
+ callAccept,
3048
+ callReject,
3049
+ callCancel,
3050
+ callHangup,
3051
+ callQueryMembers,
3052
+ callHasOngoingCall,
3053
+ callGetLiveKitToken,
3054
+ callJoinMedia,
3055
+ callLeaveMedia,
3056
+ callSetMicrophoneEnabled,
3057
+ callSetCameraEnabled,
3058
+ callStartScreenShare,
3059
+ callStopScreenShare,
3060
+ callGetMediaState,
3061
+ callGetRoom
3062
+ };
3063
+
552
3064
  // src/sdk/login.ts
553
- async function login(payload) {
3065
+ async function loginInternal(payload, session) {
554
3066
  return performLogin(payload, {
555
- requestLogin: postSdkLoginApiRequest
3067
+ requestLogin: postSdkLoginApiRequest,
3068
+ isCurrent: () => isCurrentSession(session),
3069
+ clearSession: () => {
3070
+ }
556
3071
  });
557
3072
  }
558
- registerReconnectLoginHandler((token) => login({ token }));
559
- async function logout(disconnect = true) {
560
- let success = false;
3073
+ async function login(payload) {
3074
+ clearSdkSession();
3075
+ const attempt = beginAuthentication();
3076
+ setSdkUserID(payload.userID || payload.userId);
3077
+ if (payload.userId?.trim())
3078
+ beginLoginMessageBuffer(attempt);
561
3079
  try {
562
- const resp = await postSdkLogoutApiRequest(
563
- {}
564
- );
565
- let res = JSON.parse(resp);
566
- const result = unwrapSdkData(res);
567
- success = Boolean(result) || isSuccessCode(res.code);
568
- } catch {
569
- success = false;
570
- } finally {
3080
+ const success = await loginInternal({ token: payload.token }, attempt);
3081
+ if (!isCurrentSession(attempt))
3082
+ return false;
3083
+ if (!success) {
3084
+ clearSdkSession();
3085
+ rejectQueuedRequestsAfterLoginFailure();
3086
+ return false;
3087
+ }
3088
+ if (payload.userId?.trim()) {
3089
+ await openLocalStorage(payload.userId.trim(), JSON.stringify([getSocketUrl(), getUploadUrl()]));
3090
+ if (!isCurrentSession(attempt))
3091
+ return false;
3092
+ await flushLoginMessageBuffer(attempt);
3093
+ }
3094
+ if (!isCurrentSession(attempt))
3095
+ return false;
3096
+ finishAuthentication(attempt);
3097
+ resumeQueuedRequestsAfterLogin();
3098
+ return true;
3099
+ } catch (error) {
3100
+ if (!isCurrentSession(attempt))
3101
+ return false;
571
3102
  clearSdkSession();
3103
+ rejectQueuedRequestsAfterLoginFailure(error);
3104
+ throw error;
3105
+ } finally {
3106
+ discardLoginMessageBuffer(attempt);
3107
+ finishAuthentication(attempt);
572
3108
  }
573
- return success;
3109
+ }
3110
+ registerReconnectLoginHandler((token) => loginInternal({ token }, captureSession()));
3111
+ function isLogoutFinalizationError(error) {
3112
+ const message = getSdkErrorMessageFromUnknown(error, "");
3113
+ const code = getSdkErrorCode(error);
3114
+ return code === 3 || code === "3" || code === 4 || code === "4" || code === 303 || code === "303" || /ack timeout|request timeout|socket not connected|socket not initialized|disconnected|client disconnected|connect timeout|reconnect failed|restore login failed|socket operation queue is full|认证失败|token验证失败/i.test(message);
3115
+ }
3116
+ async function logout(disconnect = false) {
3117
+ clearSdkSession();
3118
+ const attempt = captureSession();
3119
+ try {
3120
+ try {
3121
+ await callLeaveMedia();
3122
+ } catch (error) {
3123
+ console.error("[VanshiSdk] leave media during logout failed:", error);
3124
+ }
3125
+ if (!isCurrentSession(attempt))
3126
+ return false;
3127
+ const response = await postSdkLogoutApiRequest({});
3128
+ if (!isCurrentSession(attempt))
3129
+ return false;
3130
+ const result = unwrapSdkResponse(response);
3131
+ return Boolean(result.data) || isSuccessCode(result.code);
3132
+ } catch (error) {
3133
+ if (!isCurrentSession(attempt))
3134
+ return false;
3135
+ if (isLogoutFinalizationError(error)) {
3136
+ return false;
3137
+ }
3138
+ throw error;
3139
+ } finally {
3140
+ if (isCurrentSession(attempt)) {
3141
+ clearSdkSession();
3142
+ if (disconnect)
3143
+ disconnectSocket();
3144
+ }
3145
+ }
3146
+ }
3147
+
3148
+ // src/sdk/storage/history.ts
3149
+ import Dexie2 from "dexie";
3150
+ async function mergeHistoryWithLocalMessages(ctx, query, result) {
3151
+ if (!Array.isArray(result?.messageList))
3152
+ return result;
3153
+ if (!ctx)
3154
+ return filterInvalidReplyMessages(result);
3155
+ if (!isCurrentStorage(ctx))
3156
+ throw new Error("Local storage account changed during history query");
3157
+ await ctx.pending;
3158
+ const merged = await storageWork(ctx, async (ctx2) => {
3159
+ const key = conversationKey(query.conversationID, query.conversationType);
3160
+ return ctx2.db.transaction("r", ctx2.db.messages, async () => {
3161
+ const serverIDs = result.messageList.filter((row) => row.messageID).map((row) => [key, String(row.messageID)]);
3162
+ const clientIDs = result.messageList.filter((row) => row.clientBizId).map((row) => [key, String(row.clientBizId)]);
3163
+ const rowsByID = /* @__PURE__ */ new Map();
3164
+ const include = (rows2) => rows2.forEach((row) => rowsByID.set(row.localID, row));
3165
+ if (serverIDs.length)
3166
+ include(await ctx2.db.messages.where("[conversationKey+messageID]").anyOf(serverIDs).toArray());
3167
+ if (clientIDs.length)
3168
+ include(await ctx2.db.messages.where("[conversationKey+clientBizId]").anyOf(clientIDs).toArray());
3169
+ if (!query.config?.nextMessage) {
3170
+ const timestamps = result.messageList.map((row) => Number(row.timestamp)).filter(Number.isFinite);
3171
+ const oldest = timestamps.reduce((min, value) => Math.min(min, value), Infinity);
3172
+ include(await ctx2.db.messages.where("[conversationKey+timestamp]").between([key, timestamps.length ? oldest : Dexie2.minKey], [key, Dexie2.maxKey], true, true).toArray());
3173
+ include(await ctx2.db.messages.where("[conversationKey+sendState]").anyOf([[key, 0], [key, 2]]).toArray());
3174
+ include(await ctx2.db.messages.where("[conversationKey+hasServerID]").equals([key, 0]).toArray());
3175
+ }
3176
+ const rows = [...rowsByID.values()];
3177
+ const byServer = new Map(rows.filter((row) => row.messageID).map((row) => [String(row.messageID), row]));
3178
+ const byClient = new Map(rows.filter((row) => row.clientBizId).map((row) => [String(row.clientBizId), row]));
3179
+ const included = /* @__PURE__ */ new Set();
3180
+ const messageList = [];
3181
+ const add = (message) => {
3182
+ const identities = [
3183
+ message.localID && `local:${message.localID}`,
3184
+ message.messageID && `server:${message.messageID}`,
3185
+ message.clientBizId && `client:${message.clientBizId}`
3186
+ ].filter(Boolean);
3187
+ if (identities.some((id) => included.has(id)))
3188
+ return;
3189
+ identities.forEach((id) => included.add(id));
3190
+ messageList.push(message);
3191
+ };
3192
+ const first = Number(result.messageList[0]?.timestamp);
3193
+ const last = Number(result.messageList[result.messageList.length - 1]?.timestamp);
3194
+ const descending = Number.isFinite(first) && Number.isFinite(last) && first !== last ? first > last : query.config?.reverse !== false;
3195
+ for (const message of result.messageList) {
3196
+ const client = byClient.get(String(message.clientBizId));
3197
+ const local = byServer.get(String(message.messageID)) || (client && (!message.messageID || !client.messageID || String(message.messageID) === client.messageID) ? client : void 0);
3198
+ if (local?.deleted)
3199
+ continue;
3200
+ if (isSelfReferencingReply(message)) {
3201
+ if (local)
3202
+ add(displayMessage(local));
3203
+ continue;
3204
+ }
3205
+ if (local?.revoked) {
3206
+ add(displayMessage(local));
3207
+ continue;
3208
+ }
3209
+ add(local ? { ...message, ...displayMessage(local) } : message);
3210
+ }
3211
+ if (!query.config?.nextMessage) {
3212
+ const timestamps = result.messageList.map((message) => Number(message.timestamp)).filter(Number.isFinite);
3213
+ const oldest = timestamps.reduce((min, timestamp) => Math.min(min, timestamp), Infinity);
3214
+ for (const row of rows) {
3215
+ if (!row.deleted && (row.sendState !== 1 || !row.messageID || !timestamps.length || row.timestamp >= oldest))
3216
+ add(displayMessage(row));
3217
+ }
3218
+ }
3219
+ messageList.sort((a, b) => {
3220
+ const timeA = Number(a.timestamp) || 0;
3221
+ const timeB = Number(b.timestamp) || 0;
3222
+ const keyA = String(a.localID || a.messageID || a.clientBizId || "");
3223
+ const keyB = String(b.localID || b.messageID || b.clientBizId || "");
3224
+ return (descending ? -1 : 1) * (timeA - timeB || compareOrderKey(a.orderKey, b.orderKey) || keyA.localeCompare(keyB));
3225
+ });
3226
+ return { ...result, messageList };
3227
+ });
3228
+ });
3229
+ if (!isCurrentStorage(ctx))
3230
+ throw new Error("Local storage account changed during history query");
3231
+ return merged || filterInvalidReplyMessages(result);
3232
+ }
3233
+ function compareOrderKey(a, b) {
3234
+ const validA = /^\d+$/.test(String(a));
3235
+ const validB = /^\d+$/.test(String(b));
3236
+ if (validA !== validB)
3237
+ return validA ? -1 : 1;
3238
+ if (!validA)
3239
+ return 0;
3240
+ const left = String(a).replace(/^0+(?=\d)/, "");
3241
+ const right = String(b).replace(/^0+(?=\d)/, "");
3242
+ return left.length - right.length || (left < right ? -1 : left > right ? 1 : 0);
3243
+ }
3244
+ function displayMessage(row) {
3245
+ const { _mutationAt, _mutationEpoch, _contentSequence, _receiptSequence, _sendSequence, _sendEpoch, ...message } = row;
3246
+ return {
3247
+ ...restoreMediaPreviews(message),
3248
+ localMessageID: row.localMessageID || row.localID,
3249
+ messageID: row.messageID || "",
3250
+ clientBizId: row.clientBizId || "",
3251
+ message: row.message ?? row.massage ?? "",
3252
+ massage: row.massage ?? row.message ?? "",
3253
+ sentStatus: row.sentStatus ?? row.sendState
3254
+ };
3255
+ }
3256
+
3257
+ // src/sdk/message/clientBizId.ts
3258
+ var sequence = 0;
3259
+ function withClientBizId(message) {
3260
+ sequence = (sequence + 1) % 1e6;
3261
+ return {
3262
+ ...message,
3263
+ clientBizId: message.clientBizId || `cbiz_${Date.now()}_${sequence.toString(36)}_${Math.random().toString(36).slice(2, 10)}`
3264
+ };
574
3265
  }
575
3266
 
576
3267
  // src/apis/SdkSendMessagePostApi.ts
@@ -646,7 +3337,6 @@ var getVideoDurationAndFirstFrame = (file) => {
646
3337
  video.onloadedmetadata = () => {
647
3338
  canvas.width = video.videoWidth || 640;
648
3339
  canvas.height = video.videoHeight || 360;
649
- window.URL.revokeObjectURL(video.src);
650
3340
  duration = video?.duration;
651
3341
  videoWidth = video.videoWidth || 640;
652
3342
  videoHeight = video.videoHeight || 360;
@@ -707,6 +3397,24 @@ function getAudioDuration(file) {
707
3397
  });
708
3398
  }
709
3399
 
3400
+ // src/constants/messages/Message.ts
3401
+ var SEDMessageType = /* @__PURE__ */ ((SEDMessageType2) => {
3402
+ SEDMessageType2["Unknown"] = "0";
3403
+ SEDMessageType2["Command"] = "2";
3404
+ SEDMessageType2["Barrage"] = "20";
3405
+ SEDMessageType2["Text"] = "1";
3406
+ SEDMessageType2["Image"] = "11";
3407
+ SEDMessageType2["File"] = "12";
3408
+ SEDMessageType2["Audio"] = "13";
3409
+ SEDMessageType2["Video"] = "14";
3410
+ SEDMessageType2["System"] = "30";
3411
+ SEDMessageType2["Revoke"] = "31";
3412
+ SEDMessageType2["Tips"] = "32";
3413
+ SEDMessageType2["Combine"] = "100";
3414
+ SEDMessageType2["Custom"] = "200";
3415
+ return SEDMessageType2;
3416
+ })(SEDMessageType || {});
3417
+
710
3418
  // src/apis/SdkDeleteAllMessagePostApi.ts
711
3419
  var postSdkDeleteAllMessageApiRequest = (data) => {
712
3420
  return request({
@@ -716,28 +3424,78 @@ var postSdkDeleteAllMessageApiRequest = (data) => {
716
3424
  });
717
3425
  };
718
3426
 
719
- // src/sdk/message.ts
720
- var clientBizIdSeq = 0;
3427
+ // src/sdk/message/message.ts
721
3428
  var MAX_FILE_NAME_LENGTH = 20;
722
- function generateClientBizId() {
723
- clientBizIdSeq = (clientBizIdSeq + 1) % 1e6;
724
- const timestamp = Date.now();
725
- const sequence = clientBizIdSeq.toString(36);
726
- const random = Math.random().toString(36).slice(2, 10);
727
- return `cbiz_${timestamp}_${sequence}_${random}`;
728
- }
729
- function withClientBizId(message) {
730
- return {
731
- ...message,
732
- clientBizId: message.clientBizId || generateClientBizId()
733
- };
3429
+ function sliceChars(value, length) {
3430
+ return Array.from(value).slice(0, length).join("");
734
3431
  }
735
3432
  function truncateFileName(fileName) {
736
- return fileName.length > MAX_FILE_NAME_LENGTH ? fileName.slice(0, MAX_FILE_NAME_LENGTH) : fileName;
3433
+ const lastDotIndex = fileName.lastIndexOf(".");
3434
+ const hasExtension = lastDotIndex > 0 && lastDotIndex < fileName.length - 1;
3435
+ if (!hasExtension) {
3436
+ return sliceChars(fileName, MAX_FILE_NAME_LENGTH);
3437
+ }
3438
+ const baseName = fileName.slice(0, lastDotIndex);
3439
+ const extension = fileName.slice(lastDotIndex);
3440
+ return `${sliceChars(baseName, MAX_FILE_NAME_LENGTH)}${extension}`;
737
3441
  }
738
3442
  function getDisplayFileName(file, fallback = "") {
739
3443
  return truncateFileName(file?.name || fallback || "");
740
3444
  }
3445
+ function appendUploadFile(formData, file, fileName) {
3446
+ const uploadFileName = getDisplayFileName(file, fileName);
3447
+ if (uploadFileName) {
3448
+ formData.append("file", file, uploadFileName);
3449
+ return;
3450
+ }
3451
+ formData.append("file", file);
3452
+ }
3453
+ function assertMediaFileHasContent(file, fileName) {
3454
+ if (!file || typeof file.size !== "number" || file.size <= 0) {
3455
+ throwSdkRuntimeError({
3456
+ code: 2,
3457
+ message: "media message file is empty",
3458
+ raw: {
3459
+ fileName: fileName || file?.name || "",
3460
+ fileSize: file?.size ?? 0,
3461
+ fileType: file?.type || ""
3462
+ }
3463
+ });
3464
+ }
3465
+ }
3466
+ function debugUploadFile(file, fileName) {
3467
+ if (typeof window !== "undefined" && window.__VANSHI_SDK_DEBUG__) {
3468
+ console.debug("[VanshiSdk] uploading media file", {
3469
+ fileName: fileName || file?.name || "",
3470
+ fileSize: file.size,
3471
+ fileType: file.type || ""
3472
+ });
3473
+ }
3474
+ }
3475
+ async function createUploadBlob(file, fileName) {
3476
+ assertMediaFileHasContent(file, fileName);
3477
+ let bytes;
3478
+ try {
3479
+ bytes = await file.arrayBuffer();
3480
+ } catch (error) {
3481
+ throwSdkTransportError(error, "media message file cannot be read");
3482
+ }
3483
+ if (bytes.byteLength <= 0) {
3484
+ throwSdkRuntimeError({
3485
+ code: 2,
3486
+ message: "media message file is empty",
3487
+ raw: {
3488
+ fileName: fileName || file?.name || "",
3489
+ fileSize: file.size,
3490
+ readSize: bytes.byteLength,
3491
+ fileType: file.type || ""
3492
+ }
3493
+ });
3494
+ }
3495
+ return new Blob([bytes], {
3496
+ type: file.type || "application/octet-stream"
3497
+ });
3498
+ }
741
3499
  function createLocalMediaMessage(message, toConversationID, conversationType) {
742
3500
  const file = message.file;
743
3501
  const localPreviewUrl = file && typeof URL !== "undefined" && URL.createObjectURL ? URL.createObjectURL(file) : void 0;
@@ -753,9 +3511,10 @@ function createLocalMediaMessage(message, toConversationID, conversationType) {
753
3511
  clientBizId: message.clientBizId,
754
3512
  conversationID: toConversationID,
755
3513
  conversationType,
756
- senderUserID: "",
3514
+ senderUserID: getSdkUserID(),
757
3515
  direction: 0,
758
3516
  sentStatus: 0,
3517
+ sendState: 0,
759
3518
  timestamp: Date.now(),
760
3519
  receiptStatus: 0,
761
3520
  isServerMessage: false,
@@ -766,13 +3525,38 @@ function createLocalMediaMessage(message, toConversationID, conversationType) {
766
3525
  fileName: getDisplayFileName(file, message.fileName),
767
3526
  fileSize: file ? String(file.size) : message.fileSize || "",
768
3527
  fileDownloadUrl: message.fileDownloadUrl || localPreviewUrl || "",
769
- thumbnailDownloadUrl: message.thumbnailDownloadUrl || localPreviewUrl || "",
3528
+ thumbnailDownloadUrl: message.thumbnailDownloadUrl || (isSameUid(message.type, "11" /* Image */) ? localPreviewUrl : "") || "",
770
3529
  thumbnailWidth: message.thumbnailWidth || "",
771
3530
  thumbnailHeight: message.thumbnailHeight || "",
772
3531
  duration: message.duration || "",
773
3532
  localPreviewUrl
774
3533
  };
775
3534
  }
3535
+ function createLocalTextMessage(message, toConversationID, conversationType) {
3536
+ return {
3537
+ massage: message.message,
3538
+ message: message.message,
3539
+ type: message.type,
3540
+ extendedData: message.extendedData,
3541
+ messageID: "",
3542
+ orderKey: "",
3543
+ clientSid: "",
3544
+ localMessageID: message.clientBizId,
3545
+ clientBizId: message.clientBizId,
3546
+ conversationID: toConversationID,
3547
+ conversationType,
3548
+ senderUserID: "",
3549
+ direction: 0,
3550
+ sentStatus: 0,
3551
+ sendState: 0,
3552
+ timestamp: String(Date.now()),
3553
+ receiptStatus: 0,
3554
+ isServerMessage: false,
3555
+ isBroadcastMessage: false,
3556
+ isGroupTargetedMessage: "",
3557
+ id: ""
3558
+ };
3559
+ }
776
3560
  async function getImageSize(file) {
777
3561
  return new Promise((resolve, reject) => {
778
3562
  const url = URL.createObjectURL(file);
@@ -792,37 +3576,65 @@ async function getImageSize(file) {
792
3576
  });
793
3577
  }
794
3578
  async function uploadSdkFile(file, fileName) {
3579
+ const session = captureSession();
3580
+ const storage = captureStorage();
3581
+ const uploadFile = await createUploadBlob(file, fileName);
3582
+ const uploadFileName = getDisplayFileName(file, fileName) || "file";
3583
+ debugUploadFile(uploadFile, uploadFileName);
795
3584
  const formData = new FormData();
796
- const uploadFileName = truncateFileName(fileName || file.name || "");
797
- if (uploadFileName) {
798
- formData.append("file", file, uploadFileName);
799
- } else {
800
- formData.append("file", file);
801
- }
802
- const response = await fetch(getUploadUrl(), {
803
- method: "POST",
804
- headers: {
805
- Authorization: getStoredToken()
806
- },
807
- body: formData
808
- });
809
- if (!response.ok) {
810
- throw new Error(`file upload failed: ${response.status}`);
3585
+ appendUploadFile(formData, uploadFile, uploadFileName);
3586
+ const formDataFile = formData.get("file");
3587
+ if (formDataFile instanceof Blob) {
3588
+ assertMediaFileHasContent(formDataFile, uploadFileName);
811
3589
  }
812
- const payload = await response.json();
813
- if (payload.Code !== 0 && payload.Code !== "0") {
814
- throw new Error(payload.Message || "file upload failed");
815
- }
816
- if (!payload.Result) {
817
- throw new Error("file upload result is empty");
3590
+ try {
3591
+ assertCurrentSession(session);
3592
+ if (storage && !isCurrentStorage(storage))
3593
+ throw new Error("Local storage account changed before upload");
3594
+ const response = await fetch(getUploadUrl(), {
3595
+ method: "POST",
3596
+ headers: {
3597
+ Authorization: getStoredToken()
3598
+ },
3599
+ body: formData
3600
+ });
3601
+ assertCurrentSession(session);
3602
+ if (!response.ok) {
3603
+ throwSdkRuntimeError({
3604
+ code: response.status,
3605
+ message: `file upload failed: ${response.status}`,
3606
+ raw: response
3607
+ });
3608
+ }
3609
+ const payload = await response.json();
3610
+ assertCurrentSession(session);
3611
+ const result = assertSdkSuccess({
3612
+ code: payload.Code,
3613
+ message: payload.Message,
3614
+ data: payload.Result || payload.data,
3615
+ raw: payload
3616
+ });
3617
+ if (!result.data) {
3618
+ throwSdkRuntimeError({
3619
+ message: "file upload result is empty",
3620
+ raw: payload
3621
+ });
3622
+ }
3623
+ return result.data;
3624
+ } catch (error) {
3625
+ throwSdkTransportError(error, "file upload failed");
818
3626
  }
819
- return payload.Result;
820
3627
  }
821
- async function enrichMediaMessage(message) {
3628
+ async function enrichMediaMessage(message, onVideoPrepared, assertSession = () => {
3629
+ }) {
822
3630
  const file = message.file;
823
3631
  if (!file) {
824
- throw new Error("media message file is required");
3632
+ throwSdkRuntimeError({
3633
+ message: "media message file is required",
3634
+ raw: message
3635
+ });
825
3636
  }
3637
+ assertMediaFileHasContent(file, message.fileName);
826
3638
  const enrichedMessage = {
827
3639
  ...message,
828
3640
  fileName: getDisplayFileName(file, message.fileName),
@@ -840,11 +3652,14 @@ async function enrichMediaMessage(message) {
840
3652
  enrichedMessage.duration = String(res.duration);
841
3653
  enrichedMessage.thumbnailWidth = String(res.videoWidth);
842
3654
  enrichedMessage.thumbnailHeight = String(res.videoHeight);
3655
+ await onVideoPrepared?.(res);
3656
+ assertSession();
843
3657
  enrichedMessage.thumbnailDownloadUrl = await uploadSdkFile(
844
3658
  res.firstFrameFile,
845
3659
  res.firstFrameFile.name
846
3660
  );
847
3661
  }
3662
+ assertSession();
848
3663
  const fileDownloadUrl = await uploadSdkFile(file, enrichedMessage.fileName);
849
3664
  enrichedMessage.fileDownloadUrl = fileDownloadUrl;
850
3665
  if (isSameUid(message.type, "11" /* Image */)) {
@@ -853,19 +3668,20 @@ async function enrichMediaMessage(message) {
853
3668
  delete enrichedMessage.file;
854
3669
  return enrichedMessage;
855
3670
  }
856
- async function sendMessage(payload) {
857
- const response = await postSdkSendMessageApiRequest({
858
- ...payload,
859
- message: withClientBizId(payload.message)
860
- });
861
- return unwrapSdkData(response);
862
- }
863
- async function sendMediaMessage(payload, options) {
3671
+ async function sendMessage(payload, options) {
3672
+ const session = captureSession();
3673
+ if (isAuthenticating())
3674
+ await waitForAuthentication(session);
3675
+ const storage = captureStorage();
864
3676
  const messageWithClientBizId = withClientBizId(payload.message);
3677
+ await persistOutgoing(storage, { ...payload, message: messageWithClientBizId });
3678
+ if (storage && !isCurrentStorage(storage))
3679
+ throw new Error("Local storage account changed before sending");
3680
+ assertCurrentSession(session);
865
3681
  if (options?.onMessageAttached) {
866
3682
  try {
867
3683
  options.onMessageAttached(
868
- createLocalMediaMessage(
3684
+ createLocalTextMessage(
869
3685
  messageWithClientBizId,
870
3686
  payload.toConversationID,
871
3687
  payload.conversationType
@@ -874,15 +3690,77 @@ async function sendMediaMessage(payload, options) {
874
3690
  } catch {
875
3691
  }
876
3692
  }
877
- const response = await postSdkSendMediaMessageApiRequest({
3693
+ assertCurrentSession(session);
3694
+ const response = await postSdkSendMessageApiRequest({
878
3695
  ...payload,
879
- message: await enrichMediaMessage(messageWithClientBizId)
3696
+ message: messageWithClientBizId
880
3697
  });
881
- return unwrapSdkData(response);
3698
+ const result = unwrapSdkData(response);
3699
+ return result ? {
3700
+ ...result,
3701
+ message: result.message || result.massage
3702
+ } : result;
3703
+ }
3704
+ async function sendMediaMessage(payload, options) {
3705
+ const session = captureSession();
3706
+ if (isAuthenticating())
3707
+ await waitForAuthentication(session);
3708
+ const storage = captureStorage();
3709
+ const messageWithClientBizId = withClientBizId(payload.message);
3710
+ const localMessage = createLocalMediaMessage(messageWithClientBizId, payload.toConversationID, payload.conversationType);
3711
+ await persistOutgoing(storage, { ...payload, message: localMessage });
3712
+ const notify = () => {
3713
+ assertCurrentSession(session);
3714
+ if (storage && !isCurrentStorage(storage))
3715
+ throw new Error("Local storage account changed during media preparation");
3716
+ try {
3717
+ options?.onMessageAttached?.({ ...localMessage });
3718
+ } catch {
3719
+ }
3720
+ };
3721
+ try {
3722
+ notify();
3723
+ const enriched = await enrichMediaMessage(messageWithClientBizId, async (video) => {
3724
+ assertCurrentSession(session);
3725
+ if (storage && !isCurrentStorage(storage))
3726
+ throw new Error("Local storage account changed during media preparation");
3727
+ Object.assign(localMessage, {
3728
+ duration: String(video.duration),
3729
+ thumbnailWidth: String(video.videoWidth),
3730
+ thumbnailHeight: String(video.videoHeight),
3731
+ thumbnailFile: video.firstFrameFile,
3732
+ thumbnailDownloadUrl: URL.createObjectURL(video.firstFrameFile)
3733
+ });
3734
+ await persistOutgoing(storage, { ...payload, message: localMessage });
3735
+ notify();
3736
+ }, () => assertCurrentSession(session));
3737
+ if (storage && !isCurrentStorage(storage))
3738
+ throw new Error("Local storage account changed during media preparation");
3739
+ assertCurrentSession(session);
3740
+ const response = await postSdkSendMediaMessageApiRequest({
3741
+ ...payload,
3742
+ message: enriched
3743
+ });
3744
+ return unwrapSdkData(response);
3745
+ } catch (error) {
3746
+ await markSendError(storage, messageWithClientBizId.clientBizId, outgoingFallback(payload, storage), error);
3747
+ if (isSdkRuntimeErrorObject(error)) {
3748
+ throw error;
3749
+ }
3750
+ throwSdkTransportError(error, "send media message failed");
3751
+ }
882
3752
  }
883
3753
  async function queryHistoryMessage(payload) {
3754
+ const session = captureSession();
3755
+ if (isAuthenticating())
3756
+ await waitForAuthentication(session);
3757
+ const storage = captureStorage();
884
3758
  const response = await postSdkQueryHistoryMessageApiRequest(payload);
885
- return unwrapSdkData(response);
3759
+ assertCurrentSession(session);
3760
+ const result = unwrapSdkData(response);
3761
+ const merged = await mergeHistoryWithLocalMessages(storage, payload, result);
3762
+ assertCurrentSession(session);
3763
+ return merged;
886
3764
  }
887
3765
  async function deleteAllConversationMessages() {
888
3766
  const response = await postSdkDeleteAllConversationMessagesApiRequest(
@@ -897,7 +3775,7 @@ async function deleteAllConversationMessages() {
897
3775
  }
898
3776
  async function deleteMessages(conversationID, conversationType, messageList) {
899
3777
  const response = await postSdkDeleteMessagesApiRequest({
900
- conversationID: Number(conversationID),
3778
+ conversationID,
901
3779
  conversationType: conversationType.toString(),
902
3780
  messageList: messageList.map((msg) => ({
903
3781
  messageID: msg.messageID,
@@ -912,11 +3790,20 @@ async function revokeMessage(message, config) {
912
3790
  }
913
3791
  async function deleteAllMessage(conversationID, conversationType) {
914
3792
  const response = await postSdkDeleteAllMessageApiRequest({
915
- conversationID: Number(conversationID),
3793
+ conversationID,
916
3794
  conversationType: conversationType.toString()
917
3795
  });
918
3796
  return unwrapSdkResponse(response);
919
3797
  }
3798
+ var message_default = {
3799
+ sendMessage,
3800
+ sendMediaMessage,
3801
+ queryHistoryMessage,
3802
+ deleteAllConversationMessages,
3803
+ deleteMessages,
3804
+ revokeMessage,
3805
+ deleteAllMessage
3806
+ };
920
3807
 
921
3808
  // src/apis/SdkQueryGroupMessageReceiptReadMemberListPostApi.ts
922
3809
  var postSdkQueryGroupMessageReceiptReadMemberListApiRequest = (data) => {
@@ -954,7 +3841,7 @@ var postSdkSendMessageReceiptsReadApiRequest = (data) => {
954
3841
  });
955
3842
  };
956
3843
 
957
- // src/sdk/messageReceipts.ts
3844
+ // src/sdk/message/messageReceipts.ts
958
3845
  async function sendMessageReceiptsRead(conversationID, conversationType, messageList) {
959
3846
  const response = await postSdkSendMessageReceiptsReadApiRequest({
960
3847
  conversationID,
@@ -965,7 +3852,7 @@ async function sendMessageReceiptsRead(conversationID, conversationType, message
965
3852
  }
966
3853
  async function queryMessageReceiptsInfo(conversationID, conversationType, messageList) {
967
3854
  const response = await postSdkQueryMessageReceiptsInfoApiRequest({
968
- conversationID: Number(conversationID),
3855
+ conversationID,
969
3856
  conversationType: conversationType.toString(),
970
3857
  messageList
971
3858
  });
@@ -987,12 +3874,606 @@ async function queryGroupMessageReceiptReadMemberList(groupID, config, message)
987
3874
  });
988
3875
  return unwrapSdkResponse(response);
989
3876
  }
3877
+ var messageReceipts_default = {
3878
+ sendMessageReceiptsRead,
3879
+ queryMessageReceiptsInfo,
3880
+ queryGroupMessageReceiptUnreadMemberList,
3881
+ queryGroupMessageReceiptReadMemberList
3882
+ };
3883
+
3884
+ // src/sdk/message/index.ts
3885
+ var MessageIndexApis = {
3886
+ ...message_default,
3887
+ ...messageReceipts_default
3888
+ };
3889
+
3890
+ // src/sdk/zimApiCore.ts
3891
+ function isFileLike(value) {
3892
+ return typeof File !== "undefined" && value instanceof File;
3893
+ }
3894
+ function isBlobLike(value) {
3895
+ return typeof Blob !== "undefined" && value instanceof Blob;
3896
+ }
3897
+ function normalizeSdkPayload(value, omitLocalMessageFiles = false) {
3898
+ if (!value || typeof value !== "object") {
3899
+ return value;
3900
+ }
3901
+ if (value instanceof Date || value instanceof ArrayBuffer || ArrayBuffer.isView(value) || isFileLike(value) || isBlobLike(value)) {
3902
+ return value;
3903
+ }
3904
+ if (value instanceof Map) {
3905
+ const entries = Object.fromEntries(value);
3906
+ return omitLocalMessageFiles ? normalizeSdkPayload(entries, true) : entries;
3907
+ }
3908
+ if (Array.isArray(value)) {
3909
+ return value.map((item) => normalizeSdkPayload(item, omitLocalMessageFiles));
3910
+ }
3911
+ const normalized = {};
3912
+ Object.entries(value).forEach(([key, item]) => {
3913
+ if (omitLocalMessageFiles && (["fileLocalPath", "file", "thumbnailFile", "localPreviewUrl", "localID", "conversationKey", "updatedAt", "errorCode", "errorMessage"].includes(key) || key.startsWith("_")))
3914
+ return;
3915
+ if (omitLocalMessageFiles && ["fileDownloadUrl", "thumbnailDownloadUrl"].includes(key) && typeof item === "string" && item.startsWith("blob:"))
3916
+ return;
3917
+ if (typeof item !== "function" && item !== void 0) {
3918
+ normalized[key] = normalizeSdkPayload(item, omitLocalMessageFiles);
3919
+ }
3920
+ });
3921
+ return normalized;
3922
+ }
3923
+ async function callSdkApi(method, payload = {}) {
3924
+ const response = await request({
3925
+ path: `/sdk/${method}`,
3926
+ method: "POST",
3927
+ data: normalizeSdkPayload(payload, method === "replyMessage")
3928
+ });
3929
+ return unwrapSdkData(response);
3930
+ }
3931
+
3932
+ // src/sdk/social/blacklist.ts
3933
+ function addUsersToBlacklist(userIDs) {
3934
+ return callSdkApi("addUsersToBlacklist", { userIDs });
3935
+ }
3936
+ function removeUsersFromBlacklist(userIDs) {
3937
+ return callSdkApi("removeUsersFromBlacklist", { userIDs });
3938
+ }
3939
+ function queryBlacklist(config) {
3940
+ return callSdkApi("queryBlacklist", { config });
3941
+ }
3942
+ function checkUserIsInBlacklist(userID) {
3943
+ return callSdkApi("checkUserIsInBlacklist", { userID });
3944
+ }
3945
+ var blacklist_default = {
3946
+ addUsersToBlacklist,
3947
+ removeUsersFromBlacklist,
3948
+ queryBlacklist,
3949
+ checkUserIsInBlacklist
3950
+ };
3951
+
3952
+ // src/sdk/social/friend.ts
3953
+ function sendFriendApplication(userID, config) {
3954
+ return callSdkApi("sendFriendApplication", { userID, config });
3955
+ }
3956
+ function deleteFriends(userIDs, config) {
3957
+ return callSdkApi("deleteFriends", { userIDs, config });
3958
+ }
3959
+ function updateFriendAlias(friendAlias, userID) {
3960
+ return callSdkApi("updateFriendAlias", { friendAlias, userID });
3961
+ }
3962
+ function queryFriendApplicationList(config) {
3963
+ return callSdkApi("queryFriendApplicationList", { config });
3964
+ }
3965
+ function acceptFriendApplication(userID, config) {
3966
+ return callSdkApi("acceptFriendApplication", { userID, config });
3967
+ }
3968
+ function rejectFriendApplication(userID, config) {
3969
+ return callSdkApi("rejectFriendApplication", { userID, config });
3970
+ }
3971
+ function queryFriendList(config) {
3972
+ return callSdkApi("queryFriendList", { config });
3973
+ }
3974
+ function queryFriendsInfo(userIDs) {
3975
+ return callSdkApi("queryFriendsInfo", { userIDs });
3976
+ }
3977
+ var friend_default = {
3978
+ sendFriendApplication,
3979
+ deleteFriends,
3980
+ updateFriendAlias,
3981
+ queryFriendApplicationList,
3982
+ acceptFriendApplication,
3983
+ rejectFriendApplication,
3984
+ queryFriendList,
3985
+ queryFriendsInfo
3986
+ };
3987
+
3988
+ // src/sdk/social/user.ts
3989
+ function querySelfUserInfo() {
3990
+ return callSdkApi("querySelfUserInfo");
3991
+ }
3992
+ async function renewToken(token) {
3993
+ const session = captureSession();
3994
+ const result = await callSdkApi("renewToken", {
3995
+ token
3996
+ });
3997
+ assertCurrentSession(session);
3998
+ setSdkSession(result?.token || token);
3999
+ return result ?? { token };
4000
+ }
4001
+ function queryUsersInfo(userIDs, config) {
4002
+ return callSdkApi("queryUsersInfo", { userIDs, config });
4003
+ }
4004
+ function queryUsersStatus(userIDs) {
4005
+ return callSdkApi("queryUsersStatus", { userIDs });
4006
+ }
4007
+ function subscribeUsersStatus(userIDs, config) {
4008
+ return callSdkApi("subscribeUsersStatus", { userIDs, config });
4009
+ }
4010
+ function unsubscribeUsersStatus(userIDs) {
4011
+ return callSdkApi("unsubscribeUsersStatus", { userIDs });
4012
+ }
4013
+ function updateUserName(userName) {
4014
+ return callSdkApi("updateUserName", { userName });
4015
+ }
4016
+ function updateUserAvatarUrl(userAvatarUrl) {
4017
+ return callSdkApi("updateUserAvatarUrl", { userAvatarUrl });
4018
+ }
4019
+ var user_default = {
4020
+ querySelfUserInfo,
4021
+ renewToken,
4022
+ queryUsersInfo,
4023
+ queryUsersStatus,
4024
+ subscribeUsersStatus,
4025
+ unsubscribeUsersStatus,
4026
+ updateUserName,
4027
+ updateUserAvatarUrl
4028
+ };
4029
+
4030
+ // src/sdk/social/index.ts
4031
+ var SocialApis = {
4032
+ ...friend_default,
4033
+ ...blacklist_default,
4034
+ ...user_default
4035
+ };
4036
+ var social_default = SocialApis;
4037
+
4038
+ // src/sdk/group/group.ts
4039
+ function createGroup(groupInfo, userIDs, config) {
4040
+ return callSdkApi("createGroup", { groupInfo, userIDs, config });
4041
+ }
4042
+ function joinGroup(groupID) {
4043
+ return callSdkApi("joinGroup", { groupID });
4044
+ }
4045
+ function leaveGroup(groupID) {
4046
+ return callSdkApi("leaveGroup", { groupID });
4047
+ }
4048
+ function dismissGroup(groupID) {
4049
+ return callSdkApi("dismissGroup", { groupID });
4050
+ }
4051
+ function kickGroupMembers(userIDs, groupID) {
4052
+ return callSdkApi("kickGroupMembers", { userIDs, groupID });
4053
+ }
4054
+ function inviteUsersIntoGroup(userIDs, groupID) {
4055
+ return callSdkApi("inviteUsersIntoGroup", { userIDs, groupID });
4056
+ }
4057
+ function transferGroupOwner(toUserID, groupID) {
4058
+ return callSdkApi("transferGroupOwner", { toUserID, groupID });
4059
+ }
4060
+ function updateGroupAlias(groupAlias, groupID) {
4061
+ return callSdkApi("updateGroupAlias", { groupAlias, groupID });
4062
+ }
4063
+ function updateGroupName(groupName, groupID) {
4064
+ return callSdkApi("updateGroupName", { groupName, groupID });
4065
+ }
4066
+ function muteGroup(isMute, groupID, config) {
4067
+ return callSdkApi("muteGroup", { isMute, groupID, config });
4068
+ }
4069
+ function muteGroupMembers(isMute, userIDs, groupID, config) {
4070
+ return callSdkApi("muteGroupMembers", { isMute, userIDs, groupID, config });
4071
+ }
4072
+ function updateGroupAvatarUrl(groupAvatarUrl, groupID) {
4073
+ return callSdkApi("updateGroupAvatarUrl", { groupAvatarUrl, groupID });
4074
+ }
4075
+ function updateGroupNotice(groupNotice, groupID) {
4076
+ return callSdkApi("updateGroupNotice", { groupNotice, groupID });
4077
+ }
4078
+ function updateGroupJoinMode(mode, groupID) {
4079
+ return callSdkApi("updateGroupJoinMode", { mode, groupID });
4080
+ }
4081
+ function updateGroupInviteMode(mode, groupID) {
4082
+ return callSdkApi("updateGroupInviteMode", { mode, groupID });
4083
+ }
4084
+ function acceptGroupInviteApplication(inviterUserID, groupID, config) {
4085
+ return callSdkApi("acceptGroupInviteApplication", {
4086
+ inviterUserID,
4087
+ groupID,
4088
+ config
4089
+ });
4090
+ }
4091
+ function acceptGroupJoinApplication(userID, groupID, config) {
4092
+ return callSdkApi("acceptGroupJoinApplication", { userID, groupID, config });
4093
+ }
4094
+ function queryGroupApplicationList(config) {
4095
+ return callSdkApi("queryGroupApplicationList", { config });
4096
+ }
4097
+ function rejectGroupInviteApplication(inviterUserID, groupID, config) {
4098
+ return callSdkApi("rejectGroupInviteApplication", {
4099
+ inviterUserID,
4100
+ groupID,
4101
+ config
4102
+ });
4103
+ }
4104
+ function rejectGroupJoinApplication(userID, groupID, config) {
4105
+ return callSdkApi("rejectGroupJoinApplication", { userID, groupID, config });
4106
+ }
4107
+ function sendGroupInviteApplications(userIDs, groupID, config) {
4108
+ return callSdkApi("sendGroupInviteApplications", {
4109
+ userIDs,
4110
+ groupID,
4111
+ config
4112
+ });
4113
+ }
4114
+ function sendGroupJoinApplication(groupID, config) {
4115
+ return callSdkApi("sendGroupJoinApplication", { groupID, config });
4116
+ }
4117
+ function updateGroupBeInviteMode(mode, groupID) {
4118
+ return callSdkApi("updateGroupBeInviteMode", { mode, groupID });
4119
+ }
4120
+ function queryGroupInfo(groupID) {
4121
+ return callSdkApi("queryGroupInfo", { groupID });
4122
+ }
4123
+ function queryGroupList(count, config) {
4124
+ return callSdkApi("queryGroupList", { count, config });
4125
+ }
4126
+ function setGroupMemberRole(role, forUserID, groupID) {
4127
+ return callSdkApi("setGroupMemberRole", { role, forUserID, groupID });
4128
+ }
4129
+ function setGroupMemberNickname(nickname, forUserID, groupID) {
4130
+ return callSdkApi("setGroupMemberNickname", {
4131
+ nickname,
4132
+ forUserID,
4133
+ groupID
4134
+ });
4135
+ }
4136
+ function queryGroupMemberInfo(userID, groupID) {
4137
+ return callSdkApi("queryGroupMemberInfo", { userID, groupID });
4138
+ }
4139
+ function queryGroupMemberList(groupID, config) {
4140
+ return callSdkApi("queryGroupMemberList", { groupID, config });
4141
+ }
4142
+ function queryGroupMemberMutedList(groupID, config) {
4143
+ return callSdkApi("queryGroupMemberMutedList", { groupID, config });
4144
+ }
4145
+ function queryGroupMemberCount(groupID) {
4146
+ return callSdkApi("queryGroupMemberCount", { groupID });
4147
+ }
4148
+ var group_default = {
4149
+ createGroup,
4150
+ joinGroup,
4151
+ leaveGroup,
4152
+ dismissGroup,
4153
+ kickGroupMembers,
4154
+ inviteUsersIntoGroup,
4155
+ transferGroupOwner,
4156
+ updateGroupAlias,
4157
+ updateGroupName,
4158
+ muteGroup,
4159
+ muteGroupMembers,
4160
+ updateGroupAvatarUrl,
4161
+ updateGroupNotice,
4162
+ updateGroupJoinMode,
4163
+ updateGroupInviteMode,
4164
+ acceptGroupInviteApplication,
4165
+ acceptGroupJoinApplication,
4166
+ queryGroupApplicationList,
4167
+ rejectGroupInviteApplication,
4168
+ rejectGroupJoinApplication,
4169
+ sendGroupInviteApplications,
4170
+ sendGroupJoinApplication,
4171
+ updateGroupBeInviteMode,
4172
+ queryGroupInfo,
4173
+ queryGroupList,
4174
+ setGroupMemberRole,
4175
+ setGroupMemberNickname,
4176
+ queryGroupMemberInfo,
4177
+ queryGroupMemberList,
4178
+ queryGroupMemberMutedList,
4179
+ queryGroupMemberCount
4180
+ };
4181
+
4182
+ // src/sdk/conversation/conversationExt.ts
4183
+ function queryConversationPinnedList(config) {
4184
+ return callSdkApi("queryConversationPinnedList", { config });
4185
+ }
4186
+ function queryConversationTotalUnreadMessageCount(config) {
4187
+ return callSdkApi("queryConversationTotalUnreadMessageCount", { config });
4188
+ }
4189
+ function updateConversationPinnedState(isPinned, conversationID, conversationType) {
4190
+ return callSdkApi("updateConversationPinnedState", {
4191
+ isPinned,
4192
+ conversationID,
4193
+ conversationType
4194
+ });
4195
+ }
4196
+ function setConversationNotificationStatus(status, conversationID, conversationType) {
4197
+ return callSdkApi("setConversationNotificationStatus", {
4198
+ status,
4199
+ conversationID,
4200
+ conversationType
4201
+ });
4202
+ }
4203
+ var conversationExt_default = {
4204
+ queryConversationPinnedList,
4205
+ queryConversationTotalUnreadMessageCount,
4206
+ updateConversationPinnedState,
4207
+ setConversationNotificationStatus
4208
+ };
4209
+
4210
+ // src/sdk/message/messageExt.ts
4211
+ function notifyMessageAttached(notification, message) {
4212
+ try {
4213
+ notification?.onMessageAttached?.(message);
4214
+ } catch {
4215
+ }
4216
+ }
4217
+ function editMessage(message, config, notification) {
4218
+ notifyMessageAttached(notification, message);
4219
+ return callSdkApi("editMessage", { message, config });
4220
+ }
4221
+ async function replyMessage(message, toOriginalMessage, config, notification) {
4222
+ const session = captureSession();
4223
+ if (isAuthenticating())
4224
+ await waitForAuthentication(session);
4225
+ const storage = captureStorage();
4226
+ const preparedReply = prepareReplyMessage(message, toOriginalMessage);
4227
+ await persistOutgoing(storage, {
4228
+ toOriginalMessage,
4229
+ message: preparedReply
4230
+ });
4231
+ if (storage && !isCurrentStorage(storage))
4232
+ throw new Error("Local storage account changed before sending reply");
4233
+ notifyMessageAttached(notification, { ...preparedReply });
4234
+ assertCurrentSession(session);
4235
+ const result = await callSdkApi("replyMessage", { message: preparedReply, toOriginalMessage, config });
4236
+ if (result?.message && typeof result.message === "object") {
4237
+ return { ...result, message: { ...result.message, clientBizId: preparedReply.clientBizId } };
4238
+ }
4239
+ return result && typeof result === "object" ? { ...result, clientBizId: preparedReply.clientBizId } : result;
4240
+ }
4241
+ function prepareReplyMessage(message, original) {
4242
+ const candidate = { ...message };
4243
+ delete candidate.messageID;
4244
+ delete candidate.localMessageID;
4245
+ delete candidate.localID;
4246
+ delete candidate.orderKey;
4247
+ delete candidate.messageSeq;
4248
+ delete candidate.conversationSeq;
4249
+ delete candidate.id;
4250
+ delete candidate.c2cSeq;
4251
+ delete candidate.clientSid;
4252
+ delete candidate.errorCode;
4253
+ delete candidate.errorMessage;
4254
+ if (candidate.clientBizId && candidate.clientBizId === original.clientBizId)
4255
+ delete candidate.clientBizId;
4256
+ const prepared = withClientBizId(candidate);
4257
+ return {
4258
+ ...prepared,
4259
+ localMessageID: prepared.clientBizId,
4260
+ conversationID: original.conversationID ?? candidate.conversationID,
4261
+ conversationType: original.conversationType ?? candidate.conversationType,
4262
+ timestamp: Date.now(),
4263
+ direction: 0,
4264
+ sentStatus: 0,
4265
+ sendState: 0,
4266
+ // Build the quote before persistence/notification, independently of the ACK.
4267
+ // Copy display fields only so editing the draft never changes the original.
4268
+ repliedInfo: {
4269
+ state: original.deleted ? 1 : 0,
4270
+ messageID: original.messageID,
4271
+ messageSeq: original.messageSeq ?? original.id ?? original.c2cSeq,
4272
+ senderUserID: original.senderUserID,
4273
+ sentTime: original.timestamp,
4274
+ messageInfo: {
4275
+ type: original.type,
4276
+ message: original.message instanceof Uint8Array ? original.message.slice() : original.message ?? original.massage ?? "",
4277
+ extendedData: original.extendedData,
4278
+ subType: original.subType,
4279
+ fileName: original.fileName
4280
+ }
4281
+ }
4282
+ };
4283
+ }
4284
+ function queryMessageRepliedList(message, config) {
4285
+ return callSdkApi("queryMessageRepliedList", { message, config });
4286
+ }
4287
+ function queryCombineMessageDetail(config) {
4288
+ return callSdkApi("queryCombineMessageDetail", {
4289
+ combineID: config.combineID
4290
+ });
4291
+ }
4292
+ function searchLocalMessages(conversationID, conversationType, config) {
4293
+ if (captureStorage()) {
4294
+ const read = async () => {
4295
+ const result = await queryLocalMessages({
4296
+ conversationID,
4297
+ conversationType,
4298
+ count: config.count,
4299
+ order: config.order === 1 ? "asc" : "desc",
4300
+ keywords: config.keywords,
4301
+ messageTypes: config.messageTypes,
4302
+ senderUserIDs: config.senderUserIDs,
4303
+ startTime: config.startTime,
4304
+ endTime: config.endTime,
4305
+ before: config.nextMessage ? { timestamp: Number(config.nextMessage.timestamp), localID: String(config.nextMessage.localID || config.nextMessage.localMessageID || "") } : void 0
4306
+ });
4307
+ return {
4308
+ conversationID,
4309
+ conversationType,
4310
+ messageList: result.messageList,
4311
+ nextMessage: result.hasMore ? result.messageList[result.messageList.length - 1] : void 0
4312
+ };
4313
+ };
4314
+ return read();
4315
+ }
4316
+ return callSdkApi("searchLocalMessages", {
4317
+ conversationID,
4318
+ conversationType,
4319
+ config
4320
+ });
4321
+ }
4322
+ function queryMessages(messageIds, conversationID, conversationType) {
4323
+ return callSdkApi("queryMessages", {
4324
+ messageIds,
4325
+ conversationID,
4326
+ conversationType
4327
+ });
4328
+ }
4329
+ var messageExt_default = {
4330
+ editMessage,
4331
+ replyMessage,
4332
+ queryMessageRepliedList,
4333
+ queryCombineMessageDetail,
4334
+ searchLocalMessages,
4335
+ queryMessages
4336
+ };
4337
+
4338
+ // src/sdk/zimApis.ts
4339
+ var ZimApis = {
4340
+ ...social_default,
4341
+ ...group_default,
4342
+ ...conversationExt_default,
4343
+ ...messageExt_default
4344
+ };
4345
+ var zimApis_default = ZimApis;
4346
+
4347
+ // src/constants/social/ZIMUserExtension.ts
4348
+ var ZIMUserRelationType = /* @__PURE__ */ ((ZIMUserRelationType2) => {
4349
+ ZIMUserRelationType2[ZIMUserRelationType2["Unknown"] = -1] = "Unknown";
4350
+ ZIMUserRelationType2[ZIMUserRelationType2["None"] = 0] = "None";
4351
+ ZIMUserRelationType2[ZIMUserRelationType2["Friend"] = 1] = "Friend";
4352
+ return ZIMUserRelationType2;
4353
+ })(ZIMUserRelationType || {});
4354
+ var ZIMUserOnlineStatus = /* @__PURE__ */ ((ZIMUserOnlineStatus2) => {
4355
+ ZIMUserOnlineStatus2[ZIMUserOnlineStatus2["Online"] = 0] = "Online";
4356
+ ZIMUserOnlineStatus2[ZIMUserOnlineStatus2["Offline"] = 1] = "Offline";
4357
+ ZIMUserOnlineStatus2[ZIMUserOnlineStatus2["Logout"] = 2] = "Logout";
4358
+ ZIMUserOnlineStatus2[ZIMUserOnlineStatus2["Unknown"] = 99] = "Unknown";
4359
+ return ZIMUserOnlineStatus2;
4360
+ })(ZIMUserOnlineStatus || {});
4361
+
4362
+ // src/constants/social/ZIMFriendExtension.ts
4363
+ var ZIMFriendApplicationType = /* @__PURE__ */ ((ZIMFriendApplicationType2) => {
4364
+ ZIMFriendApplicationType2[ZIMFriendApplicationType2["Unknown"] = -1] = "Unknown";
4365
+ ZIMFriendApplicationType2[ZIMFriendApplicationType2["None"] = 0] = "None";
4366
+ ZIMFriendApplicationType2[ZIMFriendApplicationType2["Received"] = 1] = "Received";
4367
+ ZIMFriendApplicationType2[ZIMFriendApplicationType2["Sent"] = 2] = "Sent";
4368
+ ZIMFriendApplicationType2[ZIMFriendApplicationType2["Both"] = 3] = "Both";
4369
+ return ZIMFriendApplicationType2;
4370
+ })(ZIMFriendApplicationType || {});
4371
+ var ZIMFriendApplicationState = /* @__PURE__ */ ((ZIMFriendApplicationState2) => {
4372
+ ZIMFriendApplicationState2[ZIMFriendApplicationState2["Unknown"] = -1] = "Unknown";
4373
+ ZIMFriendApplicationState2[ZIMFriendApplicationState2["Waiting"] = 1] = "Waiting";
4374
+ ZIMFriendApplicationState2[ZIMFriendApplicationState2["Accepted"] = 2] = "Accepted";
4375
+ ZIMFriendApplicationState2[ZIMFriendApplicationState2["Rejected"] = 3] = "Rejected";
4376
+ ZIMFriendApplicationState2[ZIMFriendApplicationState2["Expired"] = 4] = "Expired";
4377
+ ZIMFriendApplicationState2[ZIMFriendApplicationState2["Disabled"] = 5] = "Disabled";
4378
+ return ZIMFriendApplicationState2;
4379
+ })(ZIMFriendApplicationState || {});
4380
+ var ZIMFriendDeleteType = /* @__PURE__ */ ((ZIMFriendDeleteType2) => {
4381
+ ZIMFriendDeleteType2[ZIMFriendDeleteType2["Both"] = 0] = "Both";
4382
+ ZIMFriendDeleteType2[ZIMFriendDeleteType2["Single"] = 1] = "Single";
4383
+ return ZIMFriendDeleteType2;
4384
+ })(ZIMFriendDeleteType || {});
4385
+
4386
+ // src/constants/group/ZIMGroupExtension.ts
4387
+ var ZIMGroupMessageNotificationStatus = /* @__PURE__ */ ((ZIMGroupMessageNotificationStatus2) => {
4388
+ ZIMGroupMessageNotificationStatus2[ZIMGroupMessageNotificationStatus2["Notify"] = 1] = "Notify";
4389
+ ZIMGroupMessageNotificationStatus2[ZIMGroupMessageNotificationStatus2["DoNotDisturb"] = 2] = "DoNotDisturb";
4390
+ return ZIMGroupMessageNotificationStatus2;
4391
+ })(ZIMGroupMessageNotificationStatus || {});
4392
+ var ZIMGroupMuteMode = /* @__PURE__ */ ((ZIMGroupMuteMode2) => {
4393
+ ZIMGroupMuteMode2[ZIMGroupMuteMode2["None"] = 0] = "None";
4394
+ ZIMGroupMuteMode2[ZIMGroupMuteMode2["Normal"] = 1] = "Normal";
4395
+ ZIMGroupMuteMode2[ZIMGroupMuteMode2["All"] = 2] = "All";
4396
+ ZIMGroupMuteMode2[ZIMGroupMuteMode2["Custom"] = 3] = "Custom";
4397
+ return ZIMGroupMuteMode2;
4398
+ })(ZIMGroupMuteMode || {});
4399
+ var ZIMGroupJoinMode = /* @__PURE__ */ ((ZIMGroupJoinMode2) => {
4400
+ ZIMGroupJoinMode2[ZIMGroupJoinMode2["Any"] = 0] = "Any";
4401
+ ZIMGroupJoinMode2[ZIMGroupJoinMode2["Auth"] = 1] = "Auth";
4402
+ ZIMGroupJoinMode2[ZIMGroupJoinMode2["Forbid"] = 2] = "Forbid";
4403
+ return ZIMGroupJoinMode2;
4404
+ })(ZIMGroupJoinMode || {});
4405
+ var ZIMGroupInviteMode = /* @__PURE__ */ ((ZIMGroupInviteMode2) => {
4406
+ ZIMGroupInviteMode2[ZIMGroupInviteMode2["Any"] = 0] = "Any";
4407
+ ZIMGroupInviteMode2[ZIMGroupInviteMode2["Admin"] = 1] = "Admin";
4408
+ return ZIMGroupInviteMode2;
4409
+ })(ZIMGroupInviteMode || {});
4410
+ var ZIMGroupBeInviteMode = /* @__PURE__ */ ((ZIMGroupBeInviteMode2) => {
4411
+ ZIMGroupBeInviteMode2[ZIMGroupBeInviteMode2["None"] = 0] = "None";
4412
+ ZIMGroupBeInviteMode2[ZIMGroupBeInviteMode2["Auth"] = 1] = "Auth";
4413
+ return ZIMGroupBeInviteMode2;
4414
+ })(ZIMGroupBeInviteMode || {});
4415
+ var ZIMGroupEnterType = /* @__PURE__ */ ((ZIMGroupEnterType2) => {
4416
+ ZIMGroupEnterType2[ZIMGroupEnterType2["Unknown"] = 0] = "Unknown";
4417
+ ZIMGroupEnterType2[ZIMGroupEnterType2["Created"] = 1] = "Created";
4418
+ ZIMGroupEnterType2[ZIMGroupEnterType2["JoinApply"] = 2] = "JoinApply";
4419
+ ZIMGroupEnterType2[ZIMGroupEnterType2["Joined"] = 3] = "Joined";
4420
+ ZIMGroupEnterType2[ZIMGroupEnterType2["Invited"] = 4] = "Invited";
4421
+ ZIMGroupEnterType2[ZIMGroupEnterType2["InviteApply"] = 5] = "InviteApply";
4422
+ return ZIMGroupEnterType2;
4423
+ })(ZIMGroupEnterType || {});
4424
+ var ZIMGroupApplicationType = /* @__PURE__ */ ((ZIMGroupApplicationType2) => {
4425
+ ZIMGroupApplicationType2[ZIMGroupApplicationType2["Unknown"] = -1] = "Unknown";
4426
+ ZIMGroupApplicationType2[ZIMGroupApplicationType2["None"] = 0] = "None";
4427
+ ZIMGroupApplicationType2[ZIMGroupApplicationType2["Join"] = 1] = "Join";
4428
+ ZIMGroupApplicationType2[ZIMGroupApplicationType2["Invite"] = 2] = "Invite";
4429
+ ZIMGroupApplicationType2[ZIMGroupApplicationType2["BeInvite"] = 3] = "BeInvite";
4430
+ return ZIMGroupApplicationType2;
4431
+ })(ZIMGroupApplicationType || {});
4432
+ var ZIMGroupApplicationState = /* @__PURE__ */ ((ZIMGroupApplicationState2) => {
4433
+ ZIMGroupApplicationState2[ZIMGroupApplicationState2["Unknown"] = -1] = "Unknown";
4434
+ ZIMGroupApplicationState2[ZIMGroupApplicationState2["Waiting"] = 1] = "Waiting";
4435
+ ZIMGroupApplicationState2[ZIMGroupApplicationState2["Accepted"] = 2] = "Accepted";
4436
+ ZIMGroupApplicationState2[ZIMGroupApplicationState2["Rejected"] = 3] = "Rejected";
4437
+ ZIMGroupApplicationState2[ZIMGroupApplicationState2["Expired"] = 4] = "Expired";
4438
+ ZIMGroupApplicationState2[ZIMGroupApplicationState2["Disabled"] = 5] = "Disabled";
4439
+ return ZIMGroupApplicationState2;
4440
+ })(ZIMGroupApplicationState || {});
4441
+
4442
+ // src/constants/conversations/ZIMConversationExtension.ts
4443
+ var ZIMConversationNotificationStatus = /* @__PURE__ */ ((ZIMConversationNotificationStatus2) => {
4444
+ ZIMConversationNotificationStatus2[ZIMConversationNotificationStatus2["Notify"] = 1] = "Notify";
4445
+ ZIMConversationNotificationStatus2[ZIMConversationNotificationStatus2["DoNotDisturb"] = 2] = "DoNotDisturb";
4446
+ return ZIMConversationNotificationStatus2;
4447
+ })(ZIMConversationNotificationStatus || {});
4448
+ var ZIMConversationPinnedFilter = /* @__PURE__ */ ((ZIMConversationPinnedFilter2) => {
4449
+ ZIMConversationPinnedFilter2[ZIMConversationPinnedFilter2["All"] = 0] = "All";
4450
+ ZIMConversationPinnedFilter2[ZIMConversationPinnedFilter2["OnlyPinned"] = 1] = "OnlyPinned";
4451
+ ZIMConversationPinnedFilter2[ZIMConversationPinnedFilter2["OnlyUnpinned"] = 2] = "OnlyUnpinned";
4452
+ return ZIMConversationPinnedFilter2;
4453
+ })(ZIMConversationPinnedFilter || {});
4454
+
4455
+ // src/constants/conversations/ZIMConversationDefines.ts
4456
+ var ZIMConversationEvent = /* @__PURE__ */ ((ZIMConversationEvent2) => {
4457
+ ZIMConversationEvent2[ZIMConversationEvent2["Added"] = 0] = "Added";
4458
+ ZIMConversationEvent2[ZIMConversationEvent2["Updated"] = 1] = "Updated";
4459
+ ZIMConversationEvent2[ZIMConversationEvent2["Disabled"] = 2] = "Disabled";
4460
+ ZIMConversationEvent2[ZIMConversationEvent2["Deleted"] = 3] = "Deleted";
4461
+ return ZIMConversationEvent2;
4462
+ })(ZIMConversationEvent || {});
4463
+
4464
+ // src/constants/messages/ZIMMessageExtension.ts
4465
+ var ZIMMessageOrder = /* @__PURE__ */ ((ZIMMessageOrder2) => {
4466
+ ZIMMessageOrder2[ZIMMessageOrder2["Descending"] = 0] = "Descending";
4467
+ ZIMMessageOrder2[ZIMMessageOrder2["Ascending"] = 1] = "Ascending";
4468
+ return ZIMMessageOrder2;
4469
+ })(ZIMMessageOrder || {});
990
4470
 
991
4471
  // src/sdk/index.ts
992
4472
  var VanshiSdk = {
4473
+ ...storage_default,
993
4474
  // 创建 ws 实例
994
4475
  create: initSocket,
995
- // 退出 SDK 登录并断开 ws 连接
4476
+ // 退出 SDK 登录,默认不断开 ws 连接
996
4477
  logout,
997
4478
  // 判断当前 ws 是否连接
998
4479
  isConnected: isSocketConnected,
@@ -1002,8 +4483,12 @@ var VanshiSdk = {
1002
4483
  off,
1003
4484
  // 登录 SDK
1004
4485
  login,
1005
- // 查询会话列表
4486
+ // 音视频通话
4487
+ ...call_default,
4488
+ // 查询单个会话
1006
4489
  queryConversation,
4490
+ // 查询会话列表
4491
+ queryConversationList,
1007
4492
  // 删除全部会话列表
1008
4493
  deleteAllConversations,
1009
4494
  // 删除单个会话
@@ -1033,31 +4518,148 @@ var VanshiSdk = {
1033
4518
  // 查询群消息未读成员列表。
1034
4519
  queryGroupMessageReceiptUnreadMemberList,
1035
4520
  // 查询群消息已读成员列表。
1036
- queryGroupMessageReceiptReadMemberList
4521
+ queryGroupMessageReceiptReadMemberList,
4522
+ // 好友、黑名单、用户、群组、会话与消息扩展 API
4523
+ ...zimApis_default
1037
4524
  };
1038
4525
  var sdk_default = VanshiSdk;
1039
4526
  export {
4527
+ LocalSyncError,
4528
+ SEDMessageType,
4529
+ SdkRuntimeError,
4530
+ ZIMCallAction,
4531
+ ZIMCallConversationType,
4532
+ ZIMCallEventEnum,
4533
+ ZIMCallMode,
4534
+ ZIMCallType,
4535
+ ZIMConnectionEvent,
4536
+ ZIMConnectionState,
4537
+ ZIMConversationEvent,
4538
+ ZIMConversationNotificationStatus,
4539
+ ZIMConversationPinnedFilter,
4540
+ ZIMEventEnum,
4541
+ ZIMFriendApplicationState,
4542
+ ZIMFriendApplicationType,
4543
+ ZIMFriendDeleteType,
4544
+ ZIMGroupApplicationState,
4545
+ ZIMGroupApplicationType,
4546
+ ZIMGroupBeInviteMode,
4547
+ ZIMGroupEnterType,
4548
+ ZIMGroupInviteMode,
4549
+ ZIMGroupJoinMode,
4550
+ ZIMGroupMessageNotificationStatus,
4551
+ ZIMGroupMuteMode,
4552
+ ZIMMessageOrder,
4553
+ ZIMUserOnlineStatus,
4554
+ ZIMUserRelationType,
4555
+ ZIM_LOCAL_CALL_EVENTS,
4556
+ acceptFriendApplication,
4557
+ acceptGroupInviteApplication,
4558
+ acceptGroupJoinApplication,
4559
+ addUsersToBlacklist,
4560
+ callAccept,
4561
+ callCancel,
4562
+ callGetLiveKitToken,
4563
+ callGetMediaState,
4564
+ callGetRoom,
4565
+ callHangup,
4566
+ callHasOngoingCall,
4567
+ callInvite,
4568
+ callInviteMembers,
4569
+ callJoinMedia,
4570
+ callLeaveMedia,
4571
+ callQueryMembers,
4572
+ callReject,
4573
+ callSetCameraEnabled,
4574
+ callSetMicrophoneEnabled,
4575
+ callStartScreenShare,
4576
+ callStopScreenShare,
4577
+ checkUserIsInBlacklist,
1040
4578
  clearConversationTotalUnreadMessageCount,
1041
4579
  clearConversationUnreadMessageCount,
4580
+ clearLocalMessages,
1042
4581
  initSocket as create,
4582
+ createGroup,
1043
4583
  sdk_default as default,
1044
4584
  deleteAllConversationMessages,
1045
4585
  deleteAllConversations,
1046
4586
  deleteAllMessage,
1047
4587
  deleteConversation,
4588
+ deleteFriends,
1048
4589
  deleteMessages,
4590
+ dismissGroup,
4591
+ editMessage,
4592
+ getLocalStorageStatus,
4593
+ inviteUsersIntoGroup,
1049
4594
  isSocketConnected as isConnected,
4595
+ isSdkRuntimeErrorObject,
4596
+ joinGroup,
4597
+ kickGroupMembers,
4598
+ leaveGroup,
1050
4599
  login,
1051
4600
  logout,
4601
+ muteGroup,
4602
+ muteGroupMembers,
1052
4603
  off,
1053
4604
  on,
4605
+ queryBlacklist,
4606
+ queryCombineMessageDetail,
1054
4607
  queryConversation,
4608
+ queryConversationList,
4609
+ queryConversationPinnedList,
4610
+ queryConversationTotalUnreadMessageCount,
4611
+ queryFailedMessages,
4612
+ queryFriendApplicationList,
4613
+ queryFriendList,
4614
+ queryFriendsInfo,
4615
+ queryGroupApplicationList,
4616
+ queryGroupInfo,
4617
+ queryGroupList,
4618
+ queryGroupMemberCount,
4619
+ queryGroupMemberInfo,
4620
+ queryGroupMemberList,
4621
+ queryGroupMemberMutedList,
1055
4622
  queryGroupMessageReceiptReadMemberList,
1056
4623
  queryGroupMessageReceiptUnreadMemberList,
1057
4624
  queryHistoryMessage,
4625
+ queryLocalMessages,
1058
4626
  queryMessageReceiptsInfo,
4627
+ queryMessageRepliedList,
4628
+ queryMessages,
4629
+ querySelfUserInfo,
4630
+ queryUsersInfo,
4631
+ queryUsersStatus,
4632
+ rejectFriendApplication,
4633
+ rejectGroupInviteApplication,
4634
+ rejectGroupJoinApplication,
4635
+ removeUsersFromBlacklist,
4636
+ renewToken,
4637
+ replyMessage,
1059
4638
  revokeMessage,
4639
+ searchLocalMessages,
4640
+ sendFriendApplication,
4641
+ sendGroupInviteApplications,
4642
+ sendGroupJoinApplication,
1060
4643
  sendMediaMessage,
1061
4644
  sendMessage,
1062
- sendMessageReceiptsRead
4645
+ sendMessageReceiptsRead,
4646
+ setConversationNotificationStatus,
4647
+ setGroupMemberNickname,
4648
+ setGroupMemberRole,
4649
+ subscribeUsersStatus,
4650
+ syncConversationMessages,
4651
+ syncLocalMessages,
4652
+ transferGroupOwner,
4653
+ unsubscribeUsersStatus,
4654
+ updateConversationPinnedState,
4655
+ updateFriendAlias,
4656
+ updateGroupAlias,
4657
+ updateGroupAvatarUrl,
4658
+ updateGroupBeInviteMode,
4659
+ updateGroupInviteMode,
4660
+ updateGroupJoinMode,
4661
+ updateGroupName,
4662
+ updateGroupNotice,
4663
+ updateUserAvatarUrl,
4664
+ updateUserName
1063
4665
  };