@typexim/web-sdk 0.0.4-alpha.1 → 0.0.4-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,5 +1,41 @@
1
1
  # TypeX WEB SDK
2
2
 
3
+ ## 调用方式
4
+
5
+ ```javascript
6
+ import { create, enums, events } from '@typexim/web-sdk';
7
+
8
+ const chat = create('sdk', {
9
+ httpHost: 'https://coco.typex.im',
10
+ websocketHost: 'wss://w.typexim.com'
11
+ })
12
+
13
+ chat.setLogLevel(enums.LogLevel.info);
14
+
15
+ chat.login({
16
+ user_code: 'xxxx'
17
+ });
18
+ chat.once(
19
+ events.CHAT_SDK_EVENT.SDK_READY,
20
+ async () => {
21
+ chat.on(
22
+ events.CHAT_SDK_EVENT.FEED_LIST_UPDATED
23
+ ({ is_sync_completed, list }) => {
24
+ console.log('feed列表', list);
25
+ }
26
+ );
27
+
28
+ const result = await chat.getFeedList();
29
+ if (result.ok) {
30
+ console.log('feed列表', result.data.list);
31
+ } else {
32
+ console.log(result.code);
33
+ console.log(result.msg);
34
+ }
35
+ }
36
+ );
37
+ ```
38
+
3
39
  ## API清单
4
40
 
5
41
  ### 事件
@@ -58,3 +94,7 @@
58
94
 
59
95
  1. getRoomRecentMessageList(最近50条)
60
96
  2. sendMessageToRoom
97
+
98
+ ## 注意事项
99
+
100
+ 1. 同个浏览器只有最先调用login成功的窗口可以正常使用,其他窗口要使用就得先在已登录的窗口调用logout或者关闭窗口
@@ -2,14 +2,6 @@ export declare enum Boolean {
2
2
  yes = 1,
3
3
  no = 0
4
4
  }
