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