mdm-im-v3 2.1.0 → 2.1.2

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.
Files changed (25) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/{FilePreview-dpnc9m3Z.js → FilePreview-Cz0KMA_m.js} +2 -2
  3. package/dist/core.es.js +2 -2
  4. package/dist/{index-CsaYx_7b.js → index-C8koozDu.js} +2 -2
  5. package/dist/{index-CNxeqkio.js → index-GiaKeCU7.js} +47 -47
  6. package/dist/{index-D5S79LNp.js → index-P9_HILzo.js} +599 -319
  7. package/dist/index.es.js +1 -1
  8. package/dist/style/index.css +1 -1
  9. package/dist-h5/{ChatHistoryH5-BhrGF5WR.js → ChatHistoryH5-CKDqKPl-.js} +2 -2
  10. package/dist-h5/{ChatPageH5-B3azJu7O.js → ChatPageH5-FXZxtcQr.js} +3 -3
  11. package/dist-h5/{ChatSettingsH5-CFr9Bn69.js → ChatSettingsH5-PackrlPt.js} +1 -1
  12. package/dist-h5/{CreateGroupH5-a16jkObH.js → CreateGroupH5-CO_ZH36_.js} +2 -2
  13. package/dist-h5/{GroupAnnouncementH5-B-Qfzoy2.js → GroupAnnouncementH5-yUDR5kYW.js} +1 -1
  14. package/dist-h5/{GroupInfoDialogH5-BHLKs62Z.js → GroupInfoDialogH5-BRk-_mEn.js} +1 -1
  15. package/dist-h5/{GroupMembersH5-D_f1RFjO.js → GroupMembersH5-BVHUF2mG.js} +1 -1
  16. package/dist-h5/{GroupNoticeH5-46ZjBe92.js → GroupNoticeH5-KA-L2h9r.js} +1 -1
  17. package/dist-h5/{MemberDetailH5-B7nlr8ZT.js → MemberDetailH5-BfAE0nx-.js} +2 -2
  18. package/dist-h5/{SearchPageH5-hYxNItvz.js → SearchPageH5-B6B_U4hW.js} +2 -2
  19. package/dist-h5/{SelectMembersH5-DQIfX_w4.js → SelectMembersH5-CrPoBnYp.js} +2 -2
  20. package/dist-h5/{imChat-Bfl2JS8Q.js → imChat-pGlpr8Vj.js} +40 -7
  21. package/dist-h5/index.es.js +2 -2
  22. package/dist-h5/{indexH5-DAnvVyZ1.js → indexH5-CCvF1MV7.js} +413 -128
  23. package/dist-h5/{indexH5-CUQe-Ize.js → indexH5-sKvS7t3k.js} +7 -3
  24. package/package.json +72 -71
  25. package/dist-h5/request-CK8gkCRf.js +0 -44
@@ -7,6 +7,74 @@ import { showToast } from "vant";
7
7
  import { defineStore } from "pinia";
8
8
  import axios from "axios";
9
9
  import { ElMessage } from "element-plus";