5
- export declare enum ApiResponseCode {
6
- SDK_ERROR = -10000,
7
- NETWORK_ERROR = -9999,
8
- MAKE_SIGN_FAIL = -9998,
9
- REQUEST_SINGLETON_LOCK_FAIL = -9997,
10
- PARAMS_INVALID = -9996,
11
- SUCCESS = 0
12
- }
13
5
  export declare enum PushMessageType {
14
6
  CREATE_FEED = 0,
15
7
  UPDATE_FEED = 1,
@@ -3,3 +3,11 @@ export declare enum LogLevel {
3
3
  warning = "WARNING",
4
4
  info = "INFO"
5
5
  }
6
+ export declare enum HttpClientResponseCode {
7
+ SDK_ERROR = -10000,
8
+ NETWORK_ERROR = -9999,
9
+ MAKE_SIGN_FAIL = -9998,
10
+ REQUEST_SINGLETON_LOCK_FAIL = -9997,
11
+ PARAMS_INVALID = -9996,
12
+ SUCCESS = 0
13
+ }
@@ -3,7 +3,13 @@ import * as events from './events';
3
3
  import * as interfaces from './interfaces';
4
4
  import { ChatSDKConfig } from './interfaces/sdk';
5
5
  import ChatSDK from './modules/ChatSDK';
6
- export declare function create(config: ChatSDKConfig): ChatSDK;
6
+ /**
7
+ * 创建一个ChatSDK实例,对于同一个instanceId只会返回同一份实例
8
+ * @param instanceId ChatSDK实例唯一id
9
+ * @param config ChatSDK配置
10
+ * @returns
11
+ */
12
+ export declare function create(instanceId: string, config: ChatSDKConfig): ChatSDK;
7
13
  export declare const utils: {
8
14
  parseSystemMessageContent: (str: string) => {
9
15
  text?: string;
@@ -12,31 +12,42 @@ export type ChatSDKConfig = {
12
12
  */
13
13
  developer?: string;
14
14
  };
15
- type ChatSDKContextBase = {
15
+ export type ChatSDKContext = {
16
16
  isReady: boolean;
17
17
  deviceId: string | null;
18
- };
19
- type ChatSDKContextLogin = ChatSDKContextBase & {
20
- isLogin: true;
18
+ isLogin: boolean;
21
19
  loginUser: {
22
20
  userId: string;
23
21
  secretKey: string;
24
22
  sessionId: string;
25
- };
26
- };
27
- type ChatSDKContextNotLogin = ChatSDKContextBase & {
28
- isLogin: false;
23
+ } | null;
29
24
  };
30
- export type ChatSDKContext = ChatSDKContextLogin | ChatSDKContextNotLogin;
31
25
  export type HttpClientResponseOk<T = unknown> = {
26
+ /**
27
+ * 调用成功
28
+ */
32
29
  ok: true;
30
+ /**
31
+ * 响应数据
32
+ */
33
33
  data: T;
34
34
  };
35
35
  export type HttpClientResponseFail = {
36
+ /**
37
+ * 调用失败
38
+ */
36
39
  ok: false;
40
+ /**
41
+ * 调用失败code
42
+ */
37
43
  code: number;
44
+ /**
45
+ * 调用失败说明
46
+ */
38
47
  msg: string;
48
+ /**
49
+ * 调用失败说明(国际化)
50
+ */
39
51
  msg_i18n?: Record<string, string>;
40
52
  };
41
53
  export type HttpClientResponse<T = unknown> = HttpClientResponseOk<T> | HttpClientResponseFail;
42
- export {};
@@ -30,22 +30,20 @@ export default class ChatSDK extends Base<CHAT_SDK_EVENT_MAP> {
30
30
  */
31
31
  setLogLevel(level: LogLevel): void;
32
32
  /**
33
- * 使用一次性 userCode 登录,建立会话并连接 WebSocket。
34
- * 成功后会触发事件:SDK_READY。
35
- * @param {SDKLoginReq} req 登录请求参数(包含 userCode)
36
- * @returns {Promise<HttpClientResponse>} 成功返回 ok,失败返回错误信息
33
+ * 使用user_code登录,建立会话并连接WebSocket,成功后会触发事件:SDK_READY
34
+ * @param req
35
+ * @returns
37
36
  */
38
37
  login(req: SDKLoginReq): Promise<HttpClientResponse>;
39
38
  /**
40
- * 退出当前登录用户并断开 WebSocket。
41
- * 成功后会触发事件:SDK_NOT_READY。
42
- * @returns {Promise<HttpClientResponse>} 成功返回 ok,失败返回错误信息
39
+ * 退出当前登录用户并断开 WebSocket,成功后会触发事件:SDK_NOT_READY
40
+ * @returns
43
41
  */
44
42
  logout(): Promise<HttpClientResponse>;
45
- private handleKickoff;
43
+ private afterLogout;
46
44
  /**
47
- * 获取当前登录用户的 userId。
48
- * @returns {string | null} 已登录返回 userId,未登录返回 null
45
+ * 获取当前登录用户的user_id
46
+ * @returns
49
47
  */
50
48
  getLoginUser(): string | null;
51
49
  /**
@@ -1,6 +1,7 @@
1
1
  export default class SingletonLock {
2
- private destroyed;
3
- private promise;
2
+ private obtained;
3
+ private abortLockObtain;
4
4
  constructor();
5
- request(): Promise<boolean>;
5
+ requestLock(): Promise<boolean>;
6
+ releaseLock(): void;
6
7
  }
@@ -1,4 +1,4 @@
1
- import { ApiResponseCode } from '../enums';
1
+ import { HttpClientResponseCode } from '../enums';
2
2
  import { HttpClientResponseFail, HttpClientResponseOk } from '../interfaces/sdk';
3
- export declare function createErrorResponse(code: ApiResponseCode, msg: string): HttpClientResponseFail;
3
+ export declare function createErrorResponse(code: Exclude<HttpClientResponseCode, HttpClientResponseCode.SUCCESS>, msg: string): HttpClientResponseFail;
4
4
  export declare function createOkResponse<T = unknown>(data: T): HttpClientResponseOk<T>;
@@ -1 +1 @@
1
- import e from"js-cookie";import t from"axios";import s from"crypto-js";var i,n,o,r,a,c,h,d,l,_,E;!function(e){e[e.yes=1]="yes",e[e.no=0]="no"}(i||(i={})),function(e){e[e.SDK_ERROR=-1e4]="SDK_ERROR",e[e.NETWORK_ERROR=-9999]="NETWORK_ERROR",e[e.MAKE_SIGN_FAIL=-9998]="MAKE_SIGN_FAIL",e[e.REQUEST_SINGLETON_LOCK_FAIL=-9997]="REQUEST_SINGLETON_LOCK_FAIL",e[e.PARAMS_INVALID=-9996]="PARAMS_INVALID",e[e.SUCCESS=0]="SUCCESS"}(n||(n={})),function(e){e[e.CREATE_FEED=0]="CREATE_FEED",e[e.UPDATE_FEED=1]="UPDATE_FEED",e[e.ADD_MESSAGE=2]="ADD_MESSAGE",e[e.UPDATE_MESSAGE=3]="UPDATE_MESSAGE",e[e.UPDATE_USER_DATA=4]="UPDATE_USER_DATA",e[e.USER_LOGIN=5]="USER_LOGIN",e[e.READ_MESSAGE=6]="READ_MESSAGE",e[e.PONG_MESSAGE=8]="PONG_MESSAGE",e[e.DELETE_MESSAGE=9]="DELETE_MESSAGE",e[e.UPDATE_GROUP=12]="UPDATE_GROUP",e[e.DELETE_GROUP=13]="DELETE_GROUP",e[e.UPDATE_LOGIN_USER=17]="UPDATE_LOGIN_USER",e[e.UPDATE_MESSAGE_REACTION=19]="UPDATE_MESSAGE_REACTION",e[e.ADD_GROUP_MEMBER=21]="ADD_GROUP_MEMBER",e[e.DEL_GROUP_MEMBER=22]="DEL_GROUP_MEMBER",e[e.ADD_CONTACTS=23]="ADD_CONTACTS",e[e.UPDATE_CONTACTS=24]="UPDATE_CONTACTS",e[e.DEL_CONTACTS=25]="DEL_CONTACTS",e[e.BLOCK_USER=26]="BLOCK_USER",e[e.DEVICE_CODE=27]="DEVICE_CODE",e[e.MSG_OPEN_CLOCK=28]="MSG_OPEN_CLOCK",e[e.DELETE_SECRET_CHAT=29]="DELETE_SECRET_CHAT",e[e.DELETE_FEED=30]="DELETE_FEED",e[e.FAVORITE_UPDATE=31]="FAVORITE_UPDATE",e[e.STICKERS_UPDATE=32]="STICKERS_UPDATE",e[e.CUSTOM_STICKERS_UPDATE=33]="CUSTOM_STICKERS_UPDATE",e[e.MANAGE_DEVICE_UPDATE=34]="MANAGE_DEVICE_UPDATE",e[e.MASS_MESSAGE_UPDATE=35]="MASS_MESSAGE_UPDATE",e[e.CHAT_WALLPAPER_DELETE=36]="CHAT_WALLPAPER_DELETE",e[e.CHAT_WALLPAPER_ADD=37]="CHAT_WALLPAPER_ADD",e[e.CONTACT_REQUEST=40]="CONTACT_REQUEST",e[e.USER_PREFERENCE_CHANGE=38]="USER_PREFERENCE_CHANGE",e[e.UPDATE_GROUP_PIN=41]="UPDATE_GROUP_PIN",e[e.RTC_CANCEL=46]="RTC_CANCEL",e[e.ADD_FEED_FOLDER=43]="ADD_FEED_FOLDER",e[e.UPDATE_FEED_FOLDER=44]="UPDATE_FEED_FOLDER",e[e.UPDATE_FOLDER_FEED_LIST=47]="UPDATE_FOLDER_FEED_LIST",e[e.DELETE_FEED_FOLDER=45]="DELETE_FEED_FOLDER",e[e.UPDATE_CHATTER=42]="UPDATE_CHATTER",e[e.PAPER_KEY=48]="PAPER_KEY",e[e.PAPER_KEY_LOG=49]="PAPER_KEY_LOG",e[e.USER_LOGIN_SCAN=50]="USER_LOGIN_SCAN",e[e.RTC_RINGING=51]="RTC_RINGING",e[e.ON_UPDATE_PRIVATE_SETTING=52]="ON_UPDATE_PRIVATE_SETTING",e[e.ON_DELETE_PRIVATE_SETTING=53]="ON_DELETE_PRIVATE_SETTING",e[e.ON_UPDATE_LOGIN_PRIVATE_SETTING=54]="ON_UPDATE_LOGIN_PRIVATE_SETTING",e[e.PUSH_LIVE_MESSAGE=56]="PUSH_LIVE_MESSAGE",e[e.BIG_PACKAGE_PUSH=126]="BIG_PACKAGE_PUSH",e[e.KICK_OUT_DEVICE=127]="KICK_OUT_DEVICE",e[e.CLEAR_HISTORY=128]="CLEAR_HISTORY",e[e.CLEAR_DATA=255]="CLEAR_DATA"}(o||(o={})),function(e){e[e.singleChat=1]="singleChat",e[e.groupChat=2]="groupChat"}(r||(r={})),function(e){e[e.text=0]="text",e[e.system=1]="system"}(a||(a={})),function(e){e[e.text=0]="text",e[e.image=1]="image",e[e.card=2]="card",e[e.audio=3]="audio",e[e.video=4]="video",e[e.file=5]="file",e[e.mergeForward=6]="mergeForward",e[e.sticker=7]="sticker",e[e.richText=8]="richText",e[e.custom=9]="custom"}(c||(c={})),function(e){e[e.success=0]="success",e[e.sending=1]="sending",e[e.failed=2]="failed"}(h||(h={})),function(e){e[e.Owner=1]="Owner",e[e.Admin=2]="Admin",e[e.Common=3]="Common",e[e.Bot=4]="Bot"}(d||(d={})),function(e){e[e.normal=0]="normal",e[e.recall=1]="recall"}(l||(l={})),function(e){e[e.private=1]="private",e[e.public=0]="public",e[e.anonymous=2]="anonymous"}(_||(_={})),function(e){e.error="ERROR",e.warning="WARNING",e.info="INFO"}(E||(E={}));var g=Object.freeze({__proto__:null,get ApiResponseCode(){return n},get Boolean(){return i},get ChatType(){return r},get GroupRole(){return d},get GroupType(){return _},get LogLevel(){return E},get MessageKind(){return a},get MessageOperation(){return l},get MessageSendStatus(){return h},get MessageType(){return c},get PushMessageType(){return o}});const u={SDK_READY:"sdkReady",SDK_NOT_READY:"sdkNotReady",FEED_LIST_UPDATED:"feedListUpdated",MESSAGE_RECEIVED:"messageReceived",MESSAGE_UPDATED:"messageUpdated",MESSAGE_RECEIPT_RECEIVED:"messageReceiptReceived",ROOM_MESSAGE_RECEIVED:"roomMessageReceived"};var p=Object.freeze({__proto__:null,CHAT_SDK_EVENT:u}),m=Object.freeze({__proto__:null});function S(e,t){return{ok:!1,code:e,msg:t}}function f(e){return{ok:!0,data:e}}const T=e=>{try{return null==e||"string"!=typeof e||0===e.length?null:JSON.parse(e)}catch{return null}},A=e=>{const t=T(e);if(null!==t){let e;return null!=t.content&&(e=T(t.content)??void 0),{client_msg_id:t.client_msg_id,content:e}}return{}};class C{all;constructor(e){this.all=e||new Map}on(e,t){const s=this.all.get(e);return s?s.push(t):this.all.set(e,[t]),()=>{this.off(e,t)}}off(e,t){const s=this.all.get(e);s&&(t?s.splice(s.indexOf(t)>>>0,1):this.all.set(e,[]))}emit(e,t){let s=this.all.get(e);s&&s.slice().map(e=>{e(t)}),s=this.all.get("*"),s&&s.slice().map(s=>{s(e,t)})}once(e,t){if("*"===e){const s=(e,i)=>{t(e,i),this.off("*",s)};this.on(e,s)}else{const s=i=>{t(i),this.off(e,s)};this.on(e,s)}}}class R extends C{url;logger;ws;destroyed;pingTimeout;pongTimeout;reConnectTimeout;constructor(e){super(),this.url=e.url,this.logger=e.logger,this.ws=null,this.destroyed=!1,this.pingTimeout=null,this.pongTimeout=null,this.reConnectTimeout=null,this.reConnect=this.reConnect.bind(this),window.addEventListener("online",this.reConnect),this.connect()}disconnect(){this.ws&&(this.ws.onopen=null,this.ws.onerror=null,this.ws.onmessage=null,this.ws.onclose=null,this.ws.close(),this.ws=null),null!==this.pingTimeout&&(clearTimeout(this.pingTimeout),this.pingTimeout=null),null!==this.pongTimeout&&(clearTimeout(this.pongTimeout),this.pongTimeout=null),null!==this.reConnectTimeout&&(clearTimeout(this.reConnectTimeout),this.reConnectTimeout=null)}connect(){if(this.ws)return void this.logger.warn("ReConnectWebsocket try connect but already connected");const e=new WebSocket(this.url);this.ws=e,e.onopen=()=>{this.delaySendPing()},e.onerror=e=>{this.logger.info("ReConnectWebsocket error",e)},e.onmessage=e=>{try{const t=JSON.parse(e.data);if(t.type===o.PONG_MESSAGE?null!==this.pongTimeout&&(clearTimeout(this.pongTimeout),this.pongTimeout=null,this.delaySendPing()):this.sendAck(t.id),t.type===o.BIG_PACKAGE_PUSH)for(const e of t.content.big_push)this.emit("message",e);else this.emit("message",t)}catch(e){this.logger.error("ReConnectWebsocket message parse failed",e)}},e.onclose=e=>{4001===e.code?this.kickOff():this.reConnect()}}reConnect(){this.disconnect(),this.destroyed||(this.reConnectTimeout=setTimeout(()=>{this.reConnectTimeout=null,this.connect()},5e3))}send(e){if(this.destroyed)return!1;if(!this.ws)return this.logger.warn("ReConnectWebsocket send before connect"),!1;try{return this.ws.readyState!==this.ws.OPEN?(this.logger.warn("ReConnectWebsocket send but not open",this.ws.readyState),!1):(this.ws.send(JSON.stringify(e)),!0)}catch(e){return this.logger.warn("ReConnectWebsocket send error",e),!1}}sendAck(e){this.send({type:1,ids:[e]})}destroy(){this.destroyed||(this.destroyed=!0,this.disconnect(),window.removeEventListener("online",this.reConnect))}delaySendPing(){null!==this.pingTimeout&&(clearTimeout(this.pingTimeout),this.pingTimeout=null),this.pingTimeout=setTimeout(()=>{this.pingTimeout=null;this.send({type:0})?this.waitPong():this.reConnect()},2e4)}waitPong(){this.pongTimeout=setTimeout(()=>{this.logger.info("ReConnectWebsocket pong timeout"),this.reConnect()},1e4)}kickOff(){this.destroy(),this.emit("kickoff")}}class D extends C{host;context;logger;ws;constructor(e){super(),this.host=e.host,this.context=e.context,this.logger=e.logger,this.ws=null}connect(){if(!this.context.isLogin)return void this.logger.warn("BusinessWebsocket try connect but not login");if(this.ws)return void this.logger.warn("BusinessWebsocket try connect but already connected");const e=`${this.host}/ws?token=${this.context.loginUser.sessionId}&platform=web`;this.ws=new R({url:e,logger:this.logger}),this.ws.on("message",e=>{this.emit("message",e)}),this.ws.on("kickoff",()=>{this.emit("kickoff")})}disconnect(){this.ws?(this.ws.destroy(),this.ws=null):this.logger.info("BusinessWebsocket already disconnected")}}class y{_isSyncCompleted;map;chatMap;list;waitMap;constructor(){this._isSyncCompleted=!1,this.map=new Map,this.chatMap=new Map,this.waitMap=new Map,this.list=[]}getAll(){const e=[];for(const t of this.list){const s=this.map.get(t);s&&e.push(s)}return e}get(e){return this.map.get(e)??null}getByChatId(e){const t=this.chatMap.get(e);return void 0!==t?this.get(t):null}async waitFor(e){const t=this.get(e);if(t)return t;if(this._isSyncCompleted)return null;let s=this.waitMap.get(e);return s||(s=(()=>{let e=()=>{};return{promise:new Promise(t=>{e=t}),resolve:e}})(),this.waitMap.set(e,s)),await s.promise,this.get(e)}resolveWait(e){const t=this.waitMap.get(e);t&&(t.resolve(!0),this.waitMap.delete(e))}remove(e){const t=this.map.get(e);return!!t&&(this.map.delete(e),this.chatMap.delete(t.chat_id),this.list.splice(this.list.indexOf(e),1),!0)}push(e){this.remove(e.id),this.map.set(e.id,e),this.list.push(e.id),this.chatMap.set(e.chat_id,e.id),this.resolveWait(e.id)}unshift(e){this.remove(e.id),this.map.set(e.id,e),this.list.unshift(e.id),this.chatMap.set(e.chat_id,e.id),this.resolveWait(e.id)}update(e){this.map.has(e.id)&&this.map.set(e.id,e)}markSyncComplete(){this._isSyncCompleted=!0;for(const[e,t]of this.waitMap)t.resolve(!0);this.waitMap.clear()}isSyncCompleted(){return this._isSyncCompleted}reset(){this._isSyncCompleted=!1,this.map.clear(),this.chatMap.clear(),this.waitMap.clear(),this.list=[]}}function w(e){return(new TextEncoder).encode(e).buffer}async function L(e,t){return window.crypto&&window.crypto.subtle?await async function(e,t){const s=w(t),i=w(e),n=await crypto.subtle.importKey("raw",s,{name:"HMAC",hash:{name:"SHA-256"}},!1,["sign"]),o=await crypto.subtle.sign("HMAC",n,i);return Array.from(new Uint8Array(o)).map(e=>e.toString(16).padStart(2,"0")).join("")}(e,t):function(e,t){return s.HmacSHA256(e,t).toString(s.enc.Hex)}(e,t)}const P=e=>{if(null==e)return"";if("object"!=typeof e||Array.isArray(e)){if(Array.isArray(e)){let t="";for(const s of e)t+=P(s);return t}return"string"==typeof e?e:String(e)}{const t=Object.keys(e).sort();let s="";for(const i of t)s+=P(e[i]);return s}};function O(){return S(n.MAKE_SIGN_FAIL,"make sign fail")}class I{api;context;logger;constructor(e){this.api=t.create({baseURL:e.host,headers:{"x-developer":e.developer}}),this.context=e.context,this.logger=e.logger}async request(e){try{const{needSign:t=!0,...s}=e;if(s.headers||(s.headers={}),s.headers.platform="web",t&&"POST"===e.method?.toUpperCase()&&s.data){if(!this.context.isLogin)return this.logger.warn("HttpClient needs make sign but not login"),O();try{const e=await(async(e,t)=>{const s=await P(e);return await L(s,t)})(s.data,this.context.loginUser.secretKey);s.headers.sign=e}catch(e){return this.logger.error("HttpClient make sign error",e),O()}}const i=await this.api(s),o=i.data;return o.code===n.SUCCESS?{ok:!0,data:o.data}:(this.logger.info("HttpClient server error",i),{ok:!1,code:o.code,msg:o.msg,msg_i18n:o.msg_i18n})}catch{return{ok:!1,code:n.NETWORK_ERROR,msg:"network error"}}}get(e,t,s){return this.request({url:e,method:"GET",params:t,...s})}post(e,t,s){return this.request({url:e,method:"POST",data:t,...s})}}class U{level;logHandler;constructor(){this.level=E.error,this.logHandler=this.createLogHandler()}getPriority(e){switch(e){case E.error:return 3;case E.warning:return 2;case E.info:return 1;default:return 0}}createLogHandler(){return(e,t,...s)=>{const i=`[tlog] ${(new Date).toISOString().split("T")[0]}`,n=t?`${i} ${t}`:i;switch(e){case E.error:console.error(n,...s);break;case E.warning:console.warn(n,...s);break;case E.info:console.info(n,...s);break;default:console.log(n,...s)}}}log(e,t,...s){this.getPriority(e)>=this.getPriority(this.level)&&this.logHandler(e,t,...s)}setLogLevel(e){this.level=e,this.logHandler=this.createLogHandler()}info(e,...t){this.log(E.info,e,...t)}warn(e,...t){this.log(E.warning,e,...t)}error(e,...t){this.log(E.error,e,...t)}}class M{context;logger;emitter;feedCache;handleKickoff;constructor(e){this.context=e.context,this.logger=e.logger,this.emitter=e.emitter,this.feedCache=e.feedCache,this.handleKickoff=e.onKickoff}emitFeedListUpdated(){this.emitter.emit(u.FEED_LIST_UPDATED,{list:this.feedCache.getAll(),is_sync_completed:this.feedCache.isSyncCompleted()})}processCreateFeed(e){this.feedCache.unshift(e.content),this.emitFeedListUpdated()}processDeleteFeed(e){this.feedCache.remove(e.content.id)&&this.emitFeedListUpdated()}processAddMessage(e){if(!this.context.isLogin)return;const t=this.context.loginUser.userId,s=e.content,i=this.feedCache.getByChatId(s.chat_id);if(null!==i){let e=!1;const n={};if(s.message_kind_id!==a.system&&s.sender_id!==t){e=!0;const t=new Set(i.unread_message_ids);t.add(s.id),n.unread_message_ids=[...t],n.badge=t.size}i.last_message_position<s.position&&(e=!0,n.last_message_position=s.position,n.last_message_send_at=s.send_at,n.last_message=s.content,n.last_message_kind_id=s.message_kind_id,n.last_message_type_id=s.message_type_id),e&&(this.feedCache.unshift({...i,...n}),this.emitFeedListUpdated()),this.emitter.emit(u.MESSAGE_RECEIVED,s)}}processUpdateMessage(e){this.emitter.emit(u.MESSAGE_UPDATED,e.content)}processPushLiveMessage(e){this.emitter.emit(u.ROOM_MESSAGE_RECEIVED,e.content)}processReadMessage(e){if(!this.context.isLogin)return;const t=this.context.loginUser.userId,s=this.feedCache.getByChatId(e.content.chat_id);if(null!==s){let i=!1;const n={};if(e.content.reader_ids.includes(t)&&e.content.message_ids.length>0){i=!0;const t=new Set(s.unread_message_ids);e.content.message_ids.forEach(e=>{t.delete(e)}),n.unread_message_ids=[...t],n.badge=t.size}i&&(this.feedCache.update({...s,...n}),this.emitFeedListUpdated()),this.emitter.emit(u.MESSAGE_RECEIPT_RECEIVED,{feed_id:s.id,message_ids:e.content.message_ids,reader_user_ids:e.content.reader_ids})}}processKickOutDevice(){this.handleKickoff()}process(e){switch(this.logger.info("PushMessageProcessor process",e),e.type){case o.CREATE_FEED:this.processCreateFeed(e);break;case o.DELETE_FEED:this.processDeleteFeed(e);break;case o.ADD_MESSAGE:this.processAddMessage(e);break;case o.UPDATE_MESSAGE:this.processUpdateMessage(e);break;case o.PUSH_LIVE_MESSAGE:this.processPushLiveMessage(e);break;case o.READ_MESSAGE:this.processReadMessage(e);break;case o.KICK_OUT_DEVICE:this.processKickOutDevice()}}}var v=e=>new Promise(t=>setTimeout(t,e));class k{destroyed;promise;constructor(){this.destroyed=!1;let e=()=>{};const t=new Promise(t=>{e=t});this.promise=t,navigator.locks.request("typex-sdk",{ifAvailable:!0},async t=>{if(t)for(e(!0);!this.destroyed;)await v(1e4);else e(!1)})}async request(){return await this.promise}}class G{static key="__typex_sdk_data__";static set(e){localStorage.setItem(this.key,JSON.stringify(e))}static get(){try{const e=localStorage.getItem(this.key);if(!e)return null;return JSON.parse(e)}catch{return null}}static clear(){localStorage.removeItem(this.key)}}class N{version;constructor(){this.version=0}increment(){this.version++}get(){return this.version}check(e){return e===this.version}}class x extends C{context;version;singletonLock;logger;httpClient;feedCache;ws;pushMessageProcessor;constructor(e){super(),this.context={isLogin:!1,isReady:!1,deviceId:null},this.version=new N,this.singletonLock=new k,this.logger=new U,this.httpClient=new I({host:e.httpHost,context:this.context,logger:this.logger,developer:e.developer}),this.feedCache=new y,this.ws=new D({host:e.websocketHost,context:this.context,logger:this.logger}),this.pushMessageProcessor=new M({context:this.context,logger:this.logger,emitter:this,feedCache:this.feedCache,onKickoff:()=>{this.handleKickoff()}}),this.init()}init(){this.loadContext(),this.ws.on("message",e=>{this.pushMessageProcessor.process(e)}),this.ws.on("kickoff",()=>{this.handleKickoff()})}async requestDeviceId(){if(this.context.deviceId)return!0;const e=await this.httpClient.post("/device_api/device_id",{platform:"web",hash_str:""},{needSign:!1});return!!e.ok&&(this.context.deviceId=e.data,this.saveContext(),!0)}saveContext(){const{context:e}=this;G.set({deviceId:e.deviceId})}loadContext(){const e=G.get();e&&e.deviceId&&(this.context.deviceId=e.deviceId)}applyLoginStatus(){this.version.increment(),this.saveContext(),this.context.isLogin?(this.loadFeedList(),this.ws.connect(),this.context.isReady=!0,this.emit(u.SDK_READY)):(this.ws.disconnect(),this.feedCache.reset(),this.context.isReady=!1,this.emit(u.SDK_NOT_READY))}async loadFeedList(){const e=this.version.get(),t={refresh_at:null,feed_id:null};for(;;){const s=await this.httpClient.get("/feed",{limit:500,last_refresh_at:t.refresh_at??void 0,last_feed_id:t.feed_id??void 0});if(!this.version.check(e))break;if(!s.ok)break;if(t.refresh_at=s.data.refresh_at,t.feed_id=s.data.last_feed_id,s.data.feed_list.forEach(e=>{this.feedCache.push(e)}),!s.data.has_more)break}this.version.check(e)&&(this.feedCache.markSyncComplete(),this.emit(u.FEED_LIST_UPDATED,{list:this.feedCache.getAll(),is_sync_completed:!0}))}isReady(){return this.context.isReady}setLogLevel(e){this.logger.setLogLevel(e)}async login(t){if(!await this.singletonLock.request())return this.logger.error("Another active TypeX SDK instance detected"),S(n.REQUEST_SINGLETON_LOCK_FAIL,"Another active TypeX SDK instance detected");if(await this.requestDeviceId(),!this.context.deviceId)return this.logger.error("Get deviceId failed"),S(n.SDK_ERROR,"Login params invalid");const s=await this.httpClient.post("/user/session/login",{userCode:t.user_code,device_id:this.context.deviceId},{needSign:!1});return s.ok?(this.context.isLogin=!0,this.context.isLogin&&(this.context.loginUser={userId:s.data.id,secretKey:s.data.key,sessionId:e.get("sessionid")??""}),this.applyLoginStatus(),f(null)):s}async logout(){const e=await this.httpClient.get("/user/logout");return e.ok?(this.context.isLogin=!1,this.applyLoginStatus(),f(null)):e}async handleKickoff(){this.context.isLogin=!1,this.applyLoginStatus()}getLoginUser(){return this.context.isLogin?this.context.loginUser.userId:null}async createSingleChatFeed(e){return await this.httpClient.post("/chat/single",{receiver_id:e})}async deleteFeed(e){return await this.httpClient.post("/feed/delete",{feed_id_list:[e],delete_peer:!0})}async getFeedList(){return f({list:this.feedCache.getAll(),is_sync_completed:this.feedCache.isSyncCompleted()})}async getFeedDetail(e){return f(await this.feedCache.waitFor(e))}async getFeedMessageList(e){const t=await this.getFeedDetail(e.feed_id);if(t.ok&&t.data){const s=t.data,{chat_id:i,first_position:n,last_message_position:o}=s,r=(e=>{const t=[];let s=e.lastPosition??e.maxPosition;for(;t.length<e.limit&&s>=e.minPosition;)t.push(s),s--;return t})({maxPosition:o,minPosition:n,lastPosition:e.last_message_position,limit:e.limit});if(r.length>0){const e=await this.httpClient.post("/message/get_by_position",{chat_position:{[i]:r}},{needSign:!1});if(e.ok&&e.data[i]){const t=e.data[i].reverse();return f({has_more:r[0]===n,list:t,last_message_position:t[0]?.position})}}}return f({has_more:!1,list:[]})}createCustomMessage(e){if(!this.context.isLogin)return S(n.SDK_ERROR,"not login");const t=this.feedCache.get(e.feed_id);if(null===t)return S(n.SDK_ERROR,"not found feed");var s;return f({id:"",chat_id:t.chat_id,position:t.last_message_position,sender_id:this.context.loginUser.userId,message_kind_id:a.text,message_type_id:c.custom,send_at:"",reader_ids:[],hide_ids:[],operation:l.normal,operation_user_id:"0",content:(s={client_msg_id:e.client_msg_id,content:e.content},JSON.stringify({client_msg_id:s.client_msg_id,content:JSON.stringify(s.content)}))})}async sendMessageToFeed(e){if(e.message_type_id===c.custom){const t=A(e.content);return this.httpClient.post("/message/send_custom",{chat_id:e.chat_id,client_id:t.client_msg_id,content:null==t.content?void 0:JSON.stringify(t.content)})}return S(n.SDK_ERROR,"not support")}async sendMessageReadReceipt(e){const t=this.feedCache.get(e.feed_id);return null===t?S(n.SDK_ERROR,"not found feed"):await this.httpClient.post("/message/read",{chat_id:t.chat_id,message_ids:e.message_ids})}async joinRoom(e){return await this.httpClient.post("/liveshow/join",{room_id:e})}async leaveRoom(e){return await this.httpClient.post("/liveshow/leave",{room_id:e})}async getRoomRecentMessageList(e){const t=await this.httpClient.get("/liveshow/get_recent_message",{room_id:e});return t.ok?f(t.data):t}async sendMessageToRoom(e){return await this.httpClient.post("/liveshow/send_message",{room_id:e.room_id,content:e.content})}}function F(e){return new x(e)}const K={parseSystemMessageContent:e=>{const t=T(e);return null!==t?{text:t.text,text_i18n:t.text_i18n}:{}},parseTextMessageContent:e=>{const t=T(e);return null!==t?{text:t.text,client_msg_id:t.client_msg_id}:{}},parseCustomMessageContent:A};export{x as ChatSDK,F as create,g as enums,p as events,m as interfaces,K as utils};
1
+ import e from"js-cookie";import t from"axios";import s from"crypto-js";var i,n,o,r,a,c,l,h,d,_,E;!function(e){e[e.yes=1]="yes",e[e.no=0]="no"}(i||(i={})),function(e){e[e.CREATE_FEED=0]="CREATE_FEED",e[e.UPDATE_FEED=1]="UPDATE_FEED",e[e.ADD_MESSAGE=2]="ADD_MESSAGE",e[e.UPDATE_MESSAGE=3]="UPDATE_MESSAGE",e[e.UPDATE_USER_DATA=4]="UPDATE_USER_DATA",e[e.USER_LOGIN=5]="USER_LOGIN",e[e.READ_MESSAGE=6]="READ_MESSAGE",e[e.PONG_MESSAGE=8]="PONG_MESSAGE",e[e.DELETE_MESSAGE=9]="DELETE_MESSAGE",e[e.UPDATE_GROUP=12]="UPDATE_GROUP",e[e.DELETE_GROUP=13]="DELETE_GROUP",e[e.UPDATE_LOGIN_USER=17]="UPDATE_LOGIN_USER",e[e.UPDATE_MESSAGE_REACTION=19]="UPDATE_MESSAGE_REACTION",e[e.ADD_GROUP_MEMBER=21]="ADD_GROUP_MEMBER",e[e.DEL_GROUP_MEMBER=22]="DEL_GROUP_MEMBER",e[e.ADD_CONTACTS=23]="ADD_CONTACTS",e[e.UPDATE_CONTACTS=24]="UPDATE_CONTACTS",e[e.DEL_CONTACTS=25]="DEL_CONTACTS",e[e.BLOCK_USER=26]="BLOCK_USER",e[e.DEVICE_CODE=27]="DEVICE_CODE",e[e.MSG_OPEN_CLOCK=28]="MSG_OPEN_CLOCK",e[e.DELETE_SECRET_CHAT=29]="DELETE_SECRET_CHAT",e[e.DELETE_FEED=30]="DELETE_FEED",e[e.FAVORITE_UPDATE=31]="FAVORITE_UPDATE",e[e.STICKERS_UPDATE=32]="STICKERS_UPDATE",e[e.CUSTOM_STICKERS_UPDATE=33]="CUSTOM_STICKERS_UPDATE",e[e.MANAGE_DEVICE_UPDATE=34]="MANAGE_DEVICE_UPDATE",e[e.MASS_MESSAGE_UPDATE=35]="MASS_MESSAGE_UPDATE",e[e.CHAT_WALLPAPER_DELETE=36]="CHAT_WALLPAPER_DELETE",e[e.CHAT_WALLPAPER_ADD=37]="CHAT_WALLPAPER_ADD",e[e.CONTACT_REQUEST=40]="CONTACT_REQUEST",e[e.USER_PREFERENCE_CHANGE=38]="USER_PREFERENCE_CHANGE",e[e.UPDATE_GROUP_PIN=41]="UPDATE_GROUP_PIN",e[e.RTC_CANCEL=46]="RTC_CANCEL",e[e.ADD_FEED_FOLDER=43]="ADD_FEED_FOLDER",e[e.UPDATE_FEED_FOLDER=44]="UPDATE_FEED_FOLDER",e[e.UPDATE_FOLDER_FEED_LIST=47]="UPDATE_FOLDER_FEED_LIST",e[e.DELETE_FEED_FOLDER=45]="DELETE_FEED_FOLDER",e[e.UPDATE_CHATTER=42]="UPDATE_CHATTER",e[e.PAPER_KEY=48]="PAPER_KEY",e[e.PAPER_KEY_LOG=49]="PAPER_KEY_LOG",e[e.USER_LOGIN_SCAN=50]="USER_LOGIN_SCAN",e[e.RTC_RINGING=51]="RTC_RINGING",e[e.ON_UPDATE_PRIVATE_SETTING=52]="ON_UPDATE_PRIVATE_SETTING",e[e.ON_DELETE_PRIVATE_SETTING=53]="ON_DELETE_PRIVATE_SETTING",e[e.ON_UPDATE_LOGIN_PRIVATE_SETTING=54]="ON_UPDATE_LOGIN_PRIVATE_SETTING",e[e.PUSH_LIVE_MESSAGE=56]="PUSH_LIVE_MESSAGE",e[e.BIG_PACKAGE_PUSH=126]="BIG_PACKAGE_PUSH",e[e.KICK_OUT_DEVICE=127]="KICK_OUT_DEVICE",e[e.CLEAR_HISTORY=128]="CLEAR_HISTORY",e[e.CLEAR_DATA=255]="CLEAR_DATA"}(n||(n={})),function(e){e[e.singleChat=1]="singleChat",e[e.groupChat=2]="groupChat"}(o||(o={})),function(e){e[e.text=0]="text",e[e.system=1]="system"}(r||(r={})),function(e){e[e.text=0]="text",e[e.image=1]="image",e[e.card=2]="card",e[e.audio=3]="audio",e[e.video=4]="video",e[e.file=5]="file",e[e.mergeForward=6]="mergeForward",e[e.sticker=7]="sticker",e[e.richText=8]="richText",e[e.custom=9]="custom"}(a||(a={})),function(e){e[e.success=0]="success",e[e.sending=1]="sending",e[e.failed=2]="failed"}(c||(c={})),function(e){e[e.Owner=1]="Owner",e[e.Admin=2]="Admin",e[e.Common=3]="Common",e[e.Bot=4]="Bot"}(l||(l={})),function(e){e[e.normal=0]="normal",e[e.recall=1]="recall"}(h||(h={})),function(e){e[e.private=1]="private",e[e.public=0]="public",e[e.anonymous=2]="anonymous"}(d||(d={})),function(e){e.error="ERROR",e.warning="WARNING",e.info="INFO"}(_||(_={})),function(e){e[e.SDK_ERROR=-1e4]="SDK_ERROR",e[e.NETWORK_ERROR=-9999]="NETWORK_ERROR",e[e.MAKE_SIGN_FAIL=-9998]="MAKE_SIGN_FAIL",e[e.REQUEST_SINGLETON_LOCK_FAIL=-9997]="REQUEST_SINGLETON_LOCK_FAIL",e[e.PARAMS_INVALID=-9996]="PARAMS_INVALID",e[e.SUCCESS=0]="SUCCESS"}(E||(E={}));var g=Object.freeze({__proto__:null,get Boolean(){return i},get ChatType(){return o},get GroupRole(){return l},get GroupType(){return d},get HttpClientResponseCode(){return E},get LogLevel(){return _},get MessageKind(){return r},get MessageOperation(){return h},get MessageSendStatus(){return c},get MessageType(){return a},get PushMessageType(){return n}});const u={SDK_READY:"sdkReady",SDK_NOT_READY:"sdkNotReady",FEED_LIST_UPDATED:"feedListUpdated",MESSAGE_RECEIVED:"messageReceived",MESSAGE_UPDATED:"messageUpdated",MESSAGE_RECEIPT_RECEIVED:"messageReceiptReceived",ROOM_MESSAGE_RECEIVED:"roomMessageReceived"};var p=Object.freeze({__proto__:null,CHAT_SDK_EVENT:u}),m=Object.freeze({__proto__:null});function f(e,t){return{ok:!1,code:e,msg:t}}function S(e){return{ok:!0,data:e}}const T=e=>{try{return null==e||"string"!=typeof e||0===e.length?null:JSON.parse(e)}catch{return null}},A=e=>{const t=T(e);if(null!==t){let e;return null!=t.content&&(e=T(t.content)??void 0),{client_msg_id:t.client_msg_id,content:e}}return{}};class C{all;constructor(e){this.all=e||new Map}on(e,t){const s=this.all.get(e);return s?s.push(t):this.all.set(e,[t]),()=>{this.off(e,t)}}off(e,t){const s=this.all.get(e);s&&(t?s.splice(s.indexOf(t)>>>0,1):this.all.set(e,[]))}emit(e,t){let s=this.all.get(e);s&&s.slice().map(e=>{e(t)}),s=this.all.get("*"),s&&s.slice().map(s=>{s(e,t)})}once(e,t){if("*"===e){const s=(e,i)=>{t(e,i),this.off("*",s)};this.on(e,s)}else{const s=i=>{t(i),this.off(e,s)};this.on(e,s)}}}class R extends C{url;logger;ws;destroyed;pingTimeout;pongTimeout;reConnectTimeout;constructor(e){super(),this.url=e.url,this.logger=e.logger,this.ws=null,this.destroyed=!1,this.pingTimeout=null,this.pongTimeout=null,this.reConnectTimeout=null,this.reConnect=this.reConnect.bind(this),window.addEventListener("online",this.reConnect),this.connect()}disconnect(){this.ws&&(this.ws.onopen=null,this.ws.onerror=null,this.ws.onmessage=null,this.ws.onclose=null,this.ws.close(),this.ws=null),null!==this.pingTimeout&&(clearTimeout(this.pingTimeout),this.pingTimeout=null),null!==this.pongTimeout&&(clearTimeout(this.pongTimeout),this.pongTimeout=null),null!==this.reConnectTimeout&&(clearTimeout(this.reConnectTimeout),this.reConnectTimeout=null)}connect(){if(this.ws)return void this.logger.warn("ReConnectWebsocket try connect but already connected");const e=new WebSocket(this.url);this.ws=e,e.onopen=()=>{this.delaySendPing()},e.onerror=e=>{this.logger.info("ReConnectWebsocket error",e)},e.onmessage=e=>{try{const t=JSON.parse(e.data);if(t.type===n.PONG_MESSAGE?null!==this.pongTimeout&&(clearTimeout(this.pongTimeout),this.pongTimeout=null,this.delaySendPing()):this.sendAck(t.id),t.type===n.BIG_PACKAGE_PUSH)for(const e of t.content.big_push)this.emit("message",e);else this.emit("message",t)}catch(e){this.logger.error("ReConnectWebsocket message parse failed",e)}},e.onclose=e=>{4001===e.code?this.kickOff():this.reConnect()}}reConnect(){this.disconnect(),this.destroyed||(this.reConnectTimeout=setTimeout(()=>{this.reConnectTimeout=null,this.connect()},5e3))}send(e){if(this.destroyed)return!1;if(!this.ws)return this.logger.warn("ReConnectWebsocket send before connect"),!1;try{return this.ws.readyState!==this.ws.OPEN?(this.logger.warn("ReConnectWebsocket send but not open",this.ws.readyState),!1):(this.ws.send(JSON.stringify(e)),!0)}catch(e){return this.logger.warn("ReConnectWebsocket send error",e),!1}}sendAck(e){this.send({type:1,ids:[e]})}destroy(){this.destroyed||(this.destroyed=!0,this.disconnect(),window.removeEventListener("online",this.reConnect))}delaySendPing(){null!==this.pingTimeout&&(clearTimeout(this.pingTimeout),this.pingTimeout=null),this.pingTimeout=setTimeout(()=>{this.pingTimeout=null;this.send({type:0})?this.waitPong():this.reConnect()},2e4)}waitPong(){this.pongTimeout=setTimeout(()=>{this.logger.info("ReConnectWebsocket pong timeout"),this.reConnect()},1e4)}kickOff(){this.destroy(),this.emit("kickoff")}}class D extends C{host;context;logger;ws;constructor(e){super(),this.host=e.host,this.context=e.context,this.logger=e.logger,this.ws=null}connect(){if(null===this.context.loginUser)return void this.logger.warn("BusinessWebsocket try connect but not login");if(this.ws)return void this.logger.warn("BusinessWebsocket try connect but already connected");const e=`${this.host}/ws?token=${this.context.loginUser.sessionId}&platform=web`;this.ws=new R({url:e,logger:this.logger}),this.ws.on("message",e=>{this.emit("message",e)}),this.ws.on("kickoff",()=>{this.emit("kickoff")})}disconnect(){this.ws?(this.ws.destroy(),this.ws=null):this.logger.info("BusinessWebsocket already disconnected")}}class L{_isSyncCompleted;map;chatMap;list;waitMap;constructor(){this._isSyncCompleted=!1,this.map=new Map,this.chatMap=new Map,this.waitMap=new Map,this.list=[]}getAll(){const e=[];for(const t of this.list){const s=this.map.get(t);s&&e.push(s)}return e}get(e){return this.map.get(e)??null}getByChatId(e){const t=this.chatMap.get(e);return void 0!==t?this.get(t):null}async waitFor(e){const t=this.get(e);if(t)return t;if(this._isSyncCompleted)return null;let s=this.waitMap.get(e);return s||(s=(()=>{let e=()=>{};return{promise:new Promise(t=>{e=t}),resolve:e}})(),this.waitMap.set(e,s)),await s.promise,this.get(e)}resolveWait(e){const t=this.waitMap.get(e);t&&(t.resolve(!0),this.waitMap.delete(e))}remove(e){const t=this.map.get(e);return!!t&&(this.map.delete(e),this.chatMap.delete(t.chat_id),this.list.splice(this.list.indexOf(e),1),!0)}push(e){this.remove(e.id),this.map.set(e.id,e),this.list.push(e.id),this.chatMap.set(e.chat_id,e.id),this.resolveWait(e.id)}unshift(e){this.remove(e.id),this.map.set(e.id,e),this.list.unshift(e.id),this.chatMap.set(e.chat_id,e.id),this.resolveWait(e.id)}update(e){this.map.has(e.id)&&this.map.set(e.id,e)}markSyncComplete(){this._isSyncCompleted=!0;for(const[e,t]of this.waitMap)t.resolve(!0);this.waitMap.clear()}isSyncCompleted(){return this._isSyncCompleted}reset(){this._isSyncCompleted=!1,this.map.clear(),this.chatMap.clear(),this.waitMap.clear(),this.list=[]}}function y(e){return(new TextEncoder).encode(e).buffer}async function w(e,t){return window.crypto&&window.crypto.subtle?await async function(e,t){const s=y(t),i=y(e),n=await crypto.subtle.importKey("raw",s,{name:"HMAC",hash:{name:"SHA-256"}},!1,["sign"]),o=await crypto.subtle.sign("HMAC",n,i);return Array.from(new Uint8Array(o)).map(e=>e.toString(16).padStart(2,"0")).join("")}(e,t):function(e,t){return s.HmacSHA256(e,t).toString(s.enc.Hex)}(e,t)}const P=e=>{if(null==e)return"";if("object"!=typeof e||Array.isArray(e)){if(Array.isArray(e)){let t="";for(const s of e)t+=P(s);return t}return"string"==typeof e?e:String(e)}{const t=Object.keys(e).sort();let s="";for(const i of t)s+=P(e[i]);return s}};function O(){return f(E.MAKE_SIGN_FAIL,"make sign fail")}class I{api;context;logger;constructor(e){this.api=t.create({baseURL:e.host,headers:{"x-developer":e.developer}}),this.context=e.context,this.logger=e.logger}async request(e){try{const{needSign:t=!0,...s}=e;if(s.headers||(s.headers={}),s.headers.platform="web",t&&"POST"===e.method?.toUpperCase()&&s.data){if(null===this.context.loginUser)return this.logger.warn("HttpClient needs make sign but not login"),O();try{const e=await(async(e,t)=>{const s=await P(e);return await w(s,t)})(s.data,this.context.loginUser.secretKey);s.headers.sign=e}catch(e){return this.logger.error("HttpClient make sign error",e),O()}}const i=await this.api(s),n=i.data;return n.code===E.SUCCESS?{ok:!0,data:n.data}:(this.logger.info("HttpClient server error",i),{ok:!1,code:n.code,msg:n.msg,msg_i18n:n.msg_i18n})}catch{return{ok:!1,code:E.NETWORK_ERROR,msg:"network error"}}}get(e,t,s){return this.request({url:e,method:"GET",params:t,...s})}post(e,t,s){return this.request({url:e,method:"POST",data:t,...s})}}class U{level;logHandler;constructor(){this.level=_.error,this.logHandler=this.createLogHandler()}getPriority(e){switch(e){case _.error:return 3;case _.warning:return 2;case _.info:return 1;default:return 0}}createLogHandler(){return(e,t,...s)=>{const i=`[tlog] ${(new Date).toISOString().split("T")[0]}`,n=t?`${i} ${t}`:i;switch(e){case _.error:console.error(n,...s);break;case _.warning:console.warn(n,...s);break;case _.info:console.info(n,...s);break;default:console.log(n,...s)}}}log(e,t,...s){this.getPriority(e)>=this.getPriority(this.level)&&this.logHandler(e,t,...s)}setLogLevel(e){this.level=e,this.logHandler=this.createLogHandler()}info(e,...t){this.log(_.info,e,...t)}warn(e,...t){this.log(_.warning,e,...t)}error(e,...t){this.log(_.error,e,...t)}}class k{context;logger;emitter;feedCache;handleKickoff;constructor(e){this.context=e.context,this.logger=e.logger,this.emitter=e.emitter,this.feedCache=e.feedCache,this.handleKickoff=e.onKickoff}emitFeedListUpdated(){this.emitter.emit(u.FEED_LIST_UPDATED,{list:this.feedCache.getAll(),is_sync_completed:this.feedCache.isSyncCompleted()})}processCreateFeed(e){this.feedCache.unshift(e.content),this.emitFeedListUpdated()}processDeleteFeed(e){this.feedCache.remove(e.content.id)&&this.emitFeedListUpdated()}processAddMessage(e){if(null===this.context.loginUser)return;const t=this.context.loginUser.userId,s=e.content,i=this.feedCache.getByChatId(s.chat_id);if(null!==i){let e=!1;const n={};if(s.message_kind_id!==r.system&&s.sender_id!==t){e=!0;const t=new Set(i.unread_message_ids);t.add(s.id),n.unread_message_ids=[...t],n.badge=t.size}i.last_message_position<s.position&&(e=!0,n.last_message_position=s.position,n.last_message_send_at=s.send_at,n.last_message=s.content,n.last_message_kind_id=s.message_kind_id,n.last_message_type_id=s.message_type_id),e&&(this.feedCache.unshift({...i,...n}),this.emitFeedListUpdated()),this.emitter.emit(u.MESSAGE_RECEIVED,s)}}processUpdateMessage(e){this.emitter.emit(u.MESSAGE_UPDATED,e.content)}processPushLiveMessage(e){this.emitter.emit(u.ROOM_MESSAGE_RECEIVED,e.content)}processReadMessage(e){if(null===this.context.loginUser)return;const t=this.context.loginUser.userId,s=this.feedCache.getByChatId(e.content.chat_id);if(null!==s){let i=!1;const n={};if(e.content.reader_ids.includes(t)&&e.content.message_ids.length>0){i=!0;const t=new Set(s.unread_message_ids);e.content.message_ids.forEach(e=>{t.delete(e)}),n.unread_message_ids=[...t],n.badge=t.size}i&&(this.feedCache.update({...s,...n}),this.emitFeedListUpdated()),this.emitter.emit(u.MESSAGE_RECEIPT_RECEIVED,{feed_id:s.id,message_ids:e.content.message_ids,reader_user_ids:e.content.reader_ids})}}processKickOutDevice(){this.handleKickoff()}process(e){switch(this.logger.info("PushMessageProcessor process",e),e.type){case n.CREATE_FEED:this.processCreateFeed(e);break;case n.DELETE_FEED:this.processDeleteFeed(e);break;case n.ADD_MESSAGE:this.processAddMessage(e);break;case n.UPDATE_MESSAGE:this.processUpdateMessage(e);break;case n.PUSH_LIVE_MESSAGE:this.processPushLiveMessage(e);break;case n.READ_MESSAGE:this.processReadMessage(e);break;case n.KICK_OUT_DEVICE:this.processKickOutDevice()}}}class M{obtained;abortLockObtain;constructor(){this.obtained=!1,this.abortLockObtain=null}async requestLock(){if(this.obtained)return!0;let e=()=>{};const t=new Promise(t=>{e=t}),s=new Promise(e=>{this.abortLockObtain=e});navigator.locks.request("typex-sdk",{ifAvailable:!0},async t=>{t?(this.obtained=!0,e(!0),await s):e(!1)});return await t}releaseLock(){this.obtained=!1,null!==this.abortLockObtain&&(this.abortLockObtain(),this.abortLockObtain=null)}}class v{static key="__typex_sdk_data__";static set(e){localStorage.setItem(this.key,JSON.stringify(e))}static get(){try{const e=localStorage.getItem(this.key);if(!e)return null;return JSON.parse(e)}catch{return null}}static clear(){localStorage.removeItem(this.key)}}class G{version;constructor(){this.version=0}increment(){this.version++}get(){return this.version}check(e){return e===this.version}}class N extends C{context;version;singletonLock;logger;httpClient;feedCache;ws;pushMessageProcessor;constructor(e){super(),this.context={isLogin:!1,loginUser:null,isReady:!1,deviceId:null},this.version=new G,this.singletonLock=new M,this.logger=new U,this.httpClient=new I({host:e.httpHost,context:this.context,logger:this.logger,developer:e.developer}),this.feedCache=new L,this.ws=new D({host:e.websocketHost,context:this.context,logger:this.logger}),this.pushMessageProcessor=new k({context:this.context,logger:this.logger,emitter:this,feedCache:this.feedCache,onKickoff:()=>{this.afterLogout()}}),this.init()}init(){this.loadContext(),this.ws.on("message",e=>{this.pushMessageProcessor.process(e)}),this.ws.on("kickoff",()=>{this.afterLogout()})}async requestDeviceId(){if(this.context.deviceId)return!0;const e=await this.httpClient.post("/device_api/device_id",{platform:"web",hash_str:""},{needSign:!1});return!!e.ok&&(this.context.deviceId=e.data,this.saveContext(),!0)}saveContext(){const{context:e}=this;v.set({deviceId:e.deviceId})}loadContext(){const e=v.get();e&&e.deviceId&&(this.context.deviceId=e.deviceId)}applyLoginStatus(){this.version.increment(),this.saveContext(),this.context.isLogin?(this.ws.connect(),this.loadFeedList(),this.context.isReady=!0,this.emit(u.SDK_READY)):(this.ws.disconnect(),this.feedCache.reset(),this.singletonLock.releaseLock(),this.context.isReady=!1,this.emit(u.SDK_NOT_READY))}async loadFeedList(){const e=this.version.get(),t={refresh_at:null,feed_id:null};for(;;){const s=await this.httpClient.get("/feed",{limit:500,last_refresh_at:t.refresh_at??void 0,last_feed_id:t.feed_id??void 0});if(!this.version.check(e))break;if(!s.ok)break;if(t.refresh_at=s.data.refresh_at,t.feed_id=s.data.last_feed_id,s.data.feed_list.forEach(e=>{this.feedCache.push(e)}),!s.data.has_more)break}this.version.check(e)&&(this.feedCache.markSyncComplete(),this.emit(u.FEED_LIST_UPDATED,{list:this.feedCache.getAll(),is_sync_completed:!0}))}isReady(){return this.context.isReady}setLogLevel(e){this.logger.setLogLevel(e)}async login(t){if(this.context.isLogin)return S(null);if(!await this.singletonLock.requestLock())return this.logger.error("Another active TypeX SDK instance detected"),f(E.REQUEST_SINGLETON_LOCK_FAIL,"Another active TypeX SDK instance detected");if(await this.requestDeviceId(),!this.context.deviceId)return this.logger.error("Get deviceId failed"),f(E.SDK_ERROR,"Get deviceId failed");const s=await this.httpClient.post("/user/session/login",{userCode:t.user_code,device_id:this.context.deviceId},{needSign:!1});if(!s.ok)return this.singletonLock.releaseLock(),s;const i=e.get("sessionid");return i?(this.context.isLogin=!0,this.context.loginUser={userId:s.data.id,secretKey:s.data.key,sessionId:i},this.applyLoginStatus(),S(null)):(this.singletonLock.releaseLock(),f(E.SDK_ERROR,"Get sessionId failed"))}async logout(){if(!this.context.isLogin)return S(null);const e=await this.httpClient.get("/user/logout");return e.ok?(this.afterLogout(),S(null)):e}async afterLogout(){this.context.isLogin=!1,this.context.loginUser=null,this.applyLoginStatus()}getLoginUser(){return null===this.context.loginUser?null:this.context.loginUser.userId}async createSingleChatFeed(e){return await this.httpClient.post("/chat/single",{receiver_id:e})}async deleteFeed(e){return await this.httpClient.post("/feed/delete",{feed_id_list:[e],delete_peer:!0})}async getFeedList(){return S({list:this.feedCache.getAll(),is_sync_completed:this.feedCache.isSyncCompleted()})}async getFeedDetail(e){return S(await this.feedCache.waitFor(e))}async getFeedMessageList(e){const t=await this.getFeedDetail(e.feed_id);if(t.ok&&t.data){const s=t.data,{chat_id:i,first_position:n,last_message_position:o}=s,r=(e=>{const t=[];let s=e.lastPosition??e.maxPosition;for(;t.length<e.limit&&s>=e.minPosition;)t.push(s),s--;return t})({maxPosition:o,minPosition:n,lastPosition:e.last_message_position,limit:e.limit});if(r.length>0){const e=await this.httpClient.post("/message/get_by_position",{chat_position:{[i]:r}},{needSign:!1});if(e.ok&&e.data[i]){const t=e.data[i].reverse();return S({has_more:r[0]===n,list:t,last_message_position:t[0]?.position})}}}return S({has_more:!1,list:[]})}createCustomMessage(e){if(null===this.context.loginUser)return f(E.SDK_ERROR,"not login");const t=this.feedCache.get(e.feed_id);if(null===t)return f(E.SDK_ERROR,"not found feed");var s;return S({id:"",chat_id:t.chat_id,position:t.last_message_position,sender_id:this.context.loginUser.userId,message_kind_id:r.text,message_type_id:a.custom,send_at:"",reader_ids:[],hide_ids:[],operation:h.normal,operation_user_id:"0",content:(s={client_msg_id:e.client_msg_id,content:e.content},JSON.stringify({client_msg_id:s.client_msg_id,content:JSON.stringify(s.content)}))})}async sendMessageToFeed(e){if(e.message_type_id===a.custom){const t=A(e.content);return this.httpClient.post("/message/send_custom",{chat_id:e.chat_id,client_id:t.client_msg_id,content:null==t.content?void 0:JSON.stringify(t.content)})}return f(E.SDK_ERROR,"not support")}async sendMessageReadReceipt(e){const t=this.feedCache.get(e.feed_id);return null===t?f(E.SDK_ERROR,"not found feed"):await this.httpClient.post("/message/read",{chat_id:t.chat_id,message_ids:e.message_ids})}async joinRoom(e){return await this.httpClient.post("/liveshow/join",{room_id:e})}async leaveRoom(e){return await this.httpClient.post("/liveshow/leave",{room_id:e})}async getRoomRecentMessageList(e){const t=await this.httpClient.get("/liveshow/get_recent_message",{room_id:e});return t.ok?S(t.data):t}async sendMessageToRoom(e){return await this.httpClient.post("/liveshow/send_message",{room_id:e.room_id,content:e.content})}}const x=new Map;function b(e,t){let s=x.get(e);return void 0===s&&(s=new N(t),x.set(e,s)),s}const F={parseSystemMessageContent:e=>{const t=T(e);return null!==t?{text:t.text,text_i18n:t.text_i18n}:{}},parseTextMessageContent:e=>{const t=T(e);return null!==t?{text:t.text,client_msg_id:t.client_msg_id}:{}},parseCustomMessageContent:A};export{N as ChatSDK,b as create,g as enums,p as events,m as interfaces,F as utils};
package/dist/web-sdk.js CHANGED
@@ -1 +1 @@
1
- "use strict";var e,t,s,i,n,o,r,a,c,h,l,d=require("js-cookie"),_=require("axios"),E=require("crypto-js");!function(e){e[e.yes=1]="yes",e[e.no=0]="no"}(e||(e={})),function(e){e[e.SDK_ERROR=-1e4]="SDK_ERROR",e[e.NETWORK_ERROR=-9999]="NETWORK_ERROR",e[e.MAKE_SIGN_FAIL=-9998]="MAKE_SIGN_FAIL",e[e.REQUEST_SINGLETON_LOCK_FAIL=-9997]="REQUEST_SINGLETON_LOCK_FAIL",e[e.PARAMS_INVALID=-9996]="PARAMS_INVALID",e[e.SUCCESS=0]="SUCCESS"}(t||(t={})),function(e){e[e.CREATE_FEED=0]="CREATE_FEED",e[e.UPDATE_FEED=1]="UPDATE_FEED",e[e.ADD_MESSAGE=2]="ADD_MESSAGE",e[e.UPDATE_MESSAGE=3]="UPDATE_MESSAGE",e[e.UPDATE_USER_DATA=4]="UPDATE_USER_DATA",e[e.USER_LOGIN=5]="USER_LOGIN",e[e.READ_MESSAGE=6]="READ_MESSAGE",e[e.PONG_MESSAGE=8]="PONG_MESSAGE",e[e.DELETE_MESSAGE=9]="DELETE_MESSAGE",e[e.UPDATE_GROUP=12]="UPDATE_GROUP",e[e.DELETE_GROUP=13]="DELETE_GROUP",e[e.UPDATE_LOGIN_USER=17]="UPDATE_LOGIN_USER",e[e.UPDATE_MESSAGE_REACTION=19]="UPDATE_MESSAGE_REACTION",e[e.ADD_GROUP_MEMBER=21]="ADD_GROUP_MEMBER",e[e.DEL_GROUP_MEMBER=22]="DEL_GROUP_MEMBER",e[e.ADD_CONTACTS=23]="ADD_CONTACTS",e[e.UPDATE_CONTACTS=24]="UPDATE_CONTACTS",e[e.DEL_CONTACTS=25]="DEL_CONTACTS",e[e.BLOCK_USER=26]="BLOCK_USER",e[e.DEVICE_CODE=27]="DEVICE_CODE",e[e.MSG_OPEN_CLOCK=28]="MSG_OPEN_CLOCK",e[e.DELETE_SECRET_CHAT=29]="DELETE_SECRET_CHAT",e[e.DELETE_FEED=30]="DELETE_FEED",e[e.FAVORITE_UPDATE=31]="FAVORITE_UPDATE",e[e.STICKERS_UPDATE=32]="STICKERS_UPDATE",e[e.CUSTOM_STICKERS_UPDATE=33]="CUSTOM_STICKERS_UPDATE",e[e.MANAGE_DEVICE_UPDATE=34]="MANAGE_DEVICE_UPDATE",e[e.MASS_MESSAGE_UPDATE=35]="MASS_MESSAGE_UPDATE",e[e.CHAT_WALLPAPER_DELETE=36]="CHAT_WALLPAPER_DELETE",e[e.CHAT_WALLPAPER_ADD=37]="CHAT_WALLPAPER_ADD",e[e.CONTACT_REQUEST=40]="CONTACT_REQUEST",e[e.USER_PREFERENCE_CHANGE=38]="USER_PREFERENCE_CHANGE",e[e.UPDATE_GROUP_PIN=41]="UPDATE_GROUP_PIN",e[e.RTC_CANCEL=46]="RTC_CANCEL",e[e.ADD_FEED_FOLDER=43]="ADD_FEED_FOLDER",e[e.UPDATE_FEED_FOLDER=44]="UPDATE_FEED_FOLDER",e[e.UPDATE_FOLDER_FEED_LIST=47]="UPDATE_FOLDER_FEED_LIST",e[e.DELETE_FEED_FOLDER=45]="DELETE_FEED_FOLDER",e[e.UPDATE_CHATTER=42]="UPDATE_CHATTER",e[e.PAPER_KEY=48]="PAPER_KEY",e[e.PAPER_KEY_LOG=49]="PAPER_KEY_LOG",e[e.USER_LOGIN_SCAN=50]="USER_LOGIN_SCAN",e[e.RTC_RINGING=51]="RTC_RINGING",e[e.ON_UPDATE_PRIVATE_SETTING=52]="ON_UPDATE_PRIVATE_SETTING",e[e.ON_DELETE_PRIVATE_SETTING=53]="ON_DELETE_PRIVATE_SETTING",e[e.ON_UPDATE_LOGIN_PRIVATE_SETTING=54]="ON_UPDATE_LOGIN_PRIVATE_SETTING",e[e.PUSH_LIVE_MESSAGE=56]="PUSH_LIVE_MESSAGE",e[e.BIG_PACKAGE_PUSH=126]="BIG_PACKAGE_PUSH",e[e.KICK_OUT_DEVICE=127]="KICK_OUT_DEVICE",e[e.CLEAR_HISTORY=128]="CLEAR_HISTORY",e[e.CLEAR_DATA=255]="CLEAR_DATA"}(s||(s={})),function(e){e[e.singleChat=1]="singleChat",e[e.groupChat=2]="groupChat"}(i||(i={})),function(e){e[e.text=0]="text",e[e.system=1]="system"}(n||(n={})),function(e){e[e.text=0]="text",e[e.image=1]="image",e[e.card=2]="card",e[e.audio=3]="audio",e[e.video=4]="video",e[e.file=5]="file",e[e.mergeForward=6]="mergeForward",e[e.sticker=7]="sticker",e[e.richText=8]="richText",e[e.custom=9]="custom"}(o||(o={})),function(e){e[e.success=0]="success",e[e.sending=1]="sending",e[e.failed=2]="failed"}(r||(r={})),function(e){e[e.Owner=1]="Owner",e[e.Admin=2]="Admin",e[e.Common=3]="Common",e[e.Bot=4]="Bot"}(a||(a={})),function(e){e[e.normal=0]="normal",e[e.recall=1]="recall"}(c||(c={})),function(e){e[e.private=1]="private",e[e.public=0]="public",e[e.anonymous=2]="anonymous"}(h||(h={})),function(e){e.error="ERROR",e.warning="WARNING",e.info="INFO"}(l||(l={}));var g=Object.freeze({__proto__:null,get ApiResponseCode(){return t},get Boolean(){return e},get ChatType(){return i},get GroupRole(){return a},get GroupType(){return h},get LogLevel(){return l},get MessageKind(){return n},get MessageOperation(){return c},get MessageSendStatus(){return r},get MessageType(){return o},get PushMessageType(){return s}});const u={SDK_READY:"sdkReady",SDK_NOT_READY:"sdkNotReady",FEED_LIST_UPDATED:"feedListUpdated",MESSAGE_RECEIVED:"messageReceived",MESSAGE_UPDATED:"messageUpdated",MESSAGE_RECEIPT_RECEIVED:"messageReceiptReceived",ROOM_MESSAGE_RECEIVED:"roomMessageReceived"};var p=Object.freeze({__proto__:null,CHAT_SDK_EVENT:u}),m=Object.freeze({__proto__:null});function S(e,t){return{ok:!1,code:e,msg:t}}function T(e){return{ok:!0,data:e}}const A=e=>{try{return null==e||"string"!=typeof e||0===e.length?null:JSON.parse(e)}catch{return null}},f=e=>{const t=A(e);if(null!==t){let e;return null!=t.content&&(e=A(t.content)??void 0),{client_msg_id:t.client_msg_id,content:e}}return{}};class C{all;constructor(e){this.all=e||new Map}on(e,t){const s=this.all.get(e);return s?s.push(t):this.all.set(e,[t]),()=>{this.off(e,t)}}off(e,t){const s=this.all.get(e);s&&(t?s.splice(s.indexOf(t)>>>0,1):this.all.set(e,[]))}emit(e,t){let s=this.all.get(e);s&&s.slice().map(e=>{e(t)}),s=this.all.get("*"),s&&s.slice().map(s=>{s(e,t)})}once(e,t){if("*"===e){const s=(e,i)=>{t(e,i),this.off("*",s)};this.on(e,s)}else{const s=i=>{t(i),this.off(e,s)};this.on(e,s)}}}class R extends C{url;logger;ws;destroyed;pingTimeout;pongTimeout;reConnectTimeout;constructor(e){super(),this.url=e.url,this.logger=e.logger,this.ws=null,this.destroyed=!1,this.pingTimeout=null,this.pongTimeout=null,this.reConnectTimeout=null,this.reConnect=this.reConnect.bind(this),window.addEventListener("online",this.reConnect),this.connect()}disconnect(){this.ws&&(this.ws.onopen=null,this.ws.onerror=null,this.ws.onmessage=null,this.ws.onclose=null,this.ws.close(),this.ws=null),null!==this.pingTimeout&&(clearTimeout(this.pingTimeout),this.pingTimeout=null),null!==this.pongTimeout&&(clearTimeout(this.pongTimeout),this.pongTimeout=null),null!==this.reConnectTimeout&&(clearTimeout(this.reConnectTimeout),this.reConnectTimeout=null)}connect(){if(this.ws)return void this.logger.warn("ReConnectWebsocket try connect but already connected");const e=new WebSocket(this.url);this.ws=e,e.onopen=()=>{this.delaySendPing()},e.onerror=e=>{this.logger.info("ReConnectWebsocket error",e)},e.onmessage=e=>{try{const t=JSON.parse(e.data);if(t.type===s.PONG_MESSAGE?null!==this.pongTimeout&&(clearTimeout(this.pongTimeout),this.pongTimeout=null,this.delaySendPing()):this.sendAck(t.id),t.type===s.BIG_PACKAGE_PUSH)for(const e of t.content.big_push)this.emit("message",e);else this.emit("message",t)}catch(e){this.logger.error("ReConnectWebsocket message parse failed",e)}},e.onclose=e=>{4001===e.code?this.kickOff():this.reConnect()}}reConnect(){this.disconnect(),this.destroyed||(this.reConnectTimeout=setTimeout(()=>{this.reConnectTimeout=null,this.connect()},5e3))}send(e){if(this.destroyed)return!1;if(!this.ws)return this.logger.warn("ReConnectWebsocket send before connect"),!1;try{return this.ws.readyState!==this.ws.OPEN?(this.logger.warn("ReConnectWebsocket send but not open",this.ws.readyState),!1):(this.ws.send(JSON.stringify(e)),!0)}catch(e){return this.logger.warn("ReConnectWebsocket send error",e),!1}}sendAck(e){this.send({type:1,ids:[e]})}destroy(){this.destroyed||(this.destroyed=!0,this.disconnect(),window.removeEventListener("online",this.reConnect))}delaySendPing(){null!==this.pingTimeout&&(clearTimeout(this.pingTimeout),this.pingTimeout=null),this.pingTimeout=setTimeout(()=>{this.pingTimeout=null;this.send({type:0})?this.waitPong():this.reConnect()},2e4)}waitPong(){this.pongTimeout=setTimeout(()=>{this.logger.info("ReConnectWebsocket pong timeout"),this.reConnect()},1e4)}kickOff(){this.destroy(),this.emit("kickoff")}}class D extends C{host;context;logger;ws;constructor(e){super(),this.host=e.host,this.context=e.context,this.logger=e.logger,this.ws=null}connect(){if(!this.context.isLogin)return void this.logger.warn("BusinessWebsocket try connect but not login");if(this.ws)return void this.logger.warn("BusinessWebsocket try connect but already connected");const e=`${this.host}/ws?token=${this.context.loginUser.sessionId}&platform=web`;this.ws=new R({url:e,logger:this.logger}),this.ws.on("message",e=>{this.emit("message",e)}),this.ws.on("kickoff",()=>{this.emit("kickoff")})}disconnect(){this.ws?(this.ws.destroy(),this.ws=null):this.logger.info("BusinessWebsocket already disconnected")}}class y{_isSyncCompleted;map;chatMap;list;waitMap;constructor(){this._isSyncCompleted=!1,this.map=new Map,this.chatMap=new Map,this.waitMap=new Map,this.list=[]}getAll(){const e=[];for(const t of this.list){const s=this.map.get(t);s&&e.push(s)}return e}get(e){return this.map.get(e)??null}getByChatId(e){const t=this.chatMap.get(e);return void 0!==t?this.get(t):null}async waitFor(e){const t=this.get(e);if(t)return t;if(this._isSyncCompleted)return null;let s=this.waitMap.get(e);return s||(s=(()=>{let e=()=>{};return{promise:new Promise(t=>{e=t}),resolve:e}})(),this.waitMap.set(e,s)),await s.promise,this.get(e)}resolveWait(e){const t=this.waitMap.get(e);t&&(t.resolve(!0),this.waitMap.delete(e))}remove(e){const t=this.map.get(e);return!!t&&(this.map.delete(e),this.chatMap.delete(t.chat_id),this.list.splice(this.list.indexOf(e),1),!0)}push(e){this.remove(e.id),this.map.set(e.id,e),this.list.push(e.id),this.chatMap.set(e.chat_id,e.id),this.resolveWait(e.id)}unshift(e){this.remove(e.id),this.map.set(e.id,e),this.list.unshift(e.id),this.chatMap.set(e.chat_id,e.id),this.resolveWait(e.id)}update(e){this.map.has(e.id)&&this.map.set(e.id,e)}markSyncComplete(){this._isSyncCompleted=!0;for(const[e,t]of this.waitMap)t.resolve(!0);this.waitMap.clear()}isSyncCompleted(){return this._isSyncCompleted}reset(){this._isSyncCompleted=!1,this.map.clear(),this.chatMap.clear(),this.waitMap.clear(),this.list=[]}}function w(e){return(new TextEncoder).encode(e).buffer}async function L(e,t){return window.crypto&&window.crypto.subtle?await async function(e,t){const s=w(t),i=w(e),n=await crypto.subtle.importKey("raw",s,{name:"HMAC",hash:{name:"SHA-256"}},!1,["sign"]),o=await crypto.subtle.sign("HMAC",n,i);return Array.from(new Uint8Array(o)).map(e=>e.toString(16).padStart(2,"0")).join("")}(e,t):function(e,t){return E.HmacSHA256(e,t).toString(E.enc.Hex)}(e,t)}const P=e=>{if(null==e)return"";if("object"!=typeof e||Array.isArray(e)){if(Array.isArray(e)){let t="";for(const s of e)t+=P(s);return t}return"string"==typeof e?e:String(e)}{const t=Object.keys(e).sort();let s="";for(const i of t)s+=P(e[i]);return s}};function O(){return S(t.MAKE_SIGN_FAIL,"make sign fail")}class I{api;context;logger;constructor(e){this.api=_.create({baseURL:e.host,headers:{"x-developer":e.developer}}),this.context=e.context,this.logger=e.logger}async request(e){try{const{needSign:s=!0,...i}=e;if(i.headers||(i.headers={}),i.headers.platform="web",s&&"POST"===e.method?.toUpperCase()&&i.data){if(!this.context.isLogin)return this.logger.warn("HttpClient needs make sign but not login"),O();try{const e=await(async(e,t)=>{const s=await P(e);return await L(s,t)})(i.data,this.context.loginUser.secretKey);i.headers.sign=e}catch(e){return this.logger.error("HttpClient make sign error",e),O()}}const n=await this.api(i),o=n.data;return o.code===t.SUCCESS?{ok:!0,data:o.data}:(this.logger.info("HttpClient server error",n),{ok:!1,code:o.code,msg:o.msg,msg_i18n:o.msg_i18n})}catch{return{ok:!1,code:t.NETWORK_ERROR,msg:"network error"}}}get(e,t,s){return this.request({url:e,method:"GET",params:t,...s})}post(e,t,s){return this.request({url:e,method:"POST",data:t,...s})}}class U{level;logHandler;constructor(){this.level=l.error,this.logHandler=this.createLogHandler()}getPriority(e){switch(e){case l.error:return 3;case l.warning:return 2;case l.info:return 1;default:return 0}}createLogHandler(){return(e,t,...s)=>{const i=`[tlog] ${(new Date).toISOString().split("T")[0]}`,n=t?`${i} ${t}`:i;switch(e){case l.error:console.error(n,...s);break;case l.warning:console.warn(n,...s);break;case l.info:console.info(n,...s);break;default:console.log(n,...s)}}}log(e,t,...s){this.getPriority(e)>=this.getPriority(this.level)&&this.logHandler(e,t,...s)}setLogLevel(e){this.level=e,this.logHandler=this.createLogHandler()}info(e,...t){this.log(l.info,e,...t)}warn(e,...t){this.log(l.warning,e,...t)}error(e,...t){this.log(l.error,e,...t)}}class M{context;logger;emitter;feedCache;handleKickoff;constructor(e){this.context=e.context,this.logger=e.logger,this.emitter=e.emitter,this.feedCache=e.feedCache,this.handleKickoff=e.onKickoff}emitFeedListUpdated(){this.emitter.emit(u.FEED_LIST_UPDATED,{list:this.feedCache.getAll(),is_sync_completed:this.feedCache.isSyncCompleted()})}processCreateFeed(e){this.feedCache.unshift(e.content),this.emitFeedListUpdated()}processDeleteFeed(e){this.feedCache.remove(e.content.id)&&this.emitFeedListUpdated()}processAddMessage(e){if(!this.context.isLogin)return;const t=this.context.loginUser.userId,s=e.content,i=this.feedCache.getByChatId(s.chat_id);if(null!==i){let e=!1;const o={};if(s.message_kind_id!==n.system&&s.sender_id!==t){e=!0;const t=new Set(i.unread_message_ids);t.add(s.id),o.unread_message_ids=[...t],o.badge=t.size}i.last_message_position<s.position&&(e=!0,o.last_message_position=s.position,o.last_message_send_at=s.send_at,o.last_message=s.content,o.last_message_kind_id=s.message_kind_id,o.last_message_type_id=s.message_type_id),e&&(this.feedCache.unshift({...i,...o}),this.emitFeedListUpdated()),this.emitter.emit(u.MESSAGE_RECEIVED,s)}}processUpdateMessage(e){this.emitter.emit(u.MESSAGE_UPDATED,e.content)}processPushLiveMessage(e){this.emitter.emit(u.ROOM_MESSAGE_RECEIVED,e.content)}processReadMessage(e){if(!this.context.isLogin)return;const t=this.context.loginUser.userId,s=this.feedCache.getByChatId(e.content.chat_id);if(null!==s){let i=!1;const n={};if(e.content.reader_ids.includes(t)&&e.content.message_ids.length>0){i=!0;const t=new Set(s.unread_message_ids);e.content.message_ids.forEach(e=>{t.delete(e)}),n.unread_message_ids=[...t],n.badge=t.size}i&&(this.feedCache.update({...s,...n}),this.emitFeedListUpdated()),this.emitter.emit(u.MESSAGE_RECEIPT_RECEIVED,{feed_id:s.id,message_ids:e.content.message_ids,reader_user_ids:e.content.reader_ids})}}processKickOutDevice(){this.handleKickoff()}process(e){switch(this.logger.info("PushMessageProcessor process",e),e.type){case s.CREATE_FEED:this.processCreateFeed(e);break;case s.DELETE_FEED:this.processDeleteFeed(e);break;case s.ADD_MESSAGE:this.processAddMessage(e);break;case s.UPDATE_MESSAGE:this.processUpdateMessage(e);break;case s.PUSH_LIVE_MESSAGE:this.processPushLiveMessage(e);break;case s.READ_MESSAGE:this.processReadMessage(e);break;case s.KICK_OUT_DEVICE:this.processKickOutDevice()}}}var v=e=>new Promise(t=>setTimeout(t,e));class k{destroyed;promise;constructor(){this.destroyed=!1;let e=()=>{};const t=new Promise(t=>{e=t});this.promise=t,navigator.locks.request("typex-sdk",{ifAvailable:!0},async t=>{if(t)for(e(!0);!this.destroyed;)await v(1e4);else e(!1)})}async request(){return await this.promise}}class G{static key="__typex_sdk_data__";static set(e){localStorage.setItem(this.key,JSON.stringify(e))}static get(){try{const e=localStorage.getItem(this.key);if(!e)return null;return JSON.parse(e)}catch{return null}}static clear(){localStorage.removeItem(this.key)}}class x{version;constructor(){this.version=0}increment(){this.version++}get(){return this.version}check(e){return e===this.version}}class N extends C{context;version;singletonLock;logger;httpClient;feedCache;ws;pushMessageProcessor;constructor(e){super(),this.context={isLogin:!1,isReady:!1,deviceId:null},this.version=new x,this.singletonLock=new k,this.logger=new U,this.httpClient=new I({host:e.httpHost,context:this.context,logger:this.logger,developer:e.developer}),this.feedCache=new y,this.ws=new D({host:e.websocketHost,context:this.context,logger:this.logger}),this.pushMessageProcessor=new M({context:this.context,logger:this.logger,emitter:this,feedCache:this.feedCache,onKickoff:()=>{this.handleKickoff()}}),this.init()}init(){this.loadContext(),this.ws.on("message",e=>{this.pushMessageProcessor.process(e)}),this.ws.on("kickoff",()=>{this.handleKickoff()})}async requestDeviceId(){if(this.context.deviceId)return!0;const e=await this.httpClient.post("/device_api/device_id",{platform:"web",hash_str:""},{needSign:!1});return!!e.ok&&(this.context.deviceId=e.data,this.saveContext(),!0)}saveContext(){const{context:e}=this;G.set({deviceId:e.deviceId})}loadContext(){const e=G.get();e&&e.deviceId&&(this.context.deviceId=e.deviceId)}applyLoginStatus(){this.version.increment(),this.saveContext(),this.context.isLogin?(this.loadFeedList(),this.ws.connect(),this.context.isReady=!0,this.emit(u.SDK_READY)):(this.ws.disconnect(),this.feedCache.reset(),this.context.isReady=!1,this.emit(u.SDK_NOT_READY))}async loadFeedList(){const e=this.version.get(),t={refresh_at:null,feed_id:null};for(;;){const s=await this.httpClient.get("/feed",{limit:500,last_refresh_at:t.refresh_at??void 0,last_feed_id:t.feed_id??void 0});if(!this.version.check(e))break;if(!s.ok)break;if(t.refresh_at=s.data.refresh_at,t.feed_id=s.data.last_feed_id,s.data.feed_list.forEach(e=>{this.feedCache.push(e)}),!s.data.has_more)break}this.version.check(e)&&(this.feedCache.markSyncComplete(),this.emit(u.FEED_LIST_UPDATED,{list:this.feedCache.getAll(),is_sync_completed:!0}))}isReady(){return this.context.isReady}setLogLevel(e){this.logger.setLogLevel(e)}async login(e){if(!await this.singletonLock.request())return this.logger.error("Another active TypeX SDK instance detected"),S(t.REQUEST_SINGLETON_LOCK_FAIL,"Another active TypeX SDK instance detected");if(await this.requestDeviceId(),!this.context.deviceId)return this.logger.error("Get deviceId failed"),S(t.SDK_ERROR,"Login params invalid");const s=await this.httpClient.post("/user/session/login",{userCode:e.user_code,device_id:this.context.deviceId},{needSign:!1});return s.ok?(this.context.isLogin=!0,this.context.isLogin&&(this.context.loginUser={userId:s.data.id,secretKey:s.data.key,sessionId:d.get("sessionid")??""}),this.applyLoginStatus(),T(null)):s}async logout(){const e=await this.httpClient.get("/user/logout");return e.ok?(this.context.isLogin=!1,this.applyLoginStatus(),T(null)):e}async handleKickoff(){this.context.isLogin=!1,this.applyLoginStatus()}getLoginUser(){return this.context.isLogin?this.context.loginUser.userId:null}async createSingleChatFeed(e){return await this.httpClient.post("/chat/single",{receiver_id:e})}async deleteFeed(e){return await this.httpClient.post("/feed/delete",{feed_id_list:[e],delete_peer:!0})}async getFeedList(){return T({list:this.feedCache.getAll(),is_sync_completed:this.feedCache.isSyncCompleted()})}async getFeedDetail(e){return T(await this.feedCache.waitFor(e))}async getFeedMessageList(e){const t=await this.getFeedDetail(e.feed_id);if(t.ok&&t.data){const s=t.data,{chat_id:i,first_position:n,last_message_position:o}=s,r=(e=>{const t=[];let s=e.lastPosition??e.maxPosition;for(;t.length<e.limit&&s>=e.minPosition;)t.push(s),s--;return t})({maxPosition:o,minPosition:n,lastPosition:e.last_message_position,limit:e.limit});if(r.length>0){const e=await this.httpClient.post("/message/get_by_position",{chat_position:{[i]:r}},{needSign:!1});if(e.ok&&e.data[i]){const t=e.data[i].reverse();return T({has_more:r[0]===n,list:t,last_message_position:t[0]?.position})}}}return T({has_more:!1,list:[]})}createCustomMessage(e){if(!this.context.isLogin)return S(t.SDK_ERROR,"not login");const s=this.feedCache.get(e.feed_id);if(null===s)return S(t.SDK_ERROR,"not found feed");var i;return T({id:"",chat_id:s.chat_id,position:s.last_message_position,sender_id:this.context.loginUser.userId,message_kind_id:n.text,message_type_id:o.custom,send_at:"",reader_ids:[],hide_ids:[],operation:c.normal,operation_user_id:"0",content:(i={client_msg_id:e.client_msg_id,content:e.content},JSON.stringify({client_msg_id:i.client_msg_id,content:JSON.stringify(i.content)}))})}async sendMessageToFeed(e){if(e.message_type_id===o.custom){const t=f(e.content);return this.httpClient.post("/message/send_custom",{chat_id:e.chat_id,client_id:t.client_msg_id,content:null==t.content?void 0:JSON.stringify(t.content)})}return S(t.SDK_ERROR,"not support")}async sendMessageReadReceipt(e){const s=this.feedCache.get(e.feed_id);return null===s?S(t.SDK_ERROR,"not found feed"):await this.httpClient.post("/message/read",{chat_id:s.chat_id,message_ids:e.message_ids})}async joinRoom(e){return await this.httpClient.post("/liveshow/join",{room_id:e})}async leaveRoom(e){return await this.httpClient.post("/liveshow/leave",{room_id:e})}async getRoomRecentMessageList(e){const t=await this.httpClient.get("/liveshow/get_recent_message",{room_id:e});return t.ok?T(t.data):t}async sendMessageToRoom(e){return await this.httpClient.post("/liveshow/send_message",{room_id:e.room_id,content:e.content})}}const F={parseSystemMessageContent:e=>{const t=A(e);return null!==t?{text:t.text,text_i18n:t.text_i18n}:{}},parseTextMessageContent:e=>{const t=A(e);return null!==t?{text:t.text,client_msg_id:t.client_msg_id}:{}},parseCustomMessageContent:f};exports.ChatSDK=N,exports.create=function(e){return new N(e)},exports.enums=g,exports.events=p,exports.interfaces=m,exports.utils=F;
1
+ "use strict";var e,t,s,i,n,o,r,a,c,l,h,d=require("js-cookie"),_=require("axios"),E=require("crypto-js");!function(e){e[e.yes=1]="yes",e[e.no=0]="no"}(e||(e={})),function(e){e[e.CREATE_FEED=0]="CREATE_FEED",e[e.UPDATE_FEED=1]="UPDATE_FEED",e[e.ADD_MESSAGE=2]="ADD_MESSAGE",e[e.UPDATE_MESSAGE=3]="UPDATE_MESSAGE",e[e.UPDATE_USER_DATA=4]="UPDATE_USER_DATA",e[e.USER_LOGIN=5]="USER_LOGIN",e[e.READ_MESSAGE=6]="READ_MESSAGE",e[e.PONG_MESSAGE=8]="PONG_MESSAGE",e[e.DELETE_MESSAGE=9]="DELETE_MESSAGE",e[e.UPDATE_GROUP=12]="UPDATE_GROUP",e[e.DELETE_GROUP=13]="DELETE_GROUP",e[e.UPDATE_LOGIN_USER=17]="UPDATE_LOGIN_USER",e[e.UPDATE_MESSAGE_REACTION=19]="UPDATE_MESSAGE_REACTION",e[e.ADD_GROUP_MEMBER=21]="ADD_GROUP_MEMBER",e[e.DEL_GROUP_MEMBER=22]="DEL_GROUP_MEMBER",e[e.ADD_CONTACTS=23]="ADD_CONTACTS",e[e.UPDATE_CONTACTS=24]="UPDATE_CONTACTS",e[e.DEL_CONTACTS=25]="DEL_CONTACTS",e[e.BLOCK_USER=26]="BLOCK_USER",e[e.DEVICE_CODE=27]="DEVICE_CODE",e[e.MSG_OPEN_CLOCK=28]="MSG_OPEN_CLOCK",e[e.DELETE_SECRET_CHAT=29]="DELETE_SECRET_CHAT",e[e.DELETE_FEED=30]="DELETE_FEED",e[e.FAVORITE_UPDATE=31]="FAVORITE_UPDATE",e[e.STICKERS_UPDATE=32]="STICKERS_UPDATE",e[e.CUSTOM_STICKERS_UPDATE=33]="CUSTOM_STICKERS_UPDATE",e[e.MANAGE_DEVICE_UPDATE=34]="MANAGE_DEVICE_UPDATE",e[e.MASS_MESSAGE_UPDATE=35]="MASS_MESSAGE_UPDATE",e[e.CHAT_WALLPAPER_DELETE=36]="CHAT_WALLPAPER_DELETE",e[e.CHAT_WALLPAPER_ADD=37]="CHAT_WALLPAPER_ADD",e[e.CONTACT_REQUEST=40]="CONTACT_REQUEST",e[e.USER_PREFERENCE_CHANGE=38]="USER_PREFERENCE_CHANGE",e[e.UPDATE_GROUP_PIN=41]="UPDATE_GROUP_PIN",e[e.RTC_CANCEL=46]="RTC_CANCEL",e[e.ADD_FEED_FOLDER=43]="ADD_FEED_FOLDER",e[e.UPDATE_FEED_FOLDER=44]="UPDATE_FEED_FOLDER",e[e.UPDATE_FOLDER_FEED_LIST=47]="UPDATE_FOLDER_FEED_LIST",e[e.DELETE_FEED_FOLDER=45]="DELETE_FEED_FOLDER",e[e.UPDATE_CHATTER=42]="UPDATE_CHATTER",e[e.PAPER_KEY=48]="PAPER_KEY",e[e.PAPER_KEY_LOG=49]="PAPER_KEY_LOG",e[e.USER_LOGIN_SCAN=50]="USER_LOGIN_SCAN",e[e.RTC_RINGING=51]="RTC_RINGING",e[e.ON_UPDATE_PRIVATE_SETTING=52]="ON_UPDATE_PRIVATE_SETTING",e[e.ON_DELETE_PRIVATE_SETTING=53]="ON_DELETE_PRIVATE_SETTING",e[e.ON_UPDATE_LOGIN_PRIVATE_SETTING=54]="ON_UPDATE_LOGIN_PRIVATE_SETTING",e[e.PUSH_LIVE_MESSAGE=56]="PUSH_LIVE_MESSAGE",e[e.BIG_PACKAGE_PUSH=126]="BIG_PACKAGE_PUSH",e[e.KICK_OUT_DEVICE=127]="KICK_OUT_DEVICE",e[e.CLEAR_HISTORY=128]="CLEAR_HISTORY",e[e.CLEAR_DATA=255]="CLEAR_DATA"}(t||(t={})),function(e){e[e.singleChat=1]="singleChat",e[e.groupChat=2]="groupChat"}(s||(s={})),function(e){e[e.text=0]="text",e[e.system=1]="system"}(i||(i={})),function(e){e[e.text=0]="text",e[e.image=1]="image",e[e.card=2]="card",e[e.audio=3]="audio",e[e.video=4]="video",e[e.file=5]="file",e[e.mergeForward=6]="mergeForward",e[e.sticker=7]="sticker",e[e.richText=8]="richText",e[e.custom=9]="custom"}(n||(n={})),function(e){e[e.success=0]="success",e[e.sending=1]="sending",e[e.failed=2]="failed"}(o||(o={})),function(e){e[e.Owner=1]="Owner",e[e.Admin=2]="Admin",e[e.Common=3]="Common",e[e.Bot=4]="Bot"}(r||(r={})),function(e){e[e.normal=0]="normal",e[e.recall=1]="recall"}(a||(a={})),function(e){e[e.private=1]="private",e[e.public=0]="public",e[e.anonymous=2]="anonymous"}(c||(c={})),function(e){e.error="ERROR",e.warning="WARNING",e.info="INFO"}(l||(l={})),function(e){e[e.SDK_ERROR=-1e4]="SDK_ERROR",e[e.NETWORK_ERROR=-9999]="NETWORK_ERROR",e[e.MAKE_SIGN_FAIL=-9998]="MAKE_SIGN_FAIL",e[e.REQUEST_SINGLETON_LOCK_FAIL=-9997]="REQUEST_SINGLETON_LOCK_FAIL",e[e.PARAMS_INVALID=-9996]="PARAMS_INVALID",e[e.SUCCESS=0]="SUCCESS"}(h||(h={}));var g=Object.freeze({__proto__:null,get Boolean(){return e},get ChatType(){return s},get GroupRole(){return r},get GroupType(){return c},get HttpClientResponseCode(){return h},get LogLevel(){return l},get MessageKind(){return i},get MessageOperation(){return a},get MessageSendStatus(){return o},get MessageType(){return n},get PushMessageType(){return t}});const u={SDK_READY:"sdkReady",SDK_NOT_READY:"sdkNotReady",FEED_LIST_UPDATED:"feedListUpdated",MESSAGE_RECEIVED:"messageReceived",MESSAGE_UPDATED:"messageUpdated",MESSAGE_RECEIPT_RECEIVED:"messageReceiptReceived",ROOM_MESSAGE_RECEIVED:"roomMessageReceived"};var p=Object.freeze({__proto__:null,CHAT_SDK_EVENT:u}),m=Object.freeze({__proto__:null});function S(e,t){return{ok:!1,code:e,msg:t}}function f(e){return{ok:!0,data:e}}const T=e=>{try{return null==e||"string"!=typeof e||0===e.length?null:JSON.parse(e)}catch{return null}},A=e=>{const t=T(e);if(null!==t){let e;return null!=t.content&&(e=T(t.content)??void 0),{client_msg_id:t.client_msg_id,content:e}}return{}};class C{all;constructor(e){this.all=e||new Map}on(e,t){const s=this.all.get(e);return s?s.push(t):this.all.set(e,[t]),()=>{this.off(e,t)}}off(e,t){const s=this.all.get(e);s&&(t?s.splice(s.indexOf(t)>>>0,1):this.all.set(e,[]))}emit(e,t){let s=this.all.get(e);s&&s.slice().map(e=>{e(t)}),s=this.all.get("*"),s&&s.slice().map(s=>{s(e,t)})}once(e,t){if("*"===e){const s=(e,i)=>{t(e,i),this.off("*",s)};this.on(e,s)}else{const s=i=>{t(i),this.off(e,s)};this.on(e,s)}}}class R extends C{url;logger;ws;destroyed;pingTimeout;pongTimeout;reConnectTimeout;constructor(e){super(),this.url=e.url,this.logger=e.logger,this.ws=null,this.destroyed=!1,this.pingTimeout=null,this.pongTimeout=null,this.reConnectTimeout=null,this.reConnect=this.reConnect.bind(this),window.addEventListener("online",this.reConnect),this.connect()}disconnect(){this.ws&&(this.ws.onopen=null,this.ws.onerror=null,this.ws.onmessage=null,this.ws.onclose=null,this.ws.close(),this.ws=null),null!==this.pingTimeout&&(clearTimeout(this.pingTimeout),this.pingTimeout=null),null!==this.pongTimeout&&(clearTimeout(this.pongTimeout),this.pongTimeout=null),null!==this.reConnectTimeout&&(clearTimeout(this.reConnectTimeout),this.reConnectTimeout=null)}connect(){if(this.ws)return void this.logger.warn("ReConnectWebsocket try connect but already connected");const e=new WebSocket(this.url);this.ws=e,e.onopen=()=>{this.delaySendPing()},e.onerror=e=>{this.logger.info("ReConnectWebsocket error",e)},e.onmessage=e=>{try{const s=JSON.parse(e.data);if(s.type===t.PONG_MESSAGE?null!==this.pongTimeout&&(clearTimeout(this.pongTimeout),this.pongTimeout=null,this.delaySendPing()):this.sendAck(s.id),s.type===t.BIG_PACKAGE_PUSH)for(const e of s.content.big_push)this.emit("message",e);else this.emit("message",s)}catch(e){this.logger.error("ReConnectWebsocket message parse failed",e)}},e.onclose=e=>{4001===e.code?this.kickOff():this.reConnect()}}reConnect(){this.disconnect(),this.destroyed||(this.reConnectTimeout=setTimeout(()=>{this.reConnectTimeout=null,this.connect()},5e3))}send(e){if(this.destroyed)return!1;if(!this.ws)return this.logger.warn("ReConnectWebsocket send before connect"),!1;try{return this.ws.readyState!==this.ws.OPEN?(this.logger.warn("ReConnectWebsocket send but not open",this.ws.readyState),!1):(this.ws.send(JSON.stringify(e)),!0)}catch(e){return this.logger.warn("ReConnectWebsocket send error",e),!1}}sendAck(e){this.send({type:1,ids:[e]})}destroy(){this.destroyed||(this.destroyed=!0,this.disconnect(),window.removeEventListener("online",this.reConnect))}delaySendPing(){null!==this.pingTimeout&&(clearTimeout(this.pingTimeout),this.pingTimeout=null),this.pingTimeout=setTimeout(()=>{this.pingTimeout=null;this.send({type:0})?this.waitPong():this.reConnect()},2e4)}waitPong(){this.pongTimeout=setTimeout(()=>{this.logger.info("ReConnectWebsocket pong timeout"),this.reConnect()},1e4)}kickOff(){this.destroy(),this.emit("kickoff")}}class D extends C{host;context;logger;ws;constructor(e){super(),this.host=e.host,this.context=e.context,this.logger=e.logger,this.ws=null}connect(){if(null===this.context.loginUser)return void this.logger.warn("BusinessWebsocket try connect but not login");if(this.ws)return void this.logger.warn("BusinessWebsocket try connect but already connected");const e=`${this.host}/ws?token=${this.context.loginUser.sessionId}&platform=web`;this.ws=new R({url:e,logger:this.logger}),this.ws.on("message",e=>{this.emit("message",e)}),this.ws.on("kickoff",()=>{this.emit("kickoff")})}disconnect(){this.ws?(this.ws.destroy(),this.ws=null):this.logger.info("BusinessWebsocket already disconnected")}}class L{_isSyncCompleted;map;chatMap;list;waitMap;constructor(){this._isSyncCompleted=!1,this.map=new Map,this.chatMap=new Map,this.waitMap=new Map,this.list=[]}getAll(){const e=[];for(const t of this.list){const s=this.map.get(t);s&&e.push(s)}return e}get(e){return this.map.get(e)??null}getByChatId(e){const t=this.chatMap.get(e);return void 0!==t?this.get(t):null}async waitFor(e){const t=this.get(e);if(t)return t;if(this._isSyncCompleted)return null;let s=this.waitMap.get(e);return s||(s=(()=>{let e=()=>{};return{promise:new Promise(t=>{e=t}),resolve:e}})(),this.waitMap.set(e,s)),await s.promise,this.get(e)}resolveWait(e){const t=this.waitMap.get(e);t&&(t.resolve(!0),this.waitMap.delete(e))}remove(e){const t=this.map.get(e);return!!t&&(this.map.delete(e),this.chatMap.delete(t.chat_id),this.list.splice(this.list.indexOf(e),1),!0)}push(e){this.remove(e.id),this.map.set(e.id,e),this.list.push(e.id),this.chatMap.set(e.chat_id,e.id),this.resolveWait(e.id)}unshift(e){this.remove(e.id),this.map.set(e.id,e),this.list.unshift(e.id),this.chatMap.set(e.chat_id,e.id),this.resolveWait(e.id)}update(e){this.map.has(e.id)&&this.map.set(e.id,e)}markSyncComplete(){this._isSyncCompleted=!0;for(const[e,t]of this.waitMap)t.resolve(!0);this.waitMap.clear()}isSyncCompleted(){return this._isSyncCompleted}reset(){this._isSyncCompleted=!1,this.map.clear(),this.chatMap.clear(),this.waitMap.clear(),this.list=[]}}function y(e){return(new TextEncoder).encode(e).buffer}async function w(e,t){return window.crypto&&window.crypto.subtle?await async function(e,t){const s=y(t),i=y(e),n=await crypto.subtle.importKey("raw",s,{name:"HMAC",hash:{name:"SHA-256"}},!1,["sign"]),o=await crypto.subtle.sign("HMAC",n,i);return Array.from(new Uint8Array(o)).map(e=>e.toString(16).padStart(2,"0")).join("")}(e,t):function(e,t){return E.HmacSHA256(e,t).toString(E.enc.Hex)}(e,t)}const P=e=>{if(null==e)return"";if("object"!=typeof e||Array.isArray(e)){if(Array.isArray(e)){let t="";for(const s of e)t+=P(s);return t}return"string"==typeof e?e:String(e)}{const t=Object.keys(e).sort();let s="";for(const i of t)s+=P(e[i]);return s}};function O(){return S(h.MAKE_SIGN_FAIL,"make sign fail")}class I{api;context;logger;constructor(e){this.api=_.create({baseURL:e.host,headers:{"x-developer":e.developer}}),this.context=e.context,this.logger=e.logger}async request(e){try{const{needSign:t=!0,...s}=e;if(s.headers||(s.headers={}),s.headers.platform="web",t&&"POST"===e.method?.toUpperCase()&&s.data){if(null===this.context.loginUser)return this.logger.warn("HttpClient needs make sign but not login"),O();try{const e=await(async(e,t)=>{const s=await P(e);return await w(s,t)})(s.data,this.context.loginUser.secretKey);s.headers.sign=e}catch(e){return this.logger.error("HttpClient make sign error",e),O()}}const i=await this.api(s),n=i.data;return n.code===h.SUCCESS?{ok:!0,data:n.data}:(this.logger.info("HttpClient server error",i),{ok:!1,code:n.code,msg:n.msg,msg_i18n:n.msg_i18n})}catch{return{ok:!1,code:h.NETWORK_ERROR,msg:"network error"}}}get(e,t,s){return this.request({url:e,method:"GET",params:t,...s})}post(e,t,s){return this.request({url:e,method:"POST",data:t,...s})}}class U{level;logHandler;constructor(){this.level=l.error,this.logHandler=this.createLogHandler()}getPriority(e){switch(e){case l.error:return 3;case l.warning:return 2;case l.info:return 1;default:return 0}}createLogHandler(){return(e,t,...s)=>{const i=`[tlog] ${(new Date).toISOString().split("T")[0]}`,n=t?`${i} ${t}`:i;switch(e){case l.error:console.error(n,...s);break;case l.warning:console.warn(n,...s);break;case l.info:console.info(n,...s);break;default:console.log(n,...s)}}}log(e,t,...s){this.getPriority(e)>=this.getPriority(this.level)&&this.logHandler(e,t,...s)}setLogLevel(e){this.level=e,this.logHandler=this.createLogHandler()}info(e,...t){this.log(l.info,e,...t)}warn(e,...t){this.log(l.warning,e,...t)}error(e,...t){this.log(l.error,e,...t)}}class k{context;logger;emitter;feedCache;handleKickoff;constructor(e){this.context=e.context,this.logger=e.logger,this.emitter=e.emitter,this.feedCache=e.feedCache,this.handleKickoff=e.onKickoff}emitFeedListUpdated(){this.emitter.emit(u.FEED_LIST_UPDATED,{list:this.feedCache.getAll(),is_sync_completed:this.feedCache.isSyncCompleted()})}processCreateFeed(e){this.feedCache.unshift(e.content),this.emitFeedListUpdated()}processDeleteFeed(e){this.feedCache.remove(e.content.id)&&this.emitFeedListUpdated()}processAddMessage(e){if(null===this.context.loginUser)return;const t=this.context.loginUser.userId,s=e.content,n=this.feedCache.getByChatId(s.chat_id);if(null!==n){let e=!1;const o={};if(s.message_kind_id!==i.system&&s.sender_id!==t){e=!0;const t=new Set(n.unread_message_ids);t.add(s.id),o.unread_message_ids=[...t],o.badge=t.size}n.last_message_position<s.position&&(e=!0,o.last_message_position=s.position,o.last_message_send_at=s.send_at,o.last_message=s.content,o.last_message_kind_id=s.message_kind_id,o.last_message_type_id=s.message_type_id),e&&(this.feedCache.unshift({...n,...o}),this.emitFeedListUpdated()),this.emitter.emit(u.MESSAGE_RECEIVED,s)}}processUpdateMessage(e){this.emitter.emit(u.MESSAGE_UPDATED,e.content)}processPushLiveMessage(e){this.emitter.emit(u.ROOM_MESSAGE_RECEIVED,e.content)}processReadMessage(e){if(null===this.context.loginUser)return;const t=this.context.loginUser.userId,s=this.feedCache.getByChatId(e.content.chat_id);if(null!==s){let i=!1;const n={};if(e.content.reader_ids.includes(t)&&e.content.message_ids.length>0){i=!0;const t=new Set(s.unread_message_ids);e.content.message_ids.forEach(e=>{t.delete(e)}),n.unread_message_ids=[...t],n.badge=t.size}i&&(this.feedCache.update({...s,...n}),this.emitFeedListUpdated()),this.emitter.emit(u.MESSAGE_RECEIPT_RECEIVED,{feed_id:s.id,message_ids:e.content.message_ids,reader_user_ids:e.content.reader_ids})}}processKickOutDevice(){this.handleKickoff()}process(e){switch(this.logger.info("PushMessageProcessor process",e),e.type){case t.CREATE_FEED:this.processCreateFeed(e);break;case t.DELETE_FEED:this.processDeleteFeed(e);break;case t.ADD_MESSAGE:this.processAddMessage(e);break;case t.UPDATE_MESSAGE:this.processUpdateMessage(e);break;case t.PUSH_LIVE_MESSAGE:this.processPushLiveMessage(e);break;case t.READ_MESSAGE:this.processReadMessage(e);break;case t.KICK_OUT_DEVICE:this.processKickOutDevice()}}}class M{obtained;abortLockObtain;constructor(){this.obtained=!1,this.abortLockObtain=null}async requestLock(){if(this.obtained)return!0;let e=()=>{};const t=new Promise(t=>{e=t}),s=new Promise(e=>{this.abortLockObtain=e});navigator.locks.request("typex-sdk",{ifAvailable:!0},async t=>{t?(this.obtained=!0,e(!0),await s):e(!1)});return await t}releaseLock(){this.obtained=!1,null!==this.abortLockObtain&&(this.abortLockObtain(),this.abortLockObtain=null)}}class v{static key="__typex_sdk_data__";static set(e){localStorage.setItem(this.key,JSON.stringify(e))}static get(){try{const e=localStorage.getItem(this.key);if(!e)return null;return JSON.parse(e)}catch{return null}}static clear(){localStorage.removeItem(this.key)}}class G{version;constructor(){this.version=0}increment(){this.version++}get(){return this.version}check(e){return e===this.version}}class x extends C{context;version;singletonLock;logger;httpClient;feedCache;ws;pushMessageProcessor;constructor(e){super(),this.context={isLogin:!1,loginUser:null,isReady:!1,deviceId:null},this.version=new G,this.singletonLock=new M,this.logger=new U,this.httpClient=new I({host:e.httpHost,context:this.context,logger:this.logger,developer:e.developer}),this.feedCache=new L,this.ws=new D({host:e.websocketHost,context:this.context,logger:this.logger}),this.pushMessageProcessor=new k({context:this.context,logger:this.logger,emitter:this,feedCache:this.feedCache,onKickoff:()=>{this.afterLogout()}}),this.init()}init(){this.loadContext(),this.ws.on("message",e=>{this.pushMessageProcessor.process(e)}),this.ws.on("kickoff",()=>{this.afterLogout()})}async requestDeviceId(){if(this.context.deviceId)return!0;const e=await this.httpClient.post("/device_api/device_id",{platform:"web",hash_str:""},{needSign:!1});return!!e.ok&&(this.context.deviceId=e.data,this.saveContext(),!0)}saveContext(){const{context:e}=this;v.set({deviceId:e.deviceId})}loadContext(){const e=v.get();e&&e.deviceId&&(this.context.deviceId=e.deviceId)}applyLoginStatus(){this.version.increment(),this.saveContext(),this.context.isLogin?(this.ws.connect(),this.loadFeedList(),this.context.isReady=!0,this.emit(u.SDK_READY)):(this.ws.disconnect(),this.feedCache.reset(),this.singletonLock.releaseLock(),this.context.isReady=!1,this.emit(u.SDK_NOT_READY))}async loadFeedList(){const e=this.version.get(),t={refresh_at:null,feed_id:null};for(;;){const s=await this.httpClient.get("/feed",{limit:500,last_refresh_at:t.refresh_at??void 0,last_feed_id:t.feed_id??void 0});if(!this.version.check(e))break;if(!s.ok)break;if(t.refresh_at=s.data.refresh_at,t.feed_id=s.data.last_feed_id,s.data.feed_list.forEach(e=>{this.feedCache.push(e)}),!s.data.has_more)break}this.version.check(e)&&(this.feedCache.markSyncComplete(),this.emit(u.FEED_LIST_UPDATED,{list:this.feedCache.getAll(),is_sync_completed:!0}))}isReady(){return this.context.isReady}setLogLevel(e){this.logger.setLogLevel(e)}async login(e){if(this.context.isLogin)return f(null);if(!await this.singletonLock.requestLock())return this.logger.error("Another active TypeX SDK instance detected"),S(h.REQUEST_SINGLETON_LOCK_FAIL,"Another active TypeX SDK instance detected");if(await this.requestDeviceId(),!this.context.deviceId)return this.logger.error("Get deviceId failed"),S(h.SDK_ERROR,"Get deviceId failed");const t=await this.httpClient.post("/user/session/login",{userCode:e.user_code,device_id:this.context.deviceId},{needSign:!1});if(!t.ok)return this.singletonLock.releaseLock(),t;const s=d.get("sessionid");return s?(this.context.isLogin=!0,this.context.loginUser={userId:t.data.id,secretKey:t.data.key,sessionId:s},this.applyLoginStatus(),f(null)):(this.singletonLock.releaseLock(),S(h.SDK_ERROR,"Get sessionId failed"))}async logout(){if(!this.context.isLogin)return f(null);const e=await this.httpClient.get("/user/logout");return e.ok?(this.afterLogout(),f(null)):e}async afterLogout(){this.context.isLogin=!1,this.context.loginUser=null,this.applyLoginStatus()}getLoginUser(){return null===this.context.loginUser?null:this.context.loginUser.userId}async createSingleChatFeed(e){return await this.httpClient.post("/chat/single",{receiver_id:e})}async deleteFeed(e){return await this.httpClient.post("/feed/delete",{feed_id_list:[e],delete_peer:!0})}async getFeedList(){return f({list:this.feedCache.getAll(),is_sync_completed:this.feedCache.isSyncCompleted()})}async getFeedDetail(e){return f(await this.feedCache.waitFor(e))}async getFeedMessageList(e){const t=await this.getFeedDetail(e.feed_id);if(t.ok&&t.data){const s=t.data,{chat_id:i,first_position:n,last_message_position:o}=s,r=(e=>{const t=[];let s=e.lastPosition??e.maxPosition;for(;t.length<e.limit&&s>=e.minPosition;)t.push(s),s--;return t})({maxPosition:o,minPosition:n,lastPosition:e.last_message_position,limit:e.limit});if(r.length>0){const e=await this.httpClient.post("/message/get_by_position",{chat_position:{[i]:r}},{needSign:!1});if(e.ok&&e.data[i]){const t=e.data[i].reverse();return f({has_more:r[0]===n,list:t,last_message_position:t[0]?.position})}}}return f({has_more:!1,list:[]})}createCustomMessage(e){if(null===this.context.loginUser)return S(h.SDK_ERROR,"not login");const t=this.feedCache.get(e.feed_id);if(null===t)return S(h.SDK_ERROR,"not found feed");var s;return f({id:"",chat_id:t.chat_id,position:t.last_message_position,sender_id:this.context.loginUser.userId,message_kind_id:i.text,message_type_id:n.custom,send_at:"",reader_ids:[],hide_ids:[],operation:a.normal,operation_user_id:"0",content:(s={client_msg_id:e.client_msg_id,content:e.content},JSON.stringify({client_msg_id:s.client_msg_id,content:JSON.stringify(s.content)}))})}async sendMessageToFeed(e){if(e.message_type_id===n.custom){const t=A(e.content);return this.httpClient.post("/message/send_custom",{chat_id:e.chat_id,client_id:t.client_msg_id,content:null==t.content?void 0:JSON.stringify(t.content)})}return S(h.SDK_ERROR,"not support")}async sendMessageReadReceipt(e){const t=this.feedCache.get(e.feed_id);return null===t?S(h.SDK_ERROR,"not found feed"):await this.httpClient.post("/message/read",{chat_id:t.chat_id,message_ids:e.message_ids})}async joinRoom(e){return await this.httpClient.post("/liveshow/join",{room_id:e})}async leaveRoom(e){return await this.httpClient.post("/liveshow/leave",{room_id:e})}async getRoomRecentMessageList(e){const t=await this.httpClient.get("/liveshow/get_recent_message",{room_id:e});return t.ok?f(t.data):t}async sendMessageToRoom(e){return await this.httpClient.post("/liveshow/send_message",{room_id:e.room_id,content:e.content})}}const N=new Map;const b={parseSystemMessageContent:e=>{const t=T(e);return null!==t?{text:t.text,text_i18n:t.text_i18n}:{}},parseTextMessageContent:e=>{const t=T(e);return null!==t?{text:t.text,client_msg_id:t.client_msg_id}:{}},parseCustomMessageContent:A};exports.ChatSDK=x,exports.create=function(e,t){let s=N.get(e);return void 0===s&&(s=new x(t),N.set(e,s)),s},exports.enums=g,exports.events=p,exports.interfaces=m,exports.utils=b;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typexim/web-sdk",
3
- "version": "0.0.4-alpha.1",
3
+ "version": "0.0.4-alpha.3",
4
4
  "private": false,
5
5
  "repository": "git@github.com:secure-typex/typex-web-sdk.git",
6
6
  "license": "MIT",
package/rollup.config.js CHANGED
@@ -23,6 +23,7 @@ function createConfig() {
23
23
  plugins: [
24
24
  del({
25
25
  targets: 'dist',
26
+ runOnce: true,
26
27
  }),
27
28
  typescript({
28
29
  tsconfig: './tsconfig.json',