10
+ function normalizeIMIdentity(config = {}) {
11
+ const version = Number(config.imIdentityVersion || 1);
12
+ const tenantID = String(config.tenantID || "");
13
+ const userID = version === 2 ? String(config.imUserID || "") : String(config.userID || "");
14
+ if (![1, 2].includes(version)) throw new Error("不支持的 IM 身份版本");
15
+ if (version === 2 && (!tenantID || !/^mdm2u_[a-f0-9]{48}$/.test(userID))) {
16
+ throw new Error("租户 IM 身份信息不完整");
17
+ }
18
+ return { imIdentityVersion: version, tenantID, userID, imUserID: version === 2 ? userID : "" };
19
+ }
20
+ function imSessionScope(config = {}) {
21
+ if (!config.userID && !config.imUserID) return "";
22
+ const identity = normalizeIMIdentity(config);
23
+ return JSON.stringify([
24
+ config.apiUrl || "",
25
+ config.wsUrl || "",
26
+ identity.tenantID,
27
+ identity.imIdentityVersion,
28
+ identity.userID,
29
+ config.platformID ?? 5
30
+ ]);
31
+ }
32
+ function imCacheNamespace(config = {}) {
33
+ if (!config.tenantID || !config.userID && !config.imUserID) return "";
34
+ return `scope2:${imSessionScope(config)}`;
35
+ }
36
+ function nextIMSession(current, input = {}) {
37
+ const merged = { ...current, ...input };
38
+ if (Number(merged.imIdentityVersion) === 2 && String(current.tenantID || "") !== String(merged.tenantID || "") && !Object.hasOwn(input, "imUserID")) {
39
+ throw new Error("切换租户必须提供目标 IM 身份");
40
+ }
41
+ const next = { ...merged, ...normalizeIMIdentity(merged) };
42
+ if (imSessionScope(current) !== imSessionScope(next)) {
43
+ for (const key of ["imToken", "satoken", "chatToken", "nickname", "faceURL", "remoteConnectUrl", "remoteConnectToken", "businessUserID", "loginCode"]) {
44
+ next[key] = Object.hasOwn(input, key) ? input[key] : "";
45
+ }
46
+ next.loginStatus = 1;
47
+ next.groupStatus = 0;
48
+ next.currentChatId = "";
49
+ next.unReadCount = 0;
50
+ next.isViewingChat = false;
51
+ }
52
+ return next;
53
+ }
54
+ function parseIMBusinessData(value) {
55
+ try {
56
+ const result = typeof value === "string" ? JSON.parse(value) : value;
57
+ return result && typeof result === "object" && !Array.isArray(result) ? result : null;
58
+ } catch {
59
+ return null;
60
+ }
61
+ }
62
+ function shouldHandleLoginNotice(notice, session) {
63
+ if (Number(session.imIdentityVersion) === 2) return false;
64
+ if (!notice || notice.event !== "LOGIN_NOTICE") return false;
65
+ if (notice.tenantID != null && String(notice.tenantID) !== String(session.tenantID)) return false;
66
+ return Boolean(notice.sendUserID && notice.imToken && session.imToken && notice.sendUserID === session.userID && notice.imToken !== session.imToken);
67
+ }
68
+ function assertIMTarget(value, kind = "user") {
69
+ const pattern = kind === "group" ? /^mdm2g_[a-f0-9]{48}$/ : /^mdm2u_[a-f0-9]{48}$/;
70
+ if (!pattern.test(String(value || ""))) throw new Error(kind === "group" ? "请先通过服务端预留租户群组身份" : "消息目标缺少租户 IM 身份");
71
+ }
72
+ function shouldHandleRemoteNotice(notice, session) {
73
+ if (!notice) return false;
74
+ if (Number(session.imIdentityVersion) !== 2) return notice.sendUserID !== session.userID;
75
+ const own = session.imUserID || session.userID;
76
+ return Boolean(own && String(notice.tenantID || "") === String(session.tenantID || "") && /^mdm2u_[a-f0-9]{48}$/.test(notice.sendIMUserID || "") && notice.recvIMUserID === own && notice.sendIMUserID !== own);
77
+ }
10
78
  function transformStatusList(input) {
11
79
  const output = {
12
80
  values: [],
@@ -306,7 +374,11 @@ const useChatStore = defineStore("chatMessage", {
306
374
  state: () => {
307
375
  return {
308
376
  userID: "",
309
- // 用户ID
377
+ // SDK IM identity (legacy hosts still supply loginCode)
378
+ imUserID: "",
379
+ businessUserID: "",
380
+ loginCode: "",
381
+ imIdentityVersion: 1,
310
382
  nickname: "",
311
383
  // 用户昵称
312
384
  faceURL: "",
@@ -368,6 +440,10 @@ const useChatStore = defineStore("chatMessage", {
368
440
  getUserInfo(state) {
369
441
  return {
370
442
  userID: state.userID,
443
+ imUserID: state.imUserID,
444
+ businessUserID: state.businessUserID,
445
+ loginCode: state.loginCode,
446
+ imIdentityVersion: state.imIdentityVersion,
371
447
  nickname: state.nickname,
372
448
  faceURL: state.faceURL,
373
449
  loginStatus: state.loginStatus,
@@ -418,16 +494,8 @@ const useChatStore = defineStore("chatMessage", {
418
494
  },
419
495
  // 保存登录用户信息
420
496
  setUserInfo(obj) {
421
- this.userID = (obj == null ? void 0 : obj.userID) || this.userID;
422
- this.nickname = (obj == null ? void 0 : obj.nickname) || this.nickname;
423
- this.faceURL = (obj == null ? void 0 : obj.faceURL) || this.faceURL;
424
- this.imToken = (obj == null ? void 0 : obj.imToken) || this.imToken;
425
- this.chatToken = (obj == null ? void 0 : obj.chatToken) || this.chatToken;
426
- this.satoken = (obj == null ? void 0 : obj.satoken) || this.satoken;
427
- this.apiUrl = (obj == null ? void 0 : obj.apiUrl) || this.apiUrl;
428
- this.wsUrl = (obj == null ? void 0 : obj.wsUrl) || this.wsUrl;
429
- this.tenantID = (obj == null ? void 0 : obj.tenantID) || this.tenantID;
430
- this.platformID = obj.platformID || this.platformID;
497
+ const next = nextIMSession(this.$state, obj);
498
+ this.$patch(next);
431
499
  },
432
500
  // 设置窗口聚焦状态
433
501
  setIsFocused(val) {
@@ -456,7 +524,7 @@ const useChatStore = defineStore("chatMessage", {
456
524
  storage: localStorage,
457
525
  // 存储位置(默认 localStorage)
458
526
  // HttpOnly Cookie 认证:satoken 和 imToken 不再持久化到 localStorage,仅保留在 Pinia 内存中
459
- paths: ["userID", "nickname", "faceURL", "platformID", "apiUrl", "wsUrl", "tenantID"]
527
+ paths: ["userID", "nickname", "faceURL", "platformID", "apiUrl", "wsUrl", "tenantID", "imUserID", "businessUserID", "loginCode", "imIdentityVersion"]
460
528
  }
461
529
  ]
462
530
  }
@@ -9947,6 +10015,68 @@ class EventEmitter {
9947
10015
  this.emitter.all.clear();
9948
10016
  }
9949
10017
  }
10018
+ const requestInstance = axios.create({
10019
+ baseURL: "",
10020
+ timeout: 6e4,
10021
+ withCredentials: true
10022
+ // HttpOnly Cookie 认证:自动携带会话 Cookie
10023
+ });
10024
+ requestInstance.defaults.headers.post["Content-Type"] = "application/json; charset=utf-8";
10025
+ requestInstance.interceptors.request.use(
10026
+ function(config) {
10027
+ config.headers["X-Login-Type"] = "3";
10028
+ const chatStore = useChatStore();
10029
+ if (chatStore.satoken) {
10030
+ config.headers["satoken"] = chatStore.satoken;
10031
+ }
10032
+ return config;
10033
+ },
10034
+ function(error) {
10035
+ return Promise.reject(error);
10036
+ }
10037
+ );
10038
+ requestInstance.interceptors.response.use(
10039
+ function(response) {
10040
+ if ((response == null ? void 0 : response.status) == 200) {
10041
+ const code = response == null ? void 0 : response.data.code;
10042
+ if (EXIT_CODE.values.includes(code)) {
10043
+ if (EXIT_CODE[code] !== "token") {
10044
+ ElMessage.error(EXIT_CODE.label[code]);
10045
+ }
10046
+ }
10047
+ } else {
10048
+ ElMessage.error("接口出错,请检查网络后重试");
10049
+ }
10050
+ return response;
10051
+ },
10052
+ function(error) {
10053
+ return Promise.reject(error);
10054
+ }
10055
+ );
10056
+ class IMSessionChangedError extends Error {
10057
+ constructor() {
10058
+ super("IM 会话已切换,请重试");
10059
+ this.name = "IMSessionChangedError";
10060
+ }
10061
+ }
10062
+ function guardIMSDK(sdk, isCurrent) {
10063
+ const lifecycle = /* @__PURE__ */ new Set(["on", "off", "logout"]);
10064
+ return new Proxy(sdk, {
10065
+ get(target, key) {
10066
+ const value = Reflect.get(target, key);
10067
+ if (typeof value !== "function") return value;
10068
+ return (...args) => {
10069
+ if (!lifecycle.has(key) && !isCurrent()) return Promise.reject(new IMSessionChangedError());
10070
+ const result = Reflect.apply(value, target, args);
10071
+ if (!result || typeof result.then !== "function" || lifecycle.has(key)) return result;
10072
+ return result.then((data) => {
10073
+ if (!isCurrent()) throw new IMSessionChangedError();
10074
+ return data;
10075
+ });
10076
+ };
10077
+ }
10078
+ });
10079
+ }
9950
10080
  const HEX = "0123456789abcdef";
9951
10081
  function randomBytes(length) {
9952
10082
  if (typeof crypto !== "undefined" && crypto.getRandomValues) {
@@ -9980,7 +10110,9 @@ class ImKit extends EventEmitter {
9980
10110
  dataDir = "./",
9981
10111
  userID,
9982
10112
  imToken,
9983
- chatToken
10113
+ chatToken,
10114
+ imIdentityVersion = 1,
10115
+ isCurrentSession = () => true
9984
10116
  }) {
9985
10117
  super();
9986
10118
  __publicField(this, "apiUrl", "");
@@ -10004,6 +10136,11 @@ class ImKit extends EventEmitter {
10004
10136
  __publicField(this, "loginStatus", IM_LOGIN_STATUS.Logout);
10005
10137
  // 1 未登录 2 登录中 3 已登录
10006
10138
  __publicField(this, "groupStatus", IM_GROUP_STATUS.Nomal);
10139
+ this.imIdentityVersion = Number(imIdentityVersion);
10140
+ this.isCurrentSession = isCurrentSession;
10141
+ this.disposed = false;
10142
+ this.pendingGroupCreates = /* @__PURE__ */ new Map();
10143
+ this.businessApiUrl = apiUrl;
10007
10144
  this.apiUrl = `${apiUrl}/imapi`;
10008
10145
  this.wsUrl = `${wsUrl}/imws`;
10009
10146
  this.platformID = platformID;
@@ -10039,7 +10176,8 @@ class ImKit extends EventEmitter {
10039
10176
  // @openim/client-sdk和@openim/wasm-client-sdk 不需要初始化,直接调用 login 即可。
10040
10177
  initSDK() {
10041
10178
  try {
10042
- this.IMSDK = Ot();
10179
+ this.disposed = false;
10180
+ this.IMSDK = guardIMSDK(Ot(), () => !this.disposed && this.isCurrentSession());
10043
10181
  this.bindEvent();
10044
10182
  } catch (err) {
10045
10183
  console.error(err);
@@ -10079,6 +10217,7 @@ class ImKit extends EventEmitter {
10079
10217
  }
10080
10218
  // 清空即时通信实例
10081
10219
  clearSDK() {
10220
+ this.disposed = true;
10082
10221
  this.unbindEvent();
10083
10222
  this.IMSDK = null;
10084
10223
  }
@@ -10308,8 +10447,9 @@ class ImKit extends EventEmitter {
10308
10447
  console.log("请先初始化SDK");
10309
10448
  return;
10310
10449
  }
10311
- this.IMSDK.logout().then(() => {
10312
- this.getLoginStatus();
10450
+ return this.IMSDK.logout().then(() => {
10451
+ this.loginStatus = IM_LOGIN_STATUS.Logout;
10452
+ this.emit("updateStatus", this.loginStatus);
10313
10453
  return true;
10314
10454
  }).catch((error) => {
10315
10455
  console.log("IM登出异常:", error);
@@ -10982,6 +11122,10 @@ class ImKit extends EventEmitter {
10982
11122
  }
10983
11123
  // 发送消息,发送通过 create***Message 创建的消息,offlinePushInfo-离线消息体,groupID-发送单聊消息时传空字符串
10984
11124
  async sendMessage({ recvID, groupID, offlinePushInfo, message }, operationID = uuidv4()) {
11125
+ if (this.imIdentityVersion === 2) {
11126
+ if (groupID) assertIMTarget(groupID, "group");
11127
+ else assertIMTarget(recvID);
11128
+ }
10985
11129
  try {
10986
11130
  const res = await this.IMSDK.sendMessage({ recvID, groupID, offlinePushInfo, message }, operationID);
10987
11131
  if (res.data) {
@@ -11156,9 +11300,26 @@ class ImKit extends EventEmitter {
11156
11300
  * @param {String} operationID
11157
11301
  */
11158
11302
  async createGroup({ groupInfo, memberUserIDs, ownerUserID, adminUserIDs }, operationID = uuidv4()) {
11159
- var _a;
11303
+ var _a, _b, _c;
11304
+ if (this.imIdentityVersion === 2) {
11305
+ if (this.disposed || !this.isCurrentSession()) throw new IMSessionChangedError();
11306
+ if (ownerUserID !== this.userID) throw new Error("群主必须是当前用户");
11307
+ if (adminUserIDs == null ? void 0 : adminUserIDs.length) throw new Error("请在建群后设置管理员");
11308
+ const spec = JSON.stringify([groupInfo == null ? void 0 : groupInfo.groupName, ownerUserID, [...memberUserIDs || []].sort()]);
11309
+ const requestID = String((groupInfo == null ? void 0 : groupInfo.groupID) || this.pendingGroupCreates.get(spec) || operationID);
11310
+ this.pendingGroupCreates.set(spec, requestID);
11311
+ const result = await requestInstance.post(`${this.businessApiUrl}/api/v1/openim/createTenantGroup`, {
11312
+ requestID,
11313
+ groupName: groupInfo == null ? void 0 : groupInfo.groupName,
11314
+ members: memberUserIDs || []
11315
+ });
11316
+ if (this.disposed || !this.isCurrentSession()) throw new IMSessionChangedError();
11317
+ if (((_a = result.data) == null ? void 0 : _a.code) !== 200) throw new Error(((_b = result.data) == null ? void 0 : _b.msg) || "建群失败");
11318
+ this.pendingGroupCreates.delete(spec);
11319
+ return true;
11320
+ }
11160
11321
  try {
11161
- const res = await ((_a = this.IMSDK) == null ? void 0 : _a.createGroup(
11322
+ const res = await ((_c = this.IMSDK) == null ? void 0 : _c.createGroup(
11162
11323
  {
11163
11324
  groupInfo,
11164
11325
  memberUserIDs,
@@ -11227,6 +11388,10 @@ class ImKit extends EventEmitter {
11227
11388
  * @param {String} operationID
11228
11389
  */
11229
11390
  async inviteUserToGroup({ groupID, userIDList: userIDList2, reason }, operationID = uuidv4()) {
11391
+ if (this.imIdentityVersion === 2) {
11392
+ assertIMTarget(groupID, "group");
11393
+ for (const id of userIDList2 || []) assertIMTarget(id);
11394
+ }
11230
11395
  if (!groupID || !this.IMSDK) return;
11231
11396
  try {
11232
11397
  const res = await this.IMSDK.inviteUserToGroup({ groupID, userIDList: userIDList2, reason }, operationID);
@@ -11610,7 +11775,12 @@ const useUsers = () => {
11610
11775
  const getAllUserStatus = () => {
11611
11776
  return userOnlineStatus.value;
11612
11777
  };
11613
- return { updateUserList, getUserIds, updateUserStatus, getCurrentUserInfo, getAllUserStatus, getUserStatus };
11778
+ const resetUsers = () => {
11779
+ userList.value = [];
11780
+ userIDList.value = [];
11781
+ userOnlineStatus.value = {};
11782
+ };
11783
+ return { resetUsers, updateUserList, getUserIds, updateUserStatus, getCurrentUserInfo, getAllUserStatus, getUserStatus };
11614
11784
  };
11615
11785
  function getDefaultExportFromCjs(x3) {
11616
11786
  return x3 && x3.__esModule && Object.prototype.hasOwnProperty.call(x3, "default") ? x3["default"] : x3;
@@ -36259,7 +36429,7 @@ const toPinyin = (text) => {
36259
36429
  const conversationList = ref([]);
36260
36430
  const skipSortIds = /* @__PURE__ */ new Set();
36261
36431
  const currentConversation = ref({});
36262
- ref([]);
36432
+ const currentConversationUsers = ref([]);
36263
36433
  const useConversations = () => {
36264
36434
  const chatMessage = useChatStore();
36265
36435
  const baseUrl = chatMessage.getBaseUrl;
@@ -36448,6 +36618,8 @@ const useConversations = () => {
36448
36618
  if (id) skipSortIds.add(id);
36449
36619
  };
36450
36620
  const clearConversationList = () => {
36621
+ skipSortIds.clear();
36622
+ currentConversationUsers.value = [];
36451
36623
  conversationList.value = [];
36452
36624
  currentConversation.value = {};
36453
36625
  chatMessage.setUnReadCount(0);
@@ -37090,7 +37262,14 @@ const useMsgList = () => {
37090
37262
  }
37091
37263
  return obj.sessionType === IM_CVS_TYPE.Group ? `${obj.senderNickname}: [未知消息]` : `${obj.senderNickname}发送了消息`;
37092
37264
  };
37265
+ const clearMessageList = () => {
37266
+ messageList.value = [];
37267
+ startClientMsgID.value = "";
37268
+ isLastMessage.value = true;
37269
+ isFirstMessage.value = false;
37270
+ };
37093
37271
  return {
37272
+ clearMessageList,
37094
37273
  messageList,
37095
37274
  startClientMsgID,
37096
37275
  isLastMessage,
@@ -37271,7 +37450,7 @@ const useBurnAfterReading = () => {
37271
37450
  const origin = Number(startAt) || Date.now();
37272
37451
  const deadline = origin + duration * 1e3;
37273
37452
  if (deadline <= Date.now()) {
37274
- setTimeout(() => runHandlers(conversationID, msg.clientMsgID), 0);
37453
+ timers.set(key, setTimeout(() => fireBurn(conversationID, msg.clientMsgID), 0));
37275
37454
  return;
37276
37455
  }
37277
37456
  deadlines.set(key, deadline);
@@ -39524,13 +39703,14 @@ async function clearCache(userID) {
39524
39703
  function useMessageCache() {
39525
39704
  const chatMessage = useChatStore();
39526
39705
  function _getUserID() {
39527
- return chatMessage.userID || "";
39706
+ return imCacheNamespace(chatMessage);
39528
39707
  }
39529
39708
  async function loadConversationsFromCache() {
39530
39709
  const userID = _getUserID();
39531
39710
  if (!userID) return null;
39532
39711
  try {
39533
39712
  const list = await loadConversations(userID);
39713
+ if (userID !== _getUserID()) return null;
39534
39714
  if (list && Array.isArray(list) && list.length > 0) {
39535
39715
  console.log(
39536
39716
  `[MessageCache] Loaded ${list.length} conversations from IndexedDB cache`
@@ -39558,6 +39738,7 @@ function useMessageCache() {
39558
39738
  if (!userID || !conversationID) return null;
39559
39739
  try {
39560
39740
  const list = await loadMessages(userID, conversationID);
39741
+ if (userID !== _getUserID()) return null;
39561
39742
  if (list && Array.isArray(list) && list.length > 0) {
39562
39743
  console.log(
39563
39744
  `[MessageCache] Loaded ${list.length} messages for ${conversationID} from IndexedDB cache`
@@ -39601,6 +39782,19 @@ function useMessageCache() {
39601
39782
  const mittBus = mitt();
39602
39783
  const newMsgAudioUrl = "data:audio/mpeg;base64,SUQzAwAAAAAgAFRJVDMAAAAYAAAA1b6zpMvYssQoc2MuY2hpbmF6LmNvbSlUUFVCAAAAGAAAANW+s6TL2LLEKHNjLmNoaW5hei5jb20pV09BUgAAABcAANW+s6TL2LLEKHNjLmNoaW5hei5jb20pVEVOQwAAABgAAADVvrOky9iyxChzYy5jaGluYXouY29tKVRBTEIAAAAYAAAA1b6zpMvYssQoc2MuY2hpbmF6LmNvbSlUWUVSAAAABQAAADIwMjBUUEUyAAAAGAAAANW+s6TL2LLEKHNjLmNoaW5hei5jb20pVElUMgAAABgAAADVvrOky9iyxChzYy5jaGluYXouY29tKVRDT04AAAAYAAAA1b6zpMvYssQoc2MuY2hpbmF6LmNvbSlDT01NAAAAHAAAAGNoaQDVvrOky9iyxChzYy5jaGluYXouY29tKVRQRTEAAAAYAAAA1b6zpMvYssQoc2MuY2hpbmF6LmNvbSkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABJRDMEAAAAADYkVFhYWAAAAAsAAANUREFUADMwMDYAVFhYWAAAAAsAAANUSU1FADIxMzEAVFhYWAAAAAoAAANjb21tZW50AABQUklWAAA1OQAAWE1QADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+Cjx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQzIDc5LjE2MTIxMCwgMjAxNy8wOC8xMS0xMDoyODozNiAgICAgICAgIj4KIDxyZGY6UkRGIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyI+CiAgPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIKICAgIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIgogICAgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIKICAgIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIgogICAgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIgogICAgeG1sbnM6eG1wRE09Imh0dHA6Ly9ucy5hZG9iZS5jb20veG1wLzEuMC9EeW5hbWljTWVkaWEvIgogICAgeG1sbnM6ZGM9Imh0dHA6Ly9wdXJsLm9yZy9kYy9lbGVtZW50cy8xLjEvIgogICAgeG1sbnM6Y3JlYXRvckF0b209Imh0dHA6Ly9ucy5hZG9iZS5jb20vY3JlYXRvckF0b20vMS4wLyIKICAgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDo3YjdlYjE0OC03ZTllLTA3NDctYTNlNS05NzlmN2IzZTQ4ODEiCiAgIHhtcE1NOkRvY3VtZW50SUQ9ImU4YTMzOWUyLWI5YjQtM2I5MC02OTFkLWMwZWMwMDAwMDAzZCIKICAgeG1wTU06T3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjM2ZDgxZDUxLWYxM2QtMWQ0MS1hODYxLWIxZTlmZDEyMzgxOCIKICAgeG1wOk1ldGFkYXRhRGF0ZT0iMjAyMC0wNi0zMFQyMTozMTozNCswODowMCIKICAgeG1wOk1vZGlmeURhdGU9IjIwMjAtMDYtMzBUMjE6MzE6MzQrMDg6MDAiCiAgIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUHJlbWllcmUgUHJvIENDIDIwMTguMCAoV2luZG93cykiCiAgIHhtcDpDcmVhdGVEYXRlPSIyMDIwLTA2LTMwVDIxOjMxOjA0KzA4OjAwIgogICB4bXBETTphdWRpb1NhbXBsZVJhdGU9Ii0xIgogICB4bXBETTphdWRpb1NhbXBsZVR5cGU9IjE2SW50IgogICB4bXBETTphdWRpb0NoYW5uZWxUeXBlPSJTdGVyZW8iCiAgIHhtcERNOnN0YXJ0VGltZVNjYWxlPSIzMDAwMCIKICAgeG1wRE06c3RhcnRUaW1lU2FtcGxlU2l6ZT0iMTAwMSIKICAgZGM6Zm9ybWF0PSJNUDMiPgogICA8eG1wTU06SGlzdG9yeT4KICAgIDxyZGY6U2VxPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJzYXZlZCIKICAgICAgc3RFdnQ6aW5zdGFuY2VJRD0iZDEwNTQ1NzMtNjlmMS02Y2M2LTJhYTItNzk0ZDAwMDAwMDZhIgogICAgICBzdEV2dDp3aGVuPSIyMDIwLTA2LTMwVDIxOjMxOjM0KzA4OjAwIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQcmVtaWVyZSBQcm8gQ0MgMjAxOC4wIChXaW5kb3dzKSIKICAgICAgc3RFdnQ6Y2hhbmdlZD0iLyIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJjcmVhdGVkIgogICAgICBzdEV2dDppbnN0YW5jZUlEPSJ4bXAuaWlkOjIwYWNhNWE3LTAwMzQtNDM0OC05ZWQ4LTBlMWY3Y2IwOGVhZCIKICAgICAgc3RFdnQ6d2hlbj0iMjAyMC0wNi0zMFQyMTozMTozNCswODowMCIKICAgICAgc3RFdnQ6c29mdHdhcmVBZ2VudD0iQWRvYmUgUHJlbWllcmUgUHJvIENDIDIwMTguMCAoV2luZG93cykiLz4KICAgICA8cmRmOmxpCiAgICAgIHN0RXZ0OmFjdGlvbj0ic2F2ZWQiCiAgICAgIHN0RXZ0Omluc3RhbmNlSUQ9InhtcC5paWQ6ZmYxY2M1NmItMmUyMS1kMTRlLThhMGEtYTZlYWVjY2ZjODVmIgogICAgICBzdEV2dDp3aGVuPSIyMDIwLTA2LTMwVDIxOjMxOjM0KzA4OjAwIgogICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQcmVtaWVyZSBQcm8gQ0MgMjAxOC4wIChXaW5kb3dzKSIKICAgICAgc3RFdnQ6Y2hhbmdlZD0iLyIvPgogICAgIDxyZGY6bGkKICAgICAgc3RFdnQ6YWN0aW9uPSJzYXZlZCIKICAgICAgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDo3YjdlYjE0OC03ZTllLTA3NDctYTNlNS05NzlmN2IzZTQ4ODEiCiAgICAgIHN0RXZ0OndoZW49IjIwMjAtMDYtMzBUMjE6MzE6MzQrMDg6MDAiCiAgICAgIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFByZW1pZXJlIFBybyBDQyAyMDE4LjAgKFdpbmRvd3MpIgogICAgICBzdEV2dDpjaGFuZ2VkPSIvbWV0YWRhdGEiLz4KICAgIDwvcmRmOlNlcT4KICAgPC94bXBNTTpIaXN0b3J5PgogICA8eG1wTU06SW5ncmVkaWVudHM+CiAgICA8cmRmOkJhZz4KICAgICA8cmRmOmxpCiAgICAgIHN0UmVmOmluc3RhbmNlSUQ9IjBmODZjODFiLTgxYzAtOTY2Mi1lNGZlLTcyOTMwMDAwMDA2YSIKICAgICAgc3RSZWY6ZG9jdW1lbnRJRD0iZjNjYTE3ZDItZjhlNi1lNjY1LTMyZmEtYTI3ZDAwMDAwMDNkIgogICAgICBzdFJlZjpmcm9tUGFydD0idGltZTo1MDU1NzM4NjQ5NTBmMjU0MDE2MDAwMDAwZDIwOTIwMjk3ODYwMGYyNTQwMTYwMDAwMDAiCiAgICAgIHN0UmVmOnRvUGFydD0idGltZTowZDIwOTIwMjk3ODYwMGYyNTQwMTYwMDAwMDAiCiAgICAgIHN0UmVmOmZpbGVQYXRoPSLlvq7kv6HmnaXkv6Hmga8ubXA0IgogICAgICBzdFJlZjptYXNrTWFya2Vycz0iTm9uZSIvPgogICAgPC9yZGY6QmFnPgogICA8L3htcE1NOkluZ3JlZGllbnRzPgogICA8eG1wTU06UGFudHJ5PgogICAgPHJkZjpCYWc+CiAgICAgPHJkZjpsaT4KICAgICAgPHJkZjpEZXNjcmlwdGlvbgogICAgICAgeG1wOkNyZWF0ZURhdGU9IjE5MDQtMDEtMDFUMDA6MDBaIgogICAgICAgeG1wOk1vZGlmeURhdGU9IjIwMjAtMDYtMzBUMjE6MzA6MzQrMDg6MDAiCiAgICAgICB4bXA6TWV0YWRhdGFEYXRlPSIyMDIwLTA2LTMwVDIxOjMwOjM0KzA4OjAwIgogICAgICAgeG1wRE06bG9nQ29tbWVudD0i5pys6KeG6aKR55Sx5rmW5Y2X5LiA5ZSv5L+h5oGv56eR5oqA5byA5Y+R55qERVblvZXlsY/ova/ku7blvZXliLbvvIx3d3cuaWV3YXkuY24iCiAgICAgICB4bXBNTTpJbnN0YW5jZUlEPSIwZjg2YzgxYi04MWMwLTk2NjItZTRmZS03MjkzMDAwMDAwNmEiCiAgICAgICB4bXBNTTpEb2N1bWVudElEPSJmM2NhMTdkMi1mOGU2LWU2NjUtMzJmYS1hMjdkMDAwMDAwM2QiCiAgICAgICB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6OTk4YTUwOGUtY2IxMC01ODQ1LWI3YmItYzBhMTg2Yjk5YmJkIj4KICAgICAgPHhtcERNOmR1cmF0aW9uCiAgICAgICB4bXBETTp2YWx1ZT0iMTAyMTkiCiAgICAgICB4bXBETTpzY2FsZT0iMS8xMDAwIi8+CiAgICAgIDxkYzp0aXRsZT4KICAgICAgIDxyZGY6QWx0PgogICAgICAgIDxyZGY6bGkgeG1sOmxhbmc9IngtZGVmYXVsdCI+RVblvZXlsY8zLjkuN+i9r+S7tuW9leWItjwvcmRmOmxpPgogICAgICAgPC9yZGY6QWx0PgogICAgICA8L2RjOnRpdGxlPgogICAgICA8eG1wTU06SGlzdG9yeT4KICAgICAgIDxyZGY6U2VxPgogICAgICAgIDxyZGY6bGkKICAgICAgICAgc3RFdnQ6YWN0aW9uPSJzYXZlZCIKICAgICAgICAgc3RFdnQ6aW5zdGFuY2VJRD0iMGY4NmM4MWItODFjMC05NjYyLWU0ZmUtNzI5MzAwMDAwMDZhIgogICAgICAgICBzdEV2dDp3aGVuPSIyMDIwLTA2LTMwVDIxOjMwOjM0KzA4OjAwIgogICAgICAgICBzdEV2dDpzb2Z0d2FyZUFnZW50PSJBZG9iZSBQcmVtaWVyZSBQcm8gQ0MgMjAxOC4wIChXaW5kb3dzKSIKICAgICAgICAgc3RFdnQ6Y2hhbmdlZD0iLyIvPgogICAgICAgPC9yZGY6U2VxPgogICAgICA8L3htcE1NOkhpc3Rvcnk+CiAgICAgIDwvcmRmOkRlc2NyaXB0aW9uPgogICAgIDwvcmRmOmxpPgogICAgPC9yZGY6QmFnPgogICA8L3htcE1NOlBhbnRyeT4KICAgPHhtcE1NOkRlcml2ZWRGcm9tCiAgICBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjIwYWNhNWE3LTAwMzQtNDM0OC05ZWQ4LTBlMWY3Y2IwOGVhZCIKICAgIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MjBhY2E1YTctMDAzNC00MzQ4LTllZDgtMGUxZjdjYjA4ZWFkIgogICAgc3RSZWY6b3JpZ2luYWxEb2N1bWVudElEPSJ4bXAuZGlkOjIwYWNhNWE3LTAwMzQtNDM0OC05ZWQ4LTBlMWY3Y2IwOGVhZCIvPgogICA8Y3JlYXRvckF0b206d2luZG93c0F0b20KICAgIGNyZWF0b3JBdG9tOmV4dGVuc2lvbj0iLnBycHJvaiIKICAgIGNyZWF0b3JBdG9tOmludm9jYXRpb25GbGFncz0iL0wiCiAgICBjcmVhdG9yQXRvbTp1bmNQcm9qZWN0UGF0aD0iXFw/XEM6XFVzZXJzXEFkbWluaXN0cmF0b3JcRGVza3RvcFzpn7PmlYgucHJwcm9qIi8+CiAgIDxjcmVhdG9yQXRvbTptYWNBdG9tCiAgICBjcmVhdG9yQXRvbTphcHBsaWNhdGlvbkNvZGU9IjEzNDc0NDk0NTUiCiAgICBjcmVhdG9yQXRvbTppbnZvY2F0aW9uQXBwbGVFdmVudD0iMTEyOTQ2ODAxOCIvPgogICA8eG1wRE06cHJvamVjdFJlZgogICAgeG1wRE06dHlwZT0ibW92aWUiLz4KICAgPHhtcERNOmR1cmF0aW9uCiAgICB4bXBETTp2YWx1ZT0iMjQiCiAgICB4bXBETTpzY2FsZT0iMTAwMS8zMDAwMCIvPgogICA8eG1wRE06c3RhcnRUaW1lY29kZQogICAgeG1wRE06dGltZUZvcm1hdD0iMjk5N0Ryb3BUaW1lY29kZSIKICAgIHhtcERNOnRpbWVWYWx1ZT0iMDA7MDA7MDA7MDAiLz4KICAgPHhtcERNOmFsdFRpbWVjb2RlCiAgICB4bXBETTp0aW1lVmFsdWU9IjAwOzAwOzAwOzAwIgogICAgeG1wRE06dGltZUZvcm1hdD0iMjk5N0Ryb3BUaW1lY29kZSIvPgogIDwvcmRmOkRlc2NyaXB0aW9uPgogPC9yZGY6UkRGPgo8L3g6eG1wbWV0YT4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIAo8P3hwYWNrZXQgZW5kPSJ3Ij8+AFRTU0UAAAAPAAADTGF2ZjU4LjI5LjEwMAAAAAAAAAAAAAAA//tUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWGluZwAAAA8AAAAmAAAlaAAHBxQUFBkZHBwcICAgIyMnJyczMzM7O0NDQ0tLUFBQVlZWXl5kZGRpaWlwcH19fYSEjIyMkpKSmZmfn5+mpqarq7CwsLi4uL6+xsbGzc3U1NTc3Nzi4ujo6O7u7vj4/f39//8AAAAATGF2YzU4LjU0AAAAAAAAAAAAAAAAJAJAAAAAAAAAJWjf6z1f//sURAAP8AAAf4AAAAgAAA/wAAABAAAB/gAAACAAAD/AAAAEhDRCABQDzOiGQIAGkK2f+m0xFBeAgB2e//65w07/0//17a3gSSj4AADRmRiCpZ6TS8Xa9XmGk0IWlm+l//ukZB4BUAAAf4AAAAgAAA/wAAABAoAVK4eEQGA7AGSYkAAA3//////11QAYA23f/9t+lCeKaU8gVsWiyotFmopjjbH9RwKMBGx3oG4r///+4ABSK4Y1/ZIS5xnmnFXOFjLC5yWcl+nqaAQQ6AAGdEH0TKsazW+3qOBAfLqqj0bv+1X91FqEgFiAAPLCEGTrIgHM/9GTPPnP+omkBz8cdXAoIAmALO0qZAXJBJSYgUkzKHHD////0kqoDOAZhKRgUACcATn3TNDtTChcc3/+pdRAEekAAREoqCWAYszVTdrpPmguGDXrukEeZgAAVQCEIigRNBdw7ueVJ/Wz8+t/Z0dppEI1BlhAgqNDyQ0BAmQ6DQe6ufXI///RhNBA8BA01AzJU4IeZ/3UgwZAA18Qkwox5EIBYAAHRDRw+YOZo7+eRoQorWSVpQIwBZUbccUMCg+2pMqQQ2v2DVoUp7XJqhlCwBShHA4NMHnhS76QPECb//d///9TqCCSMIgDZMUk1e/p/90u44aezIAIAkYKSLAgxOAYaCTmAUG3IXQfN/QVCRaZ/hSGdZC3/7hkAAtrP9X6mFVSZHlEf/Zr/+gYFlK4AD8wxQk0FwqBigDIAPdDx+GBgbg8EFAFzgPPEf9T//tEZOqBEP8CykkmCAAJ4BlFBAABA5gbKSeMYABeACTkAAAAv+S/6kf+jf///3///9dgSGVgVQQAMAXCCQBAEAgDNfzfLmpYX0LmLy2fwtYEulxKA4P0BcZA/xH4yQWELt/FKCdzCTv/3KxOBcIHoAFgdv//c8MeSRcKpJf/+VDQlCZRGMFgHX///kuOYbNLjLFzl/////J80AwHA4AAoHA4HAoAAQA+//skRP2BEOEDychJABAVIElJFSAAA7QJKSCkAABegSTkJIAAfwMA8A//0/8cZS/9AkCKbEv//YJGFvC/o//+VkNaZh///v////5gaKIKJQgQByYQAZLAACBZv7wEhIKgYyTAyYA5odmiRZsBxv0ZnFBEoQHuwbVW//s0RPQBENYDSckjEAAYYDkoJEAAAtgFKSGAAABfgeVkIIgMbcqZVudwSpZo1xBJA04MZXJ0R1t4ZDABgiTDEexg4uxm8SQoMiFXuaUSw527zjz16DYHj6AdNctoMhZdDTKmsQqKU0OXraXkIYYqR1HvFgY0UeFpTyYf/65Xl7sQIxOLysywowQByWUyVhYM//skRPkBEKICykkjAAgYwIk4JCIBAvQJKSGMAAA9AOUgFIAEZnLAIOyK3UpL/ZfizSXxe868QnFL2lBQOjomsmkqZL5k81Z3nnRalGrrT+/3fzWbO43Ev//3udkjlas/+H/////Lq8vzz/////7kfiDz1qQlQlAY//s0ZPkA0S4ESkkmEAASQBkiBAAABRANJzRgAAAdgGTCgAAAMbMiYtX3DHGnULh2AaG/TNHecesz/PzibutNi4TQnla92pl80atkzQly9dqavYvlI1CRGSa6Wu+ptbd9ToKqvtQYwAC4ot79YNmSzp9LCh470fpo9imhrYIEgskAcQEoC10AWZgoMqZ+mBMo//ukZPsAAz5ZT/4+ABA2KTn9wTQAHOVrVfmtAAFrGSu/HtIAXM41A2FOF9XJihRMU1rQPqRSSSUZj1CIZI+cWgrWha62q0lmpiJ6UTFGtFaaz1Kkuvep6nRMTUyJocLpLVS5NHilS9FS1s7DtLB7v+KAzQys5gqKsdrdCAFgaPFRYROBUFYKukHQVBXb2S3/rBXq//yXnaC6yQaXhP73YBEASkEDXEJP10Pklo5hnHf8m0ZOUlCfMdpvCtNu9fShMdZNP5Bzv1lBlRnNGV2m2WmnXXgwRqYVCOoSYEVu/YLpnIDFKiGlQHQADAoMcKUBuUdLhHKYs/V///RnP+pgd4YGRBA6cmASoRwiB6ZIBIZeVOJQZ3GWtM94CJFtJG/Va7hWwv50exkgCTG7LZvv6Tr+tEqMUWpVf/qf/sH4iHjdgwqvwAPEiD/LWIAIAKIiffYACDQcUbBB0SiWQKRqUhDh1bCSnyv21qY3Ff1TvtAEYAAAHAAACxi5jqn07+Dg6u6B1dOk9y2WrvGIJh5hq+I+ghf2QP1TUtXliiw7ymfRrZlD67I2C2OF//01IiaIECmF5NnHHE3//9HKCCE8RQBEqKZII9SI9TC8swA1pAZOHEAAmnxdz0MQuhd8H4St//t0ZOkAAys72u9hoAgjoAqv4AABCTjPd+eEVmh1AGo8AAAEtERM6IpLqQjkY+Zf/+xptwJDS3/8l9QACAANAcAAAEIAF2DAsEDqBiBoOoEJgHUmz9YZNJSxwyD8agumsp4lKM7dKrbtU954GaUn/8DN8Ys0rBFhvUUP/21sXS8M4X0Lr7///+gVBIAlwIODwPAcJmPxCcWoAAAgAD4AAAFtsMiuetkRojMadDclwXWzeoqCt9DXgQp///1FgAoAGELiMybg1I4rK0oKsPVgQibAm+OhXarCOUZUKvOCxGBgtVeUUeLxWDEYCHJYmQMnRojIEqaEn//fRZEh4uIXE/b///1pC2BfQfY+yyoyQAACsQQ4YAGTlU9L8z+9VqQE//t0ZO8AAmE12XstHOglYFpPYCIBC5TbS65pR+CrD6j08Klsclwy/9///p6mBoLVR//rQAhjAzRhQ+0AFhH0YOD0hj84syQNicITROvQoQHsfyiPxUMSOvZrmT5ixP8bsLd4pfKBZ//+YnTUz9////Ubh2TJxgPYOBWi2RZmHwAAAFFiLWASQ3pIbk0/+m/A/6YGACD4gAAUqTLZsevISUDltDxbojCXbbtPLGFjO1K2xKCHesKGbCoXEyyOSBDDwGJByqf/9bq+shpPkW9v///mYn4UGQh0tolI1bgNgcAEdDv/oxFV4/7CfgnYAAAjACoV2z9Hw4G2ZOlQqLrHcyM6eNKIE0yr6I0iOb+rzHAgx4XOb4+JDMgkGI3mnmC3//t0ZPCBAvAx0eu6afgkI9ntJKKDChzDS67qY+CED6i0YKIk////54Rkxx0UsFgDDDgAAAQLzWT/Tg11n////wQUBVlAzgwpOCAG6CoWAD8tmKW2lObIGYN+rZ1osWHs8JhcVXLTQ5hHnHi9ibeE7PJ6fk/M6ij5vlkbr6/l9oAbGnzJ0///pIUYgBCTw3RODOAAQQPMBQkPyrvR0CrOtmf9v/7eSaAkABIE7wAAokKgLsnJNZICGMCAKAYbZ0l0t2B68OtxaQ1vF+KNeBniEFCsGjAIDSBgXan+ZhGc7OH/iK0HZf5ccgijE5t4JTKozDrliIiDpU4UwLjEyS7RdZE1bDZVbF9tB7jp53//KLOFURQiQQwrgPDUgfJFu9uR//tUZPSAUi4v1vtvamoZAKpfBCYDSVDHTU4OEGAngCh0AAAAoZo/IABpU0ELiBOb818Iv2Rjf/b8rQBUAAAAOlUIQFQA4mACoAaWYrGutZXa8ThR+ZeSchqijNKKGI8sOm5ymeiM8szVujU/ZpJbuJWqt7lWr2rJqhKQMn2o7i4ug8IpD0//9C6nIPDI6JAAosLvRgwAjgEBPAgAAHWFsWzaJmt9LlS3+Cfx5x19X//9B6BLYAEEF0wBLUvnJN9b//tkZOiAEg4vV2tJOyoUgiodBAJFCbS7W+0wrehbgKk8IAAEFSpqyiJ6xH+bcFymHlBKVNFZNIIgcabZiRNtRejAWicSU+k/SYdQccb3obm57q///pPoViEx1kzUxccFskSRgABDggAVliDz0l/+p+o9ib//kaAIgAGLFwAA6RaJlhqcsXBOkEESZeuhdkIfyzXfuGbkN5SksBC73W1yUf/D+UVrydrmk3r/P/hXJWGqf6scYQHQUDvRv///UQgCm7sSbDglAAhAW+In4l//v7NCYDqAAZZHKAFMsE+pmkmVTQxJDUqdtnjoLXnp+SwP//t0ZPEBA7gx1et4VMgYoBptBAAAC0i9Va3g6+h4lqj8IAuEhXlFPgSgQL+2soo7b798vR0tMcXll/0OEIImm9yk1vR////UbDQlCGLOAAAAACgAAAfD////Xy8ACVCIAG/eHABE1grQhYixOEJZNErGGLQSmyZW3jG/SRB3nE3Yxhg7lxr/8Rz5Z9p4XRv////ziMqJspqAAHqh7////KWgGwAAAscAAN1C4hkYU+hZiYxsLawMZQmEloqZep5nWY8BhA5Q8TGiQ/cYCZqt/6AaBQSRzpli3VP///x0aCxND2AAAAMBgABr/8Rij//65AFdVQBU5zlABSgFBIHFGSYgLGhgRyF+rmXa6PHjgCRQJLnTKyWNTFzQT84ggs6i//tkZOsAEkkxVutMgioW4AqPAAABCaC7Va288Sg6gCe0AAAAdW6aaadlp7ZPFE8ebWxsO8uIbVN1W//oLTMzM25MffSRkI50MAACwfWD4IOUCDi4P7sAKBDlw+Jw+P//rD8QKgAAEGA9FgI6ocmUgMADBSCMsFwCng1LdV/GHxCaWByQxiwApmFAbZ2hQwNhzRUBY8BBEC9xUDcAVHLzjKzQhCKABmXIZorrfMQw7SFbn0g2mupIFo1ryqOJbsVeOIxxHmGb6EcsmpguOwO9bs54pgtTZ3P9XXR3DIEUIYOaA1uYjkPqUsGoYv2UxrjE//tUZPMAcjsu1WtvPCoOgCo9GAAAB3C7Z+y9RSglAKfQ8AAA6Iyig4gvGoOy8DGIBKf////8sOfhh9hkheNdcXyjE44n////////////+hpLb3/3ebX5GCgExCNfuVZb5IgKAABhCbYjUzaSMm19viTQ8dQmJdRss/uvk8qcPHrSpnPza1TfgaVXXUD3aUULCwkULlKUf8Y4AEAA2gGMlAUA2gA0uT3ABhJi1NxECDsmpebJaoDCeYsfC68QFMjo//tkZPOAAggvVWtPOWoOQAnMAAAACby9XfWmgCiEAGi2ggAEPCF4DR5LuKqt6DYSsJnrml2ARcr5A4/MV+uCCpnmdlfCpgsKqrRQ7HLLpNxaw6sMRiXoiqmvP6zBuM9D8PMjgYxBWK0d2IwMHXwbAuWDGS3EmmpqMw5G5x3aiAaGrjX3hU2ZLUuU9+pWtOLASwypobpWQsXiO6eDrtLxRu++tPDv/WdEoZ5z//bwMEa8yXL///yy+/l0gKIBACsssAUCeBDAMFAAAOCHO7KUnSeqkj/WB9AKbPRPa74b2Z7amUHuzcbrf12gAGZHZA8Y//ukZPoABf1LVm5zJIBBJFrdwayQF60dYfmcgAC6Cet/AtJADfAWFTzyWHZSgoVEJJsEdhlbHrl2RxG/SpmKA8E1rQUIUShMYIaRkomIxFLLqLGqa2pJUloo/qfSRhpBSR4mt6z3Sbo11F57qVZ3SWjSuqj/5KjBOYNglmCshAHAEbADHvUk9Yh39zP/5V34mdKgqHeQkABRJiABIQhEFwNkBCEOBogpJAF2bSoo3eFaj8WdCLXL2UQezW9asOiUGedbW7DFc6TltP3kdqh1TR/UloLRA+gusmhk1t2U62rWXanU9LrWs1Seitv+ZGsSUZIMN0EAo/HHCAAAwE+oN7FPD/1g8iy//8T+teAGCwAAwPwArAwOEmKhojCjWw0wFgKX9ocTYXFpl8YIiFuHtbao2blzCmvqyBg7bvtkBYKpmSz//Jsb5scR/U7GazMILE6zda6/R6NLY6bFg8XmgXJ+V2R2gQSPQBAAJUZdL60YhwAp3/NymfcAcKCw7/9Rj1u6KAAUTQABLMbgBsZbylBxQZCmXFCEwP5h/mmNG2jtZWnSe10wm7fzuRnI+8rNNDUy2VKITv/QMQWCqsv7TnQCUTjEM0//5wXAJj0hHRoOh4JlAhphg4FgIA8gpyhM//t0ZOGBAus9W39hoAoboAqP4AABC7jzX+0xtGhfAKk0IAAEG2v+id//48t/zeSVwwCUx1M5/puAIyg1YSjlKJFwrir+bC/EMOxFL7vfpeI/7M40M8er1bIyJKVf/FYtPI/80gyAYTNcnt//8bjESdooSBgGAVAJYAgY0JOKIpBJqAAO/er//lXEf9euAAHKHMEu6VwAk6+PYqp0lSwsjWA2VwtcMhWmL5ur3Dsj9A6O8GYpS8cj+J0xuRSA/+iao/+s3RULI0NTIwuutV1pf8zIV0zScNzQAARAIAHggAAwryTca5G5//nX//Py9E//+r+oNqACCgAHwWwBB6VE8HGy9hLeAKiJsw/0+MlAYU7Osrap9D5L1vPrEHukqtWJ//t0ZOgAArsuVetplQof4motCAuRCgzHXe09R+BegOowIIAEqsaoMycMg0f+sagxlJH/nS4UEwqY2jiKA/ms3ucPoHTH/QHaJ0X5qYoMasAAEdAB8CAAOKBBHB+Ezvy6TCfyv/N/8zlXWAAprxMA3juACcoiKp0zU2xAASyjcTE4bWi9mS+Z9QSC2k+I4JBimhtMRgxrblhCvKEB6Z/8mBZE8pUf2Mx6mpiBvGw8dJZaV//9iaUjJNE2JiI7nAAMRaAABxR/RziRoSBOEzPx37dP//2/8GqgAIMpQiT4AQAgUSB3wUSZQGeRy2EiiBoWH6E43lC+UKvD/dbMErJN0y5VErGbnKEznokUBvJqCP9SR8wT/6JuiiDCMGYG5oio//tkZPYAAgIuW/sMOmocgEpvFMAACUDHZ+yxpyB1GSj8EAtE6///MW5qkOAHYDGAHAAAEwt7oZHJ9af7ls/VKyAAxmzghvR3AEXX3bLQofGNAlWeNFiUs5ujafjJUBZYkSkRWpXX+2ooGdvu5OuBo6Qx4ElD5fXwgcp///7dKA+hKiQb1Ur//7JP2+4axsbDSADQAATAOChwALLgoqCzWaOsNf/+qoAAcRYAFPATgBsKQuYCBKHmtBEuliAXpc5itse5qIlsKLeniavr3ukhnrbivs3b27HxQ8Ym7ptv7gMlBr///KOeaZqE1iab1//9//tkZPoAErsyVWtvafgZwLodAAMDCpTJYeytryBikij8cAnUdYiUtbi0jtfUAHkgewMD3cKRQcKAACAAAVfLLOjg67Jf//8t5AEKVKZy8LYAIOmI+KgMEqwEjVWKW9JgbfCUAGlolW6BbM5vtjL4vv9VE8gQ7I7cinCf+9QVB17877KpVojEczFLDOUA5kDBKGAWPTiEruy8j5jBAMY9REdpO4AgwYHguaOKWkiyZDetwZ4zX6jUHNc5OfX0Eh1/8vMbb7Jey+BFppoK8cW+//0IkIDzOPe+AggDT8s/DaADootH2AAAn6kY5HXD/wf+//tkZPCAEoQw1ntPafoUYQodACYlCljDW+09Z6hLgym8ACRM6vQAHQ7ZTv5TAAXkOF9pMeQvKpykDFIYNhK1XBaE8Q+R5tTTV9tVKlNZgql5lSH63vIDirZt/4c4H/9lcUABlEM58CkoQJGYCoYyU+eYGhAS9531fVu/pvEQhVt3J7p8ACqMg4yKDVhlFSjJDGwsnG49itHqfMbXZFIz5r/BQOs0razSInILN/jQ8Ft/5kUg4Bqc/xUAAMAOBh6OQAADE8Q0I63+QX9X1eT0gCETVYn5ZgAGckhrKetKmw6JSUiEQbGuUTKd7KUOKMDr//tkZPEAEsox1ftPWfoUoAp/AAABCHDJZ+wwSaA+gCi4AAAEL2N+2IGpdGYUhoTiogN2/1V3M/tID44FBHjQZPRKNH3EB8EBYAH7nckc9/5o1/9eipAAURoSJ5AHgBqiSGSRyiSCOCCNanQ+hVX5UM4WpkPf9eaNRqbchBjJeMz17OYJ+KskqFdnxr/IKM//dTkCQ1xVnd0YiCBKN/+JmFJMgcDhAAAAHgABivFBAA6gJEPsdqb5/tNf+p1EAAIAUBgGQFwAosoZbEAmBxakVCQblTokjPqPjdnPja3WF/yOQJTWs92mFKTkilMOogyb//tUZPaAEiEkWXsGLRgSIMpdBAYFCEyRY+w8SeBQgCh8AAAAFNGiRs11U9zM66///ng+0Hy6TQz+Wnna8bZO5+nLWiT1oNDEE2U5gDHakMMMV5NFoo1ZOC0KVlQFAgACVSUEJS9GenF/lNrQx/90kJ6AAHMaJQfQJYAmldZJTNHTEIDh91NR9GRQrckc5fN6A/zj0Dyqza/uSnOXJilrUEZl0t/duNVsbqdCih/62WiSIe0TEw9SKoZZ1oZF71Co//tUZO4AgeciWXsPKfoVYCpfCAABCEChY+w856BBAGh0AAAAEHBCFlDuKlkAG6KqLe3YAAEAXiYkbjAi5GNeJUpc+akN9dX1qU76rEAhDtDF7MvgB9VAsmFyhPkkIRJT4fR0oVDgkE3IuoQiHby2iKG0PSq8j02VWLMe230yHOv/nu0jCNEVq9j9F3cwxf6HGtQfDQVwIUZYIRpcAJQStezQSGtaO0WYvFuQ0Z2ur+X6quMgt22CR/t3ACBwcnJh//t0ZOmAAoo1VXtPKnoWYBovAAABDODbT+0t9WBkguh0AIgMD4JrpTNqzqjd5qeMlakuqw9C/iAj2Z50LRIWzle1VVGfWab6Xd2NkDT/6zZpND1UmkyNaLsYKtSczVP4IECOAHCMuYGaObI38bAAFQOYCDwtEIrxPgHt4Yt9ljt2g/P8553ofq/8PIozCXbWRN9pMAF4XHIkaTA/B9OY10y+LXSZPsx3AqtUX0dvGvWCKnLOyMjxJ60y2f6fVypSnqUmhrQ0XrLpofMAzJ0XU1PfZt6a0U0rKg1zUiCQHlHaYu1AACklREXkbQAgW5YbC1sApJZuEvOfP9e78v7/+r/3/X/eyv/U5DQVSrEQiY5XSfVxgAzhb+XRrGoRoiyv//tkZPOAErE11XstHiggIMpfDCMTCTjRW+wtS+hxBKg8xBgsflzFqV4iqKH6XgZF+E8dmHu/xoAzKqnXhKCxUfX135nNtrOM5/dFIXuESoe75jdq3o6HkbLT2OoZTPt1BAVtppDvmtgAto5BSMgxbU5gd3q3Vt20f9f7/2/6+n96v/273+UWOKONoSCXaRM7vWlAGIH/on6QHpO4HuOo/yes0ZcoBYmRPqlmyLPhUE0jRI6uoRqcbKX//Oiq2r//3sUY4gwlqG1zfMs+7UdbpPQ95xolTm0oz0SgpPBo5J+AkAARJC3J8IrXxksXTq3B//t0ZOsAAn0r1/sMa8ol4XoPPGYKCvDZXee02OCiHKi8gIl9ClOqKW/+v9kVSe7PuMXAAHVMYAXrICAUbYx7KMlEWRlJlL25mbEswmqL1jGDLc/U1nH9oRa2TadiRMTbTcGme+VuagigtdXl/P0mG5DKJYg9Asfv702UxRKWvGO21ny41gC1egXWMgATHigsPwSO/z9GnWj9mP/RZmExHMh3dNtgFtCsqLeepIBpiqJPHF1PCRSB5S0gvZsqQ2tXtWrWV9p8RLSuZgR1kZnaoAGCwf/vWIYIKMPpo66v/04lEul5Ulaltu4J90N7UAAEgDBYVUgAI1+hK2yWg/w3Vn/oZ8kLJ0ZqSfQgyHsVTf+R4BRQmJgnRpfqbbFmrrjZ//t0ZO4AAn4t1vnsU9oqRwpPBYVBSfzTV+esT+iPCOf8FYjMSxSvBDeP1T1fUECzijoPkiR2LsgpNaOH3/t/N2S6Pub37DjjZd4tGO+jkdVHp+VOkRPNhn6R+Rmju0sjZ8EikNACpoF5AAMhP94+mmq+oyfbrY4sBcK/DqP5D+xCUX2QA6JQoF9aQQBCIO1pHOac8q9XEvtwzENvFB6BIOyP2kId7fXOLoQUL5N1nA8OGlB47lpc5p6Uaiv2Tcu4aanBFCykKu+Erm6qPiZYce0euvm2pmEoP0iAH7IAGDYHIUCoZBVTTu9R4wLHj2//6MRQt30yL76VsCDQdLNXETU2LAX4UHrRRjt58KeN0rWPIhD/PsGLBuHbLzYmWUU+//tkZPUAApQiU/sPSloXYMn9BAMTCiTZV+eJFqhvA6e8EBhUv/ujyFXS19TFL3TzaA/jGlXTX81FN3vaCSvH163SLYySMg5ymXBAFjlmzAAAYKGOWsF67L5ZaQzQrOs0yJhKNJAM8hQADS2eezd9VIEGSZifKwzoN7g8DJ2w0NN0YDn5dZEHTAgE2JJE+D52Tv/7aQd8xct4/lsi0hWy2OO+ZvrIeR+kNcghBi1+IFoAAB3hXWzwBNq18bhuot6qe1qvqADNSpWAtm1AXf12AB0VkcVKoAwhSglBQqcwEDSQyA/Qi8Jmf92Lg643TftJ//t0ZO+AErc8VfsLG/gewsmsBSJBCzy1S+wxaehSAybwEBgc8pFhtX//pKq///C6xBRjM/+cV266PapNZKF4SHMSlTtYr4ICAC6pMAAAT6VdnVdO3OVO+pfWZkhB1XAAbfESAE5FbdNT65xISTmMcgBJxvcbrUmTw5AeVF3egJREImuKxGEup/+DKcWqfynKwUQCPWZ5t/fZ09zUMurHDhCGF0UoABS4My7fC6hnAQTgs2p80SZIwVczwSBTqOWjYJScN2n+aIAdBLv2gyVTBmJTVEh9GqWbjY0ugy+P4HhqPU2I2LrmY7ibAuLlAj/40ayqdvrIkwqS3y9cdS32091yjWzvb5A+oiTqIAAB0EBGzoSkoJEhI49npJWVVUd9//tkZPuAEqE31XsLG/oXADntDAABCfTFS+wsb+hahKg8kIwsP1f16ZJggnoUW/FJACQpn4MmYYgFiBPSlwuxsX0TRZGMD0+YjitzqFpaoRz26TJVU6jX/1l/+xVCAY41bLT2VrshnMa5yqpIiwoRBFQa7epGFgLLrQCwoss9vuhoQ5YDb/IqpFARaSFN+WAABCBRofgIEoyLrIQQ+VYhKG7Wo6zN/HchzdZFHRuwsw3DULoaMmO4nkZJSlpppqU1SFSv+7mCAgw9UEDZZupkF/3e11Mt2Uks8zrNzFxU4a1vLIa2/PWoAAOnQcCBvDhG//tkZPiAElc01PnrGvoU4CnNJAABCSzVSeycTuhqAyg8ECQMfH96NDghCwMYEcABAIn5RySBEo7DFSEKYCAS6MqwpOp+zNMBAAAAP6IShRU0E1LmmCCY6PuacWjAEGMqMYCAQ/GTOEwyIKUvMEFcWrM8OAACpCJ/kfWopsC0krE0WlOtq3xoD9s8Z6ypl1vm95uateYfxg7R1pLGh9wrneu/Ka9uKOO406/b/T2VNarb/H8ce/uv3Ddampt5Vvyz/H/7/26eny/Pv02PK2P1qv//////595vvP7hzHVWyIQ0Cv8MFHAgUDATBUqdEs7///tUZPyAEjYiU3sJK0oeQMm9MCkDCXjTR+ysrWhKgCe0AAAE////qEyAWeDSGdWZw2wAAAAAAA7PDy2vdviCQp8RAEQmECDyBc0BSO/M6dMN+qqSJlakAAgAYNLPMBcAqVkgNNRfSGxEAVUdCVoDEYqqM/kx96JCmFQDo5y2hvcRYcEUqljw70MeUb1TjyqNJFYy0te9HIr9amt56hKwQlBiAAOqUP+gJNVXC8qDUFX/yj2/T///9cUqAUFriUAu//uUZOiAAs400H1poAomobnNpAgBFw0hSfm8AkCqBqi/DDABgrUeDsQ1TEFNRTMuMTAwVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//tUZOSD8lwZSU8kYAAbgTk54YAAAWwBJwAAACAAAD/AAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//sUZOGP8AAAf4AAAAgAAA/wAAABAAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV";
39603
39784
  let imInstance = null;
39785
+ let failedLogoutClient = null;
39786
+ let sessionRevision = 0;
39787
+ let sessionQueue = Promise.resolve();
39788
+ let pendingSessionKey = "";
39789
+ const subscribedStores = /* @__PURE__ */ new WeakSet();
39790
+ let activeSessionScope = "";
39791
+ let reloginTimer = null;
39792
+ const enqueueSession = (operation) => {
39793
+ const result = sessionQueue.then(operation, operation);
39794
+ sessionQueue = result.catch(() => {
39795
+ });
39796
+ return result;
39797
+ };
39604
39798
  let initPromise = null;
39605
39799
  let httpAuthBroken = false;
39606
39800
  let syncFinished = false;
@@ -39620,7 +39814,8 @@ const resolveSyncWaiters = (ok) => {
39620
39814
  };
39621
39815
  const resetSyncState = () => {
39622
39816
  syncFinished = false;
39623
- syncWaiters = [];
39817
+ const waiters = syncWaiters.splice(0);
39818
+ waiters.forEach((done) => done(false));
39624
39819
  };
39625
39820
  const buildConversationFromMessage = (msg, selfUserID) => {
39626
39821
  if (!msg) return null;
@@ -39698,13 +39893,14 @@ const useMessage = (config = {}) => {
39698
39893
  }
39699
39894
  const loginStatus = computed(() => messageStore.loginStatus);
39700
39895
  let msgAudioEl = null;
39896
+ let initTimer = null;
39701
39897
  const getMsgAudio = () => {
39702
39898
  if (!msgAudioEl) {
39703
39899
  msgAudioEl = new Audio(newMsgAudioUrl);
39704
39900
  }
39705
39901
  return msgAudioEl;
39706
39902
  };
39707
- const { updateUserStatus } = useUsers();
39903
+ const { updateUserStatus, resetUsers } = useUsers();
39708
39904
  const {
39709
39905
  conversationList: conversationList2,
39710
39906
  currentConversation: currentConversation2,
@@ -39716,6 +39912,7 @@ const useMessage = (config = {}) => {
39716
39912
  } = useConversations();
39717
39913
  const {
39718
39914
  messageList: messageList2,
39915
+ clearMessageList,
39719
39916
  updateMessageList,
39720
39917
  updateMessageIsRead,
39721
39918
  handleMessage,
@@ -39730,6 +39927,8 @@ const useMessage = (config = {}) => {
39730
39927
  onBurn(async (conversationID, clientMsgID) => {
39731
39928
  var _a;
39732
39929
  if (!conversationID || !clientMsgID) return;
39930
+ const burnScope = imSessionScope(messageStore);
39931
+ const burnRevision = sessionRevision;
39733
39932
  const inCurrentList = messageList2.value.some((item) => item.clientMsgID === clientMsgID);
39734
39933
  if (inCurrentList) deleteMessageById(clientMsgID);
39735
39934
  try {
@@ -39737,6 +39936,7 @@ const useMessage = (config = {}) => {
39737
39936
  } catch (error) {
39738
39937
  console.error("[IM] 删除焚毁消息失败:", error);
39739
39938
  }
39939
+ if (burnRevision !== sessionRevision || burnScope !== imSessionScope(messageStore)) return;
39740
39940
  if (inCurrentList) {
39741
39941
  const rest = JSON.parse(JSON.stringify(messageList2.value));
39742
39942
  saveMessagesToCache(conversationID, rest);
@@ -39746,6 +39946,7 @@ const useMessage = (config = {}) => {
39746
39946
  }
39747
39947
  try {
39748
39948
  const cached = await loadMessagesFromCache(conversationID);
39949
+ if (burnRevision !== sessionRevision || burnScope !== imSessionScope(messageStore)) return;
39749
39950
  if ((_a = cached == null ? void 0 : cached.list) == null ? void 0 : _a.length) {
39750
39951
  const rest = cached.list.filter((item) => item.clientMsgID !== clientMsgID);
39751
39952
  saveMessagesToCache(conversationID, rest);
@@ -39772,7 +39973,10 @@ const useMessage = (config = {}) => {
39772
39973
  const getUnReadMessageCount = async (bool = false) => {
39773
39974
  if (bool) {
39774
39975
  if (!imInstance) return;
39976
+ const countScope = imSessionScope(messageStore);
39977
+ const countRevision = sessionRevision;
39775
39978
  const count = await (imInstance == null ? void 0 : imInstance.getTotalUnreadMsgCount());
39979
+ if (countRevision !== sessionRevision || countScope !== imSessionScope(messageStore)) return;
39776
39980
  messageStore.setUnReadCount(count || 0);
39777
39981
  } else {
39778
39982
  const count = conversationList2.value.reduce((pre, cur) => {
@@ -39802,47 +40006,90 @@ const useMessage = (config = {}) => {
39802
40006
  }
39803
40007
  return bool;
39804
40008
  };
39805
- const initIMSDK = async (force = false) => {
39806
- if (initPromise) return initPromise;
39807
- const sameSession = !force && !!imInstance && messageStore.loginStatus === IM_LOGIN_STATUS.Logged && imInstance.userID === String(messageStore.userID || "") && imInstance.imToken === messageStore.imToken && imInstance.apiUrl === `${messageStore.apiUrl}/imapi` && imInstance.wsUrl === `${messageStore.wsUrl}/imws`;
39808
- if (sameSession) {
39809
- window["imInstance"] = imInstance;
39810
- return true;
39811
- }
39812
- initPromise = (async () => {
39813
- if (messageStore.loginStatus === IM_LOGIN_STATUS.Logged) {
39814
- await clearInstance();
40009
+ const disposeInstance = async (clearData = true) => {
40010
+ if (reloginTimer) clearTimeout(reloginTimer);
40011
+ reloginTimer = null;
40012
+ if (initTimer) clearTimeout(initTimer);
40013
+ initTimer = null;
40014
+ unbindMessageEvent();
40015
+ cancelAllBurns();
40016
+ const previous = imInstance || failedLogoutClient;
40017
+ previous == null ? void 0 : previous.removeAllListeners();
40018
+ let loggedOut = false;
40019
+ try {
40020
+ const result = await (previous == null ? void 0 : previous.logout());
40021
+ if (result === false) throw new Error("IM 退出失败,请重试后再切换");
40022
+ loggedOut = true;
40023
+ } catch (error) {
40024
+ failedLogoutClient = previous;
40025
+ throw error;
40026
+ } finally {
40027
+ if (loggedOut) {
40028
+ previous == null ? void 0 : previous.clearSDK();
40029
+ if (failedLogoutClient === previous) failedLogoutClient = null;
39815
40030
  }
40031
+ if (imInstance === previous) imInstance = null;
40032
+ if (typeof window !== "undefined" && window.imInstance === previous) window.imInstance = null;
39816
40033
  resetSyncState();
39817
- if (!imInstance) {
39818
- imInstance = new ImKit({
39819
- userID: messageStore.userID,
39820
- imToken: messageStore.imToken,
39821
- chatToken: messageStore.chatToken,
39822
- apiUrl: messageStore.apiUrl,
39823
- wsUrl: messageStore.wsUrl
39824
- });
39825
- } else {
39826
- imInstance.userID = String(messageStore.userID || "");
39827
- imInstance.imToken = messageStore.imToken;
39828
- imInstance.chatToken = messageStore.chatToken;
39829
- imInstance.apiUrl = `${messageStore.apiUrl}/imapi`;
39830
- imInstance.wsUrl = `${messageStore.wsUrl}/imws`;
40034
+ if (clearData) {
40035
+ clearConversationList();
40036
+ clearMessageList();
40037
+ resetUsers();
40038
+ messageStore.currentChatId = "";
40039
+ messageStore.remoteConnectUrl = "";
40040
+ messageStore.remoteConnectToken = "";
40041
+ messageStore.isViewingChat = false;
40042
+ }
40043
+ messageStore.setLoginStatus(IM_LOGIN_STATUS.Logout);
40044
+ }
40045
+ };
40046
+ const initIMSDK = async (force = false) => {
40047
+ const snapshot = { ...messageStore.$state, ...normalizeIMIdentity(messageStore) };
40048
+ const scope = imSessionScope(snapshot);
40049
+ if (!scope || !snapshot.imToken) return false;
40050
+ const key = JSON.stringify([scope, snapshot.imToken]);
40051
+ if (initPromise && pendingSessionKey === key) return initPromise;
40052
+ const sameSession = !force && imInstance && activeSessionScope === scope && messageStore.loginStatus === IM_LOGIN_STATUS.Logged && imInstance.imToken === snapshot.imToken;
40053
+ if (sameSession && !initPromise) return true;
40054
+ const revision = ++sessionRevision;
40055
+ pendingSessionKey = key;
40056
+ const operation = enqueueSession(async () => {
40057
+ if (revision !== sessionRevision || imSessionScope(messageStore) !== scope) return false;
40058
+ await disposeInstance(activeSessionScope !== scope);
40059
+ if (revision !== sessionRevision || imSessionScope(messageStore) !== scope) return false;
40060
+ activeSessionScope = scope;
40061
+ const client = new ImKit({
40062
+ userID: snapshot.userID,
40063
+ imToken: snapshot.imToken,
40064
+ chatToken: snapshot.chatToken,
40065
+ imIdentityVersion: snapshot.imIdentityVersion,
40066
+ apiUrl: snapshot.apiUrl,
40067
+ wsUrl: snapshot.wsUrl,
40068
+ platformID: snapshot.platformID,
40069
+ isCurrentSession: () => revision === sessionRevision && imSessionScope(messageStore) === scope
40070
+ });
40071
+ imInstance = client;
40072
+ client.initSDK();
40073
+ bindMessageEvent(client, revision, scope);
40074
+ const loginOk = await client.login();
40075
+ if (revision !== sessionRevision || imSessionScope(messageStore) !== scope) {
40076
+ await disposeInstance(true);
40077
+ return false;
39831
40078
  }
39832
- imInstance == null ? void 0 : imInstance.initSDK();
39833
- bindMessageEvent();
39834
- const loginOk = await (imInstance == null ? void 0 : imInstance.login());
39835
40079
  if (loginOk !== false) httpAuthBroken = false;
39836
- window["imInstance"] = imInstance;
40080
+ if (typeof window !== "undefined") window.imInstance = client;
39837
40081
  return loginOk !== false;
39838
- })();
40082
+ });
40083
+ initPromise = operation;
39839
40084
  try {
39840
- return await initPromise;
40085
+ return await operation;
39841
40086
  } finally {
39842
- initPromise = null;
40087
+ if (initPromise === operation) {
40088
+ initPromise = null;
40089
+ pendingSessionKey = "";
40090
+ }
39843
40091
  }
39844
40092
  };
39845
- let reloginTimer = null;
39846
40093
  let reloginAttempts = 0;
39847
40094
  let tokenInvalidNotified = false;
39848
40095
  let recoveredToken = "";
@@ -39859,11 +40106,17 @@ const useMessage = (config = {}) => {
39859
40106
  notifyTokenInvalid({ errMsg: "auto relogin exhausted" });
39860
40107
  return;
39861
40108
  }
40109
+ const recoveryScope = imSessionScope(messageStore);
40110
+ const recoveryRevision = sessionRevision;
39862
40111
  reloginTimer = setTimeout(async () => {
39863
40112
  reloginTimer = null;
40113
+ if (recoveryRevision !== sessionRevision || recoveryScope !== imSessionScope(messageStore)) return;
39864
40114
  reloginAttempts += 1;
39865
40115
  console.warn(`[IM] token 已失效,自动重新登录(第 ${reloginAttempts} 次)`);
39866
40116
  const ok = await initIMSDK(true);
40117
+ const recoveryClient = imInstance;
40118
+ const currentRevision = sessionRevision;
40119
+ if (recoveryScope !== imSessionScope(messageStore) || !recoveryClient) return;
39867
40120
  if (!ok) {
39868
40121
  tryRelogin();
39869
40122
  return;
@@ -39872,7 +40125,9 @@ const useMessage = (config = {}) => {
39872
40125
  tokenInvalidNotified = false;
39873
40126
  try {
39874
40127
  await waitForSyncFinish(8e3);
39875
- const list = await (imInstance == null ? void 0 : imInstance.getConversationListSplit(0, 200));
40128
+ if (currentRevision !== sessionRevision || recoveryScope !== imSessionScope(messageStore)) return;
40129
+ const list = await recoveryClient.getConversationListSplit(0, 200);
40130
+ if (currentRevision !== sessionRevision || recoveryScope !== imSessionScope(messageStore)) return;
39876
40131
  if (list == null ? void 0 : list.length) {
39877
40132
  handleConversationList(list, "init", handleMessage);
39878
40133
  }
@@ -39906,34 +40161,31 @@ const useMessage = (config = {}) => {
39906
40161
  tokenInvalidNotified = false;
39907
40162
  tryRelogin();
39908
40163
  };
39909
- const clearInstance = async (bool) => {
39910
- unbindMessageEvent();
39911
- cancelAllBurns();
39912
- imInstance == null ? void 0 : imInstance.removeAllListeners();
39913
- await (imInstance == null ? void 0 : imInstance.logout());
39914
- imInstance == null ? void 0 : imInstance.clearSDK();
39915
- if (imInstance) {
39916
- imInstance.syncFinished = false;
39917
- }
39918
- resetSyncState();
39919
- if (bool) {
39920
- imInstance = null;
39921
- window.imInstance = null;
39922
- clearConversationList();
39923
- }
39924
- messageStore.setLoginStatus(IM_LOGIN_STATUS.Logout);
40164
+ const clearInstance = async () => {
40165
+ ++sessionRevision;
40166
+ pendingSessionKey = "";
40167
+ if (reloginTimer) clearTimeout(reloginTimer);
40168
+ reloginTimer = null;
40169
+ return enqueueSession(async () => {
40170
+ await disposeInstance(true);
40171
+ activeSessionScope = "";
40172
+ });
39925
40173
  };
39926
- const bindMessageEvent = () => {
39927
- if (!imInstance || !imInstance.on) return;
39928
- imInstance.on("im-error", handleImError);
40174
+ const bindMessageEvent = (client, revision, scope) => {
40175
+ const isActive = () => revision === sessionRevision && client === imInstance && imSessionScope(messageStore) === scope;
40176
+ const onSessionEvent = (event, handler) => client.on(event, (...args) => {
40177
+ if (isActive()) return handler(...args);
40178
+ });
40179
+ if (!client || !client.on) return;
40180
+ onSessionEvent("im-error", handleImError);
39929
40181
  bindUnhandledRejection(handleImError);
39930
- imInstance.on(CbEvents.OnUserTokenExpired, () => {
40182
+ onSessionEvent(CbEvents.OnUserTokenExpired, () => {
39931
40183
  console.log("用户授权信息过期,重新登录");
39932
40184
  });
39933
- imInstance.on(CbEvents.OnSelfInfoUpdated, (data) => {
40185
+ onSessionEvent(CbEvents.OnSelfInfoUpdated, (data) => {
39934
40186
  console.log("用户资料更新:", data);
39935
40187
  });
39936
- imInstance.on(CbEvents.OnUserStatusChanged, (data) => {
40188
+ onSessionEvent(CbEvents.OnUserStatusChanged, (data) => {
39937
40189
  console.log("用户在线状态变化:", data);
39938
40190
  if (data == null ? void 0 : data.userID) {
39939
40191
  updateUserStatus([data]);
@@ -39955,11 +40207,10 @@ const useMessage = (config = {}) => {
39955
40207
  }
39956
40208
  });
39957
40209
  const handleSunloginEvent = (data) => {
39958
- var _a;
39959
- const info = typeof data.data === "string" && data.data.indexOf("{") > -1 ? JSON.parse(data.data) : {};
40210
+ const info = parseIMBusinessData(data.data);
40211
+ if (!info) return;
39960
40212
  const { event, sendUserID, recvUserID, sendUserName, address, session } = info;
39961
- const currentUserId = (_a = messageStore.getUserInfo) == null ? void 0 : _a.userID;
39962
- if (sendUserID === currentUserId) return;
40213
+ if (!shouldHandleRemoteNotice(info, messageStore.getUserInfo)) return;
39963
40214
  const SUN_EVENTS = {
39964
40215
  "create-sl-remote": () => mittBus.emit("createSLRemote", { address, session }),
39965
40216
  "apply-sl-control": () => mittBus.emit("applySLControl", { sendUserName, sendUserID, recvUserID }),
@@ -39973,17 +40224,13 @@ const useMessage = (config = {}) => {
39973
40224
  if (handler) handler();
39974
40225
  };
39975
40226
  const handleImNoticeEvent = (data) => {
39976
- var _a, _b;
39977
- const info = typeof data.data === "string" && data.data.indexOf("{") > -1 ? JSON.parse(data.data) : {};
39978
- const { event, sendUserID, imToken } = info;
39979
- if (event !== "LOGIN_NOTICE") return;
39980
- const currentUserId = (_a = messageStore.getUserInfo) == null ? void 0 : _a.userID;
39981
- const curImToken = (_b = messageStore.getUserInfo) == null ? void 0 : _b.imToken;
39982
- if (sendUserID === currentUserId && imToken !== curImToken) {
40227
+ const info = parseIMBusinessData(data.data);
40228
+ if (!info) return;
40229
+ if (shouldHandleLoginNotice(info, messageStore.getUserInfo)) {
39983
40230
  mittBus.emit("LOGOUT_NOTICE");
39984
40231
  }
39985
40232
  };
39986
- imInstance.on(CbEvents.OnRecvCustomBusinessMessage, (data) => {
40233
+ onSessionEvent(CbEvents.OnRecvCustomBusinessMessage, (data) => {
39987
40234
  if (data.key === "sunlogin") {
39988
40235
  handleSunloginEvent(data);
39989
40236
  } else if (data.key === "imNotice") {
@@ -39993,13 +40240,14 @@ const useMessage = (config = {}) => {
39993
40240
  const resolveConversationIDs = async (list) => {
39994
40241
  var _a;
39995
40242
  for (let i3 = 0; i3 < list.length; i3++) {
40243
+ if (!isActive()) return;
39996
40244
  const item = list[i3];
39997
40245
  let cvsId = getConversationIDBySessionType(item, "sendID");
39998
40246
  if (!cvsId) {
39999
40247
  let info = null;
40000
40248
  if (!httpAuthBroken) {
40001
40249
  try {
40002
- info = await (imInstance == null ? void 0 : imInstance.getOneConversation(
40250
+ info = await (client == null ? void 0 : client.getOneConversation(
40003
40251
  item.sessionType === IM_CVS_TYPE.Single ? item.sendID : item.groupID,
40004
40252
  item == null ? void 0 : item.sessionType
40005
40253
  ));
@@ -40007,6 +40255,7 @@ const useMessage = (config = {}) => {
40007
40255
  console.warn("[IM] 获取会话信息失败,改用消息数据兜底:", error);
40008
40256
  }
40009
40257
  }
40258
+ if (!isActive()) return;
40010
40259
  if (info == null ? void 0 : info.conversationID) {
40011
40260
  item.showName = info.showName;
40012
40261
  item.faceURL = info.faceURL;
@@ -40015,7 +40264,8 @@ const useMessage = (config = {}) => {
40015
40264
  handleConversationList([info], "add", handleMessage);
40016
40265
  if (info.ex) {
40017
40266
  const clearedEx = preserveBurnInEx("", info.ex);
40018
- await (imInstance == null ? void 0 : imInstance.setConversation({ conversationID: cvsId, ex: clearedEx }));
40267
+ await (client == null ? void 0 : client.setConversation({ conversationID: cvsId, ex: clearedEx }));
40268
+ if (!isActive()) return;
40019
40269
  info.ex = clearedEx;
40020
40270
  }
40021
40271
  } else {
@@ -40050,6 +40300,7 @@ const useMessage = (config = {}) => {
40050
40300
  for (const [cvsID, items] of grouped) {
40051
40301
  try {
40052
40302
  const cached = ((_a = await loadMessagesFromCache(cvsID)) == null ? void 0 : _a.list) || [];
40303
+ if (!isActive()) return;
40053
40304
  saveMessagesToCache(cvsID, dedupeMessages([...cached, ...items]));
40054
40305
  } catch (error) {
40055
40306
  console.warn("[IM] 兜底会话消息写入缓存失败:", error);
@@ -40060,6 +40311,7 @@ const useMessage = (config = {}) => {
40060
40311
  const curMsgList = [];
40061
40312
  const cvs = currentConversation2.value;
40062
40313
  for (let i3 = 0; i3 < list.length; i3++) {
40314
+ if (!isActive()) return;
40063
40315
  const item = list[i3];
40064
40316
  const cvsId = item.conversationID;
40065
40317
  if (item.sessionType === IM_CVS_TYPE.Single && (cvs == null ? void 0 : cvs.userID) === item.sendID || item.groupID && cvs.groupID === item.groupID) {
@@ -40077,11 +40329,13 @@ const useMessage = (config = {}) => {
40077
40329
  }
40078
40330
  return curMsgList;
40079
40331
  };
40080
- imInstance.on(CbEvents.OnRecvNewMessages, async (list) => {
40332
+ onSessionEvent(CbEvents.OnRecvNewMessages, async (list) => {
40081
40333
  console.log("批量接收消息:", list);
40082
40334
  if ((list == null ? void 0 : list.length) > 0) {
40083
40335
  await resolveConversationIDs(list);
40336
+ if (!isActive()) return;
40084
40337
  await cacheFallbackMessages(list);
40338
+ if (!isActive()) return;
40085
40339
  const curMsgList = routeMessages(list);
40086
40340
  if (curMsgList.length > 0) {
40087
40341
  const cvs = currentConversation2.value;
@@ -40092,7 +40346,7 @@ const useMessage = (config = {}) => {
40092
40346
  }
40093
40347
  }
40094
40348
  });
40095
- imInstance.on(CbEvents.OnRecvC2CReadReceipt, async (list) => {
40349
+ onSessionEvent(CbEvents.OnRecvC2CReadReceipt, async (list) => {
40096
40350
  if ((list == null ? void 0 : list.length) > 0) {
40097
40351
  const cvs = currentConversation2.value;
40098
40352
  const curList = list.filter((item) => item.userID === (cvs == null ? void 0 : cvs.userID) && item.sessionType === IM_CVS_TYPE.Single);
@@ -40107,45 +40361,50 @@ const useMessage = (config = {}) => {
40107
40361
  setConversationMsgAsRead(cvs.conversationID);
40108
40362
  }
40109
40363
  });
40110
- imInstance.on(CbEvents.OnNewRecvMessageRevoked, (data) => {
40364
+ onSessionEvent(CbEvents.OnNewRecvMessageRevoked, (data) => {
40111
40365
  if (data == null ? void 0 : data.clientMsgID) {
40112
40366
  revokeMessage(data);
40113
40367
  }
40114
40368
  });
40115
- imInstance.on("updateStatus", (val = 1) => {
40369
+ onSessionEvent("updateStatus", (val = 1) => {
40116
40370
  console.log("登录状态", val);
40117
40371
  messageStore.setLoginStatus(val);
40118
40372
  if (val === IM_LOGIN_STATUS.Logged) {
40119
- setTimeout(() => {
40373
+ initTimer = setTimeout(() => {
40374
+ if (!isActive()) return;
40120
40375
  getUnReadMessageCount();
40121
40376
  }, 1e3);
40122
40377
  } else if (val === IM_LOGIN_STATUS.Logout) {
40123
40378
  mittBus.emit("IM_LOGOUT");
40124
40379
  }
40125
40380
  });
40126
- imInstance.on(CbEvents.OnSyncServerStart, () => {
40381
+ onSessionEvent(CbEvents.OnSyncServerStart, () => {
40127
40382
  console.log("IM 服务端同步开始");
40128
40383
  syncFinished = false;
40129
40384
  });
40130
- imInstance.on(CbEvents.OnSyncServerFinish, () => {
40385
+ onSessionEvent(CbEvents.OnSyncServerFinish, () => {
40131
40386
  console.log("IM 服务端同步完成");
40132
40387
  resolveSyncWaiters(true);
40133
40388
  mittBus.emit("IM_SYNC_SERVER_FINISH", true);
40134
40389
  });
40135
- imInstance.on(CbEvents.OnSyncServerFailed, (data) => {
40390
+ onSessionEvent(CbEvents.OnSyncServerFailed, (data) => {
40136
40391
  console.warn("IM 服务端同步失败:", data);
40137
40392
  resolveSyncWaiters(false);
40138
40393
  mittBus.emit("IM_SYNC_SERVER_FINISH", false);
40139
40394
  });
40140
- imInstance.on("im-error", async (err) => {
40395
+ onSessionEvent("im-error", async (err) => {
40396
+ if (initTimer) {
40397
+ clearTimeout(initTimer);
40398
+ initTimer = null;
40399
+ }
40141
40400
  });
40142
- imInstance.on(CbEvents.OnConversationChanged, (data) => {
40401
+ onSessionEvent(CbEvents.OnConversationChanged, (data) => {
40143
40402
  if (Array.isArray(data) && (data == null ? void 0 : data.length)) {
40144
40403
  console.log("会话状态改变:", data);
40145
40404
  handleConversationList(data, "update", handleMessage);
40146
40405
  }
40147
40406
  });
40148
- imInstance.on(CbEvents.OnNewConversation, async (data) => {
40407
+ onSessionEvent(CbEvents.OnNewConversation, async (data) => {
40149
40408
  console.log("新会话:", data);
40150
40409
  if (data == null ? void 0 : data.length) {
40151
40410
  const cvsIds = conversationList2.value.map((item) => item.conversationID);
@@ -40153,7 +40412,8 @@ const useMessage = (config = {}) => {
40153
40412
  const userIds = newList.filter((item) => item.conversationType === IM_CVS_TYPE.Single).map((item) => item.userID);
40154
40413
  let statusInfo = {};
40155
40414
  if (userIds.length > 0) {
40156
- const statusList = await (imInstance == null ? void 0 : imInstance.subscribeUsersStatus(userIds));
40415
+ const statusList = await (client == null ? void 0 : client.subscribeUsersStatus(userIds));
40416
+ if (!isActive()) return;
40157
40417
  statusInfo = updateUserStatus(statusList);
40158
40418
  }
40159
40419
  newList.forEach((item) => {
@@ -40169,48 +40429,48 @@ const useMessage = (config = {}) => {
40169
40429
  mittBus.emit("OnNewConversationCreated", data);
40170
40430
  }
40171
40431
  });
40172
- imInstance.on(CbEvents.OnGroupInfoChanged, (data) => {
40432
+ onSessionEvent(CbEvents.OnGroupInfoChanged, (data) => {
40173
40433
  if (data == null ? void 0 : data.groupID) {
40174
40434
  mittBus.emit("OnGroupInfoChanged", data);
40175
40435
  }
40176
40436
  });
40177
- imInstance.on(CbEvents.OnGroupMemberInfoChanged, (data) => {
40437
+ onSessionEvent(CbEvents.OnGroupMemberInfoChanged, (data) => {
40178
40438
  if (data == null ? void 0 : data.groupID) {
40179
40439
  updateMessageSenderInfo(data);
40180
40440
  mittBus.emit("OnGroupMemberInfoChanged", data);
40181
40441
  }
40182
40442
  });
40183
- imInstance.on(CbEvents.OnJoinedGroupAdded, (data) => {
40443
+ onSessionEvent(CbEvents.OnJoinedGroupAdded, (data) => {
40184
40444
  if (data == null ? void 0 : data.groupID) {
40185
40445
  mittBus.emit("OnJoinedGroupAdded", data.groupID);
40186
40446
  }
40187
40447
  });
40188
- imInstance.on(CbEvents.OnGroupMemberAdded, (data) => {
40448
+ onSessionEvent(CbEvents.OnGroupMemberAdded, (data) => {
40189
40449
  if (data == null ? void 0 : data.groupID) {
40190
40450
  mittBus.emit("OnGroupMemberAdded", data);
40191
40451
  }
40192
40452
  });
40193
- imInstance.on(CbEvents.OnGroupApplicationAdded, (data) => {
40453
+ onSessionEvent(CbEvents.OnGroupApplicationAdded, (data) => {
40194
40454
  console.log("收到入群申请通知:", data);
40195
40455
  mittBus.emit("OnGroupApplicationAdded", data);
40196
40456
  });
40197
- imInstance.on(CbEvents.OnGroupApplicationAccepted, (data) => {
40457
+ onSessionEvent(CbEvents.OnGroupApplicationAccepted, (data) => {
40198
40458
  console.log("入群申请被同意:", data);
40199
40459
  mittBus.emit("OnGroupApplicationAccepted", data);
40200
40460
  });
40201
- imInstance.on(CbEvents.OnGroupApplicationRejected, (data) => {
40461
+ onSessionEvent(CbEvents.OnGroupApplicationRejected, (data) => {
40202
40462
  console.log("入群申请被拒绝:", data);
40203
40463
  mittBus.emit("OnGroupApplicationRejected", data);
40204
40464
  });
40205
- imInstance.on(CbEvents.OnJoinedGroupDeleted, (data) => {
40465
+ onSessionEvent(CbEvents.OnJoinedGroupDeleted, (data) => {
40206
40466
  if (data.groupID) {
40207
40467
  mittBus.emit("OnJoinedGroupDeleted", data.groupID);
40208
40468
  }
40209
40469
  });
40210
- imInstance.on(CbEvents.OnGroupMemberDeleted, (data) => {
40470
+ onSessionEvent(CbEvents.OnGroupMemberDeleted, (data) => {
40211
40471
  mittBus.emit("OnGroupMemberDeleted", data);
40212
40472
  });
40213
- imInstance.on(CbEvents.OnGroupDismissed, (data) => {
40473
+ onSessionEvent(CbEvents.OnGroupDismissed, (data) => {
40214
40474
  if (data && data.groupID) {
40215
40475
  data.status = IM_GROUP_STATUS.Dismissed;
40216
40476
  mittBus.emit("OnGroupInfoChanged", data);
@@ -40245,6 +40505,24 @@ const useMessage = (config = {}) => {
40245
40505
  imInstance.off(CbEvents.OnGroupApplicationAccepted);
40246
40506
  imInstance.off(CbEvents.OnGroupApplicationRejected);
40247
40507
  };
40508
+ if (!subscribedStores.has(messageStore)) {
40509
+ subscribedStores.add(messageStore);
40510
+ let previousScope = imSessionScope(messageStore);
40511
+ messageStore.$subscribe((_mutation, state) => {
40512
+ let nextScope = "";
40513
+ try {
40514
+ nextScope = imSessionScope(state);
40515
+ } catch {
40516
+ }
40517
+ if (previousScope === nextScope) return;
40518
+ previousScope = nextScope;
40519
+ clearConversationList();
40520
+ clearMessageList();
40521
+ resetUsers();
40522
+ cancelAllBurns();
40523
+ void clearInstance().catch((error) => console.error("[IM] 会话切换清理失败:", error));
40524
+ }, { detached: true, flush: "sync" });
40525
+ }
40248
40526
  return {
40249
40527
  messageStore,
40250
40528
  loginStatus,
@@ -40301,7 +40579,7 @@ const _sfc_main = {
40301
40579
  {
40302
40580
  path: "/messages",
40303
40581
  name: "Messages",
40304
- component: () => import("./indexH5-CUQe-Ize.js"),
40582
+ component: () => import("./indexH5-sKvS7t3k.js"),
40305
40583
  meta: { title: "消息" }
40306
40584
  },
40307
40585
  // 根路径重定向到会话列表页
@@ -40312,61 +40590,61 @@ const _sfc_main = {
40312
40590
  {
40313
40591
  path: "/chat",
40314
40592
  name: "Chat",
40315
- component: () => import("./ChatPageH5-B3azJu7O.js"),
40593
+ component: () => import("./ChatPageH5-FXZxtcQr.js"),
40316
40594
  meta: { title: "聊天" }
40317
40595
  },
40318
40596
  {
40319
40597
  path: "/search",
40320
40598
  name: "Search",
40321
- component: () => import("./SearchPageH5-hYxNItvz.js"),
40599
+ component: () => import("./SearchPageH5-B6B_U4hW.js"),
40322
40600
  meta: { title: "搜索" }
40323
40601
  },
40324
40602
  {
40325
40603
  path: "/notice",
40326
40604
  name: "Notice",
40327
- component: () => import("./GroupNoticeH5-46ZjBe92.js"),
40605
+ component: () => import("./GroupNoticeH5-KA-L2h9r.js"),
40328
40606
  meta: { title: "群通知" }
40329
40607
  },
40330
40608
  {
40331
40609
  path: "/create-group",
40332
40610
  name: "CreateGroup",
40333
- component: () => import("./CreateGroupH5-a16jkObH.js"),
40611
+ component: () => import("./CreateGroupH5-CO_ZH36_.js"),
40334
40612
  meta: { title: "创建群组" }
40335
40613
  },
40336
40614
  {
40337
40615
  path: "/select-members",
40338
40616
  name: "SelectMembers",
40339
- component: () => import("./SelectMembersH5-DQIfX_w4.js"),
40617
+ component: () => import("./SelectMembersH5-CrPoBnYp.js"),
40340
40618
  meta: { title: "成员选择" }
40341
40619
  },
40342
40620
  {
40343
40621
  path: "/member-detail",
40344
40622
  name: "MemberDetail",
40345
- component: () => import("./MemberDetailH5-B7nlr8ZT.js"),
40623
+ component: () => import("./MemberDetailH5-BfAE0nx-.js"),
40346
40624
  meta: { title: "个人详情" }
40347
40625
  },
40348
40626
  {
40349
40627
  path: "/chat-settings",
40350
40628
  name: "ChatSettings",
40351
- component: () => import("./ChatSettingsH5-CFr9Bn69.js"),
40629
+ component: () => import("./ChatSettingsH5-PackrlPt.js"),
40352
40630
  meta: { title: "聊天设置" }
40353
40631
  },
40354
40632
  {
40355
40633
  path: "/group-announcement",
40356
40634
  name: "GroupAnnouncement",
40357
- component: () => import("./GroupAnnouncementH5-B-Qfzoy2.js"),
40635
+ component: () => import("./GroupAnnouncementH5-yUDR5kYW.js"),
40358
40636
  meta: { title: "群公告" }
40359
40637
  },
40360
40638
  {
40361
40639
  path: "/chat-history",
40362
40640
  name: "ChatHistory",
40363
- component: () => import("./ChatHistoryH5-BhrGF5WR.js"),
40641
+ component: () => import("./ChatHistoryH5-CKDqKPl-.js"),
40364
40642
  meta: { title: "聊天记录" }
40365
40643
  },
40366
40644
  {
40367
40645
  path: "/group-members",
40368
40646
  name: "GroupMembers",
40369
- component: () => import("./GroupMembersH5-D_f1RFjO.js"),
40647
+ component: () => import("./GroupMembersH5-BVHUF2mG.js"),
40370
40648
  meta: { title: "群成员" }
40371
40649
  }
40372
40650
  ];
@@ -40426,6 +40704,11 @@ const _sfc_main = {
40426
40704
  }
40427
40705
  chatMessage.setUserInfo({
40428
40706
  userID: obj.loginCode || props.baseConfig.loginCode,
40707
+ imIdentityVersion: obj.imIdentityVersion || 1,
40708
+ imUserID: obj.imUserID || "",
40709
+ tenantID: obj.tenantID || "",
40710
+ businessUserID: obj.businessUserID || "",
40711
+ loginCode: obj.loginCode || "",
40429
40712
  nickname: obj.userName || props.baseConfig.userName,
40430
40713
  apiUrl: obj.apiUrl || props.baseConfig.apiUrl,
40431
40714
  wsUrl: ((_a = obj.apiUrl) == null ? void 0 : _a.replace(/^http/, "ws")) || ((_b = props.baseConfig.apiUrl) == null ? void 0 : _b.replace(/^http/, "ws")),
@@ -40460,6 +40743,7 @@ const _sfc_main = {
40460
40743
  }
40461
40744
  };
40462
40745
  export {
40746
+ _sfc_main as $,
40463
40747
  useBurnAfterReading as A,
40464
40748
  dedupeMessages as B,
40465
40749
  getDefaultExportFromCjs as C,
@@ -40484,8 +40768,9 @@ export {
40484
40768
  clampBurnDuration as V,
40485
40769
  buildBurnEx as W,
40486
40770
  downloadFileByBlob as X,
40487
- EXIT_CODE as Y,
40488
- _sfc_main as _,
40771
+ imSessionScope as Y,
40772
+ IMSessionChangedError as Z,
40773
+ requestInstance as _,
40489
40774
  getAvatarBg as a,
40490
40775
  getAvatarText as b,
40491
40776
  cloneDeep as c,