easemob-websdk 5.1.2 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/bundle/im-sdk-web.iife.js +1 -1
  2. package/cache/cache-manager.d.ts +11 -3
  3. package/cache/cache-types.d.ts +15 -4
  4. package/chat-client.d.ts +23 -2
  5. package/chunks/chat-manager-BB7wYWYG.js +2 -0
  6. package/chunks/chat-manager-auF_3s3R.cjs +1 -0
  7. package/chunks/common-Cg-lTASK.cjs +1 -0
  8. package/chunks/common-gbNWJrBc.js +1 -0
  9. package/chunks/{event-system-B62vbKqG.js → event-system-DguqwFpc.js} +1 -1
  10. package/chunks/{group-user-info-resolver-B-dyr__b.cjs → group-user-info-resolver-BNwtC7dY.cjs} +1 -1
  11. package/chunks/{group-user-info-resolver-DMccCqez.js → group-user-info-resolver-rs5Fnr0l.js} +1 -1
  12. package/chunks/index-BUNbI8gf.cjs +1 -0
  13. package/chunks/index-BxMCMoWN.js +1 -0
  14. package/chunks/index-C7fO2X0x.cjs +1 -0
  15. package/chunks/index-C_EMU0-3.js +1 -0
  16. package/chunks/index-CsqE2tfH.js +1 -0
  17. package/chunks/index-DeJuR3uW.cjs +1 -0
  18. package/chunks/index-Dknccp9Q.js +1 -0
  19. package/chunks/index-P1vnLSK2.cjs +1 -0
  20. package/chunks/multibody-content-BHb-Au8Q.js +1 -0
  21. package/chunks/multibody-content-BmdQukoX.cjs +1 -0
  22. package/chunks/push-manager-BO3xq-7O.js +1 -0
  23. package/chunks/push-manager-DMI0G9v9.cjs +1 -0
  24. package/chunks/reflection-proto-codec-B1apL0ws.js +1 -0
  25. package/chunks/reflection-proto-codec-CYglS_mx.cjs +1 -0
  26. package/chunks/session-list-query-DMe2GZ4v.cjs +1 -0
  27. package/chunks/session-list-query-Inseic9m.js +1 -0
  28. package/chunks/{user-info-DH3aE1gm.cjs → user-info-B5St7axd.cjs} +1 -1
  29. package/chunks/{user-info-AlshAvdy.js → user-info-BkqJHgja.js} +1 -1
  30. package/core/index.d.ts +1 -1
  31. package/core/message/attachment-downloader.d.ts +4 -1
  32. package/core/message/message-sender.d.ts +19 -4
  33. package/core/message/prepare-text-translation.d.ts +7 -0
  34. package/core/session-list-sync/session-list-message-events.d.ts +9 -0
  35. package/index.cjs +1 -1
  36. package/index.d.ts +3 -2
  37. package/index.js +2 -2
  38. package/managers/chat/index.cjs +1 -1
  39. package/managers/chat/index.js +1 -1
  40. package/managers/chat-manager.d.ts +128 -7
  41. package/managers/chat-thread/index.cjs +1 -1
  42. package/managers/chat-thread/index.js +1 -1
  43. package/managers/chatroom/index.cjs +1 -1
  44. package/managers/chatroom/index.js +1 -1
  45. package/managers/contact/index.cjs +1 -1
  46. package/managers/contact/index.js +1 -1
  47. package/managers/group/index.cjs +1 -1
  48. package/managers/group/index.js +1 -1
  49. package/managers/presence/index.cjs +1 -1
  50. package/managers/presence/index.js +1 -1
  51. package/managers/push/index.cjs +1 -1
  52. package/managers/push/index.js +1 -1
  53. package/managers/user-info/index.cjs +1 -1
  54. package/managers/user-info/index.js +1 -1
  55. package/message/combine-payload-codec.d.ts +2 -2
  56. package/message/combine-payload-file.d.ts +11 -0
  57. package/message/create-message.d.ts +16 -4
  58. package/message/create-multibody-message.d.ts +31 -0
  59. package/message/message-snapshot.d.ts +9 -0
  60. package/message/multibody-content.d.ts +35 -0
  61. package/message/translations.d.ts +7 -0
  62. package/package.json +1 -1
  63. package/platform/image/miniapp-image-processor.d.ts +10 -6
  64. package/protocol/msync/codec.d.ts +4 -21
  65. package/protocol/msync/message-identity.d.ts +2 -0
  66. package/protocol/msync/message-payload-decoder.d.ts +39 -0
  67. package/protocol/session-list/gateway.d.ts +8 -1
  68. package/protocol/session-list/payload-decoder.d.ts +13 -1
  69. package/types/chat-manager.d.ts +2 -2
  70. package/types/conversation.d.ts +28 -21
  71. package/types/index.d.ts +33 -9
  72. package/types/message-create.d.ts +130 -2
  73. package/upload/attachment-uploader.d.ts +8 -0
  74. package/utils/session-message-recipient.d.ts +11 -0
  75. package/validators/message-create.d.ts +25 -2
  76. package/chunks/chat-manager-BFd2Z4oY.cjs +0 -1
  77. package/chunks/chat-manager-CId_M_e5.js +0 -2
  78. package/chunks/common-BvlDAw4a.js +0 -1
  79. package/chunks/common-tHGz9VBk.cjs +0 -1
  80. package/chunks/index-BBFJYOM8.js +0 -1
  81. package/chunks/index-DJPAjXAp.cjs +0 -1
  82. package/chunks/index-DtWkVcfb.cjs +0 -1
  83. package/chunks/index-l_BUKi77.js +0 -1
  84. package/chunks/reflection-proto-codec-7r8Sei3y.js +0 -1
  85. package/chunks/reflection-proto-codec-CwrIKT1L.cjs +0 -1
  86. package/chunks/session-list-query-Cvr6rlme.js +0 -1
  87. package/chunks/session-list-query-Dslj2k3t.cjs +0 -1
@@ -1 +1 @@
1
- import{i as e}from"../../chunks/cache-utils-B99glc0H.js";import{a as t}from"../../chunks/profile-version-DLMz8lqW.js";import{p as r,E as s,V as a,l as n,S as o,h as i,m as d,j as u}from"../../chunks/common-BvlDAw4a.js";import{c,b as l,a as p,d as h,e as f,f as m,g as b,h as y,i as I}from"../../chunks/user-info-AlshAvdy.js";import{a as g}from"../../chunks/event-system-B62vbKqG.js";const U=e=>{const{orgName:t,appName:s}=r(e.appKey);return`/${t}/${s}/user/${encodeURIComponent(e.userId)}/metadata/subscription`},_=(e,t="params.userIds")=>c(e,t),w=e=>{const t=_(e);if(t.length>100)throw((e,t,r,n=s.VALIDATION_INVALID_FORMAT)=>new a(e,{code:n,details:{fields:[{path:t,message:e,rule:r}]}}))("params.userIds must contain at most 100 unique users","params.userIds","range",s.SERVICE_LIMIT_EXCEEDED);return{usernames:t}},v=(e,t)=>{const r=(e=>_(e))(t);return`${U(e)}?usernames=${r.map(e=>encodeURIComponent(e)).join(",")}`},S=(e,t)=>{switch(e){case"nickname":return"string"==typeof t?{nickname:t}:null;case"avatarurl":case"avatarUrl":return"string"==typeof t?{avatarUrl:t}:null;case"mail":return"string"==typeof t?{mail:t}:null;case"phone":return"string"==typeof t?{phone:t}:null;case"gender":return"string"==typeof t||"number"==typeof t||"boolean"==typeof t?{gender:t}:null;case"sign":return"string"==typeof t?{sign:t}:null;case"birth":return"string"==typeof t?{birth:t}:null;case"ext":return"string"==typeof t?{ext:t}:null;default:return null}},C=["nickname","avatarUrl","mail","phone","gender","sign","birth","ext"],x=d(u,{common:{},apis:{updateOwnUserInfo:{range:"4,110,204,303,505,901",localErrors:{updateOwnInfo:{validation_invalid:{code:110,retryable:!1}},updateOwnInfoByAttribute:{validation_invalid:{code:110,retryable:!1}}},errors:{service_not_enabled:{code:505,httpStatus:403,retryable:!1,matchField:"desc",matchPattern:"user metadata service not allow"},data_length_exceed:{code:901,httpStatus:403,retryable:!1,matchField:"desc",matchPattern:"exceeds"},concurrent_update_conflict:{code:303,httpStatus:409,retryable:!0,matchField:"desc",matchPattern:"Concurrent"},resource_not_found:{code:204,httpStatus:404,retryable:!1},rate_limit:{code:4,httpStatus:429,retryable:!0}}},updateOwnInfo:{range:"4,110,204,303,505,901",localErrors:{updateOwnInfo:{validation_invalid:{code:110,retryable:!1}},updateOwnInfoByAttribute:{validation_invalid:{code:110,retryable:!1}}},errors:{service_not_enabled:{code:505,httpStatus:403,retryable:!1,matchField:"desc",matchPattern:"user metadata service not allow"},data_length_exceed:{code:901,httpStatus:403,retryable:!1,matchField:"desc",matchPattern:"exceeds"},concurrent_update_conflict:{code:303,httpStatus:409,retryable:!0,matchField:"desc",matchPattern:"Concurrent"},resource_not_found:{code:204,httpStatus:404,retryable:!1},rate_limit:{code:4,httpStatus:429,retryable:!0}}},updateOwnInfoByAttribute:{range:"4,110,204,303,505,901",localErrors:{updateOwnInfo:{validation_invalid:{code:110,retryable:!1}},updateOwnInfoByAttribute:{validation_invalid:{code:110,retryable:!1}}},errors:{service_not_enabled:{code:505,httpStatus:403,retryable:!1,matchField:"desc",matchPattern:"user metadata service not allow"},data_length_exceed:{code:901,httpStatus:403,retryable:!1,matchField:"desc",matchPattern:"exceeds"},concurrent_update_conflict:{code:303,httpStatus:409,retryable:!0,matchField:"desc",matchPattern:"Concurrent"},resource_not_found:{code:204,httpStatus:404,retryable:!1},rate_limit:{code:4,httpStatus:429,retryable:!0}}},getUserInfoByUserId:{range:"4,110,204,900",localErrors:{getUserInfoByUserId:{validation_invalid:{code:110,retryable:!1}}},errors:{usercount_exceed:{code:900,httpStatus:400,retryable:!1},resource_not_found:{code:204,httpStatus:404,retryable:!1},rate_limit:{code:4,httpStatus:429,retryable:!0}}},getUserInfoByAttribute:{range:"4,110,204,900",localErrors:{getUserInfoByAttribute:{validation_invalid:{code:110,retryable:!1}}},errors:{usercount_exceed:{code:900,httpStatus:400,retryable:!1},resource_not_found:{code:204,httpStatus:404,retryable:!1},rate_limit:{code:4,httpStatus:429,retryable:!0}}},subscribeUsersInfo:{range:"4,202,210,303,1600,1601",errors:{unauthorized:{code:202,httpStatus:401,retryable:!0},forbidden:{code:210,httpStatus:403,aliases:["operation forbidden"],matchField:"error_description",matchPattern:"metadata subscription not allow",retryable:!1},subscriber_limit_exceeded:{code:1600,httpStatus:400,matchField:"error_description",matchPattern:"metadata subscription count exceeds limit",retryable:!1},target_limit_exceeded:{code:1601,httpStatus:400,matchField:"error_description",matchPattern:"metadata subscribed count exceeds limit",retryable:!1},rate_limit:{code:4,httpStatus:429,retryable:!0},server_unknown_error:{code:303,httpStatus:500,retryable:!0}}},unsubscribeUsersInfo:{range:"4,202,210,303,1600,1601",errors:{unauthorized:{code:202,httpStatus:401,retryable:!0},forbidden:{code:210,httpStatus:403,aliases:["operation forbidden"],matchField:"error_description",matchPattern:"metadata subscription not allow",retryable:!1},subscriber_limit_exceeded:{code:1600,httpStatus:400,matchField:"error_description",matchPattern:"metadata subscription count exceeds limit",retryable:!1},target_limit_exceeded:{code:1601,httpStatus:400,matchField:"error_description",matchPattern:"metadata subscribed count exceeds limit",retryable:!1},rate_limit:{code:4,httpStatus:429,retryable:!0},server_unknown_error:{code:303,httpStatus:500,retryable:!0}}},getSubscribedUsers:{range:"4,202,210,303,1600,1601",errors:{unauthorized:{code:202,httpStatus:401,retryable:!0},forbidden:{code:210,httpStatus:403,aliases:["operation forbidden"],matchField:"error_description",matchPattern:"metadata subscription not allow",retryable:!1},subscriber_limit_exceeded:{code:1600,httpStatus:400,matchField:"error_description",matchPattern:"metadata subscription count exceeds limit",retryable:!1},target_limit_exceeded:{code:1601,httpStatus:400,matchField:"error_description",matchPattern:"metadata subscribed count exceeds limit",retryable:!1},rate_limit:{code:4,httpStatus:429,retryable:!0},server_unknown_error:{code:303,httpStatus:500,retryable:!0}}}}}),O=class{constructor(){this.capabilities=["userInfo:read","rawNotify:userInfo"],this.client=null,this.eventContext=null,this.restClient=null,this.restBaseUrl=null,this.restToken=null}bind(e,t){this.client=e,this.eventContext=t??null}addEventHandler(e,t){this.eventContext?.addEventHandler(e,t)}removeEventHandler(e){this.eventContext?.removeEventHandler(e)}handleRawNotify(e){if("userInfo"!==e.type)return;const t=this.client,r=t?.getCacheManager();if(!t||!r||!t.getCurrentUserId())return;const s=e.payload,a=(e=>{const t={};for(const[r,s]of Object.entries(e.metadata)){const e=S(r,s);e&&Object.assign(t,e)}return{userId:e.userId,attributes:t,lastModified:e.lastModified,source:"subscribe_metadata_updated"===e.notifyType?"subscription":"contact_metadata_updated"===e.notifyType?"contact":"own"}})(s);if(!a)return;const o=r.applyUserInfoNotifyPatch(a);if(!o.applied)return void n.debug("User-info notify ignored",{userId:a.userId,notifyType:s.notifyType,reason:o.reason,lastModified:a.lastModified});if(r.updateUserInfoSummaryFromRuntime(a.userId),t.refreshConversationDisplayFromUserInfos?.([{userId:a.userId,lastModified:a.lastModified}]),"subscription"===a.source)return void this.eventContext?.dispatch?.("onUserInfoUpdated",[o.record.profile]);if("own"===a.source)return void this.eventContext?.dispatch?.("onOwnInfoUpdated",o.record.profile);const i=t.getContactSnapshot()?.items.find(e=>e.userId===a.userId);this.eventContext?.dispatch?.(g.CONTACT_INFO_UPDATED,{userInfo:o.record.profile,contact:i})}async getUserInfoByUserId(e){return this.runOperation("getUserInfoByUserId",async()=>{const t=this.getRestContextOrThrow(),r=this.getRestClient(t),s=l(c(e.userIds,"params.userIds")),a=p(t);n.debug("UserInfoManager getUserInfoByUserId",{userIds:s.targets});const o=await r.request(a,{method:"POST",body:s,operation:"getUserInfoByUserId"}),i=this.normalizeFetchEnvelope(o,s.targets);return this.writeCache(i),this.completeFetchedProfiles(i,s.targets)})}async getUserInfoByAttribute(e){return this.runOperation("getUserInfoByAttribute",async()=>{const t=this.getRestContextOrThrow(),r=this.getRestClient(t),s=h(c(e.userIds,"params.userIds"),f(e.attributes,"params.attributes")),a=p(t);n.debug("UserInfoManager getUserInfoByAttribute",{userIds:s.targets,attributes:s.properties});const o=await r.request(a,{method:"POST",body:s,operation:"getUserInfoByAttribute"}),i=this.normalizeFetchEnvelope(o,s.targets);return this.writeCache(i),this.completeFetchedProfiles(i,s.targets)})}async subscribeUsersInfo(e){return this.runOperation("subscribeUsersInfo",async()=>{const t=this.getRestContextOrThrow(),r=this.getRestClient(t),s=(e=>U(e))(t),a=w(e.userIds);n.debug("UserInfoManager subscribeUsersInfo",{userIds:a.usernames}),await r.request(s,{method:"POST",body:a,operation:"subscribeUsersInfo"})})}async unsubscribeUsersInfo(e){return this.runOperation("unsubscribeUsersInfo",async()=>{const t=this.getRestContextOrThrow(),r=this.getRestClient(t),s=v(t,e.userIds);n.debug("UserInfoManager unsubscribeUsersInfo",{userIds:c(e.userIds,"params.userIds")}),await r.request(s,{method:"DELETE",operation:"unsubscribeUsersInfo"})})}async getSubscribedUsers(){return this.runOperation("getSubscribedUsers",async()=>{const e=this.getRestContextOrThrow(),t=this.getRestClient(e),r=(e=>U(e))(e);n.debug("UserInfoManager getSubscribedUsers",{userId:e.userId});const s=(e=>{const t=e&&"object"==typeof e?e:null;if(!t||!Array.isArray(t.data))return[];const r=[],s=/* @__PURE__ */new Set;for(const a of t.data){if("string"!=typeof a)continue;const e=a.trim();e&&!s.has(e)&&(s.add(e),r.push(e))}return r})(await t.request(r,{method:"GET",operation:"getSubscribedUsers"}));return 0===s.length?[]:await this.getUserInfoByUserId({userIds:s})})}async updateOwnInfo(e){return this.runOperation("updateOwnInfo",async()=>{const t=this.getRestContextOrThrow(),r=this.getRestClient(t),s=m(e),a=b(t);n.debug("UserInfoManager updateOwnInfo",{userId:t.userId,fields:this.getUpdatedFieldNames(e)});const o=await r.request(a,{method:"PUT",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:s,operation:"updateOwnInfo"}),i=this.normalizeUpdateEnvelope(o,t.userId,this.buildFallbackProfile(t.userId,e));return this.writeCache([i]),this.dispatchSelfUpdate(i.profile),i.profile})}async updateOwnInfoByAttribute(e,t){return this.runOperation("updateOwnInfoByAttribute",async()=>{const r=this.getRestContextOrThrow(),s=this.getRestClient(r),a=y(e,t),o=b(r);n.debug("UserInfoManager updateOwnInfoByAttribute",{userId:r.userId,attribute:e});const i=await s.request(o,{method:"PUT",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:a,operation:"updateOwnInfoByAttribute"}),d=this.normalizeUpdateEnvelope(i,r.userId,{userId:r.userId,[e]:t});return this.writeCache([d]),this.dispatchSelfUpdate(d.profile),d.profile})}async runOperation(e,t){try{return await t()}catch(r){throw this.normalizeOperationError(e,r)}}normalizeOperationError(e,t){return t instanceof o?t:(n.warn("UserInfoManager operation failed",{operation:e,message:t instanceof Error?t.message:String(t)}),new o(`${e} failed`))}getRestContextOrThrow(){if(this.client)return this.client.getRestContext();throw new a("UserInfoManager is not bound to client",{code:s.VALIDATION_REQUIRED,details:{fields:[{path:"userInfoManager.client",message:"client is required",rule:"required"}]}})}getRestClient(e){return this.restClient&&this.restBaseUrl===e.restBaseUrl&&this.restToken===e.token||(this.restClient=new i(e.restBaseUrl,{errorMap:x,requestAdapter:this.client?.getRequestAdapter?.(),includeRequestId:!0}),this.restClient.setAuthToken(e.token),this.restBaseUrl=e.restBaseUrl,this.restToken=e.token),this.restClient}normalizeFetchEnvelope(e,t){return I(e,t)}completeFetchedProfiles(e,t){const r=new Map(e.map(e=>[e.profile.userId,e.profile]));return t.map(e=>r.get(e)??{userId:e})}normalizeUpdateEnvelope(t,r,s){const a=e(t)?t:null,n=(a&&e(a.data)?a.data:null)??(a&&this.looksLikeUserInfoAttributes(a)?a:null),o=a&&"number"==typeof a.lastModified?a.lastModified:void 0;return{profile:n?this.toUserInfo(r,n):{userId:r,...s??{}},lastModified:o}}toUserInfo(e,t){const r={userId:e};return"string"==typeof t.nickname&&(r.nickname=t.nickname),"string"==typeof t.avatarurl?r.avatarUrl=t.avatarurl:"string"==typeof t.avatarUrl&&(r.avatarUrl=t.avatarUrl),"string"==typeof t.mail&&(r.mail=t.mail),"string"==typeof t.phone&&(r.phone=t.phone),"string"!=typeof t.gender&&"number"!=typeof t.gender&&"boolean"!=typeof t.gender||(r.gender=t.gender),"string"==typeof t.sign&&(r.sign=t.sign),"string"==typeof t.birth&&(r.birth=t.birth),"string"==typeof t.ext&&(r.ext=t.ext),r}looksLikeUserInfoAttributes(t){return!!e(t)&&("nickname"in t||"avatarurl"in t||"avatarUrl"in t||"mail"in t||"phone"in t||"gender"in t||"sign"in t||"birth"in t||"ext"in t)}buildFallbackProfile(e,t){const r={userId:e},s=r;for(const a of C)a in t&&(s[a]=t[a]);return r}getUpdatedFieldNames(e){return C.filter(t=>t in e)}projectToSummary(e){const r=Date.now(),s=t(e.lastModified);return{userId:e.profile.userId,nickname:e.profile.nickname,avatarUrl:e.profile.avatarUrl,sign:e.profile.sign,ext:e.profile.ext,userInfoUpdateTime:s,lastSyncAt:r,lastAccess:r,lastUpdate:e.lastModified??r}}writeCache(e){if(!this.client||0===e.length)return;const t=this.client.getCacheManager();t&&("function"==typeof t.upsertRuntimeUserInfos&&t.upsertRuntimeUserInfos(e.map(e=>({profile:e.profile,lastModified:e.lastModified,source:e.profile.userId===this.client?.getRestContext().userId?"update":"fetch"}))),t.setUserInfoSummaries(e.map(e=>this.projectToSummary(e))))}dispatchSelfUpdate(e){this.client?.dispatchOwnInfoUpdated?.(e)}};O.key="userInfoManager";let T=O;export{T as UserInfoManager};
1
+ import{i as e}from"../../chunks/cache-utils-B99glc0H.js";import{a as t}from"../../chunks/profile-version-DLMz8lqW.js";import{p as r,E as s,V as a,l as n,S as o,R as i,m as d,C as u}from"../../chunks/common-gbNWJrBc.js";import{c,b as l,a as p,d as h,e as f,f as m,g as b,h as y,i as I}from"../../chunks/user-info-BkqJHgja.js";import{a as g}from"../../chunks/event-system-DguqwFpc.js";const U=e=>{const{orgName:t,appName:s}=r(e.appKey);return`/${t}/${s}/user/${encodeURIComponent(e.userId)}/metadata/subscription`},_=(e,t="params.userIds")=>c(e,t),w=e=>{const t=_(e);if(t.length>100)throw((e,t,r,n=s.VALIDATION_INVALID_FORMAT)=>new a(e,{code:n,details:{fields:[{path:t,message:e,rule:r}]}}))("params.userIds must contain at most 100 unique users","params.userIds","range",s.SERVICE_LIMIT_EXCEEDED);return{usernames:t}},v=(e,t)=>{const r=(e=>_(e))(t);return`${U(e)}?usernames=${r.map(e=>encodeURIComponent(e)).join(",")}`},S=(e,t)=>{switch(e){case"nickname":return"string"==typeof t?{nickname:t}:null;case"avatarurl":case"avatarUrl":return"string"==typeof t?{avatarUrl:t}:null;case"mail":return"string"==typeof t?{mail:t}:null;case"phone":return"string"==typeof t?{phone:t}:null;case"gender":return"string"==typeof t||"number"==typeof t||"boolean"==typeof t?{gender:t}:null;case"sign":return"string"==typeof t?{sign:t}:null;case"birth":return"string"==typeof t?{birth:t}:null;case"ext":return"string"==typeof t?{ext:t}:null;default:return null}},C=["nickname","avatarUrl","mail","phone","gender","sign","birth","ext"],x=d(u,{common:{},apis:{updateOwnUserInfo:{range:"4,110,204,303,505,901",localErrors:{updateOwnInfo:{validation_invalid:{code:110,retryable:!1}},updateOwnInfoByAttribute:{validation_invalid:{code:110,retryable:!1}}},errors:{service_not_enabled:{code:505,httpStatus:403,retryable:!1,matchField:"desc",matchPattern:"user metadata service not allow"},data_length_exceed:{code:901,httpStatus:403,retryable:!1,matchField:"desc",matchPattern:"exceeds"},concurrent_update_conflict:{code:303,httpStatus:409,retryable:!0,matchField:"desc",matchPattern:"Concurrent"},resource_not_found:{code:204,httpStatus:404,retryable:!1},rate_limit:{code:4,httpStatus:429,retryable:!0}}},updateOwnInfo:{range:"4,110,204,303,505,901",localErrors:{updateOwnInfo:{validation_invalid:{code:110,retryable:!1}},updateOwnInfoByAttribute:{validation_invalid:{code:110,retryable:!1}}},errors:{service_not_enabled:{code:505,httpStatus:403,retryable:!1,matchField:"desc",matchPattern:"user metadata service not allow"},data_length_exceed:{code:901,httpStatus:403,retryable:!1,matchField:"desc",matchPattern:"exceeds"},concurrent_update_conflict:{code:303,httpStatus:409,retryable:!0,matchField:"desc",matchPattern:"Concurrent"},resource_not_found:{code:204,httpStatus:404,retryable:!1},rate_limit:{code:4,httpStatus:429,retryable:!0}}},updateOwnInfoByAttribute:{range:"4,110,204,303,505,901",localErrors:{updateOwnInfo:{validation_invalid:{code:110,retryable:!1}},updateOwnInfoByAttribute:{validation_invalid:{code:110,retryable:!1}}},errors:{service_not_enabled:{code:505,httpStatus:403,retryable:!1,matchField:"desc",matchPattern:"user metadata service not allow"},data_length_exceed:{code:901,httpStatus:403,retryable:!1,matchField:"desc",matchPattern:"exceeds"},concurrent_update_conflict:{code:303,httpStatus:409,retryable:!0,matchField:"desc",matchPattern:"Concurrent"},resource_not_found:{code:204,httpStatus:404,retryable:!1},rate_limit:{code:4,httpStatus:429,retryable:!0}}},getUserInfoByUserId:{range:"4,110,204,900",localErrors:{getUserInfoByUserId:{validation_invalid:{code:110,retryable:!1}}},errors:{usercount_exceed:{code:900,httpStatus:400,retryable:!1},resource_not_found:{code:204,httpStatus:404,retryable:!1},rate_limit:{code:4,httpStatus:429,retryable:!0}}},getUserInfoByAttribute:{range:"4,110,204,900",localErrors:{getUserInfoByAttribute:{validation_invalid:{code:110,retryable:!1}}},errors:{usercount_exceed:{code:900,httpStatus:400,retryable:!1},resource_not_found:{code:204,httpStatus:404,retryable:!1},rate_limit:{code:4,httpStatus:429,retryable:!0}}},subscribeUsersInfo:{range:"4,202,210,303,1600,1601",errors:{unauthorized:{code:202,httpStatus:401,retryable:!0},forbidden:{code:210,httpStatus:403,aliases:["operation forbidden"],matchField:"error_description",matchPattern:"metadata subscription not allow",retryable:!1},subscriber_limit_exceeded:{code:1600,httpStatus:400,matchField:"error_description",matchPattern:"metadata subscription count exceeds limit",retryable:!1},target_limit_exceeded:{code:1601,httpStatus:400,matchField:"error_description",matchPattern:"metadata subscribed count exceeds limit",retryable:!1},rate_limit:{code:4,httpStatus:429,retryable:!0},server_unknown_error:{code:303,httpStatus:500,retryable:!0}}},unsubscribeUsersInfo:{range:"4,202,210,303,1600,1601",errors:{unauthorized:{code:202,httpStatus:401,retryable:!0},forbidden:{code:210,httpStatus:403,aliases:["operation forbidden"],matchField:"error_description",matchPattern:"metadata subscription not allow",retryable:!1},subscriber_limit_exceeded:{code:1600,httpStatus:400,matchField:"error_description",matchPattern:"metadata subscription count exceeds limit",retryable:!1},target_limit_exceeded:{code:1601,httpStatus:400,matchField:"error_description",matchPattern:"metadata subscribed count exceeds limit",retryable:!1},rate_limit:{code:4,httpStatus:429,retryable:!0},server_unknown_error:{code:303,httpStatus:500,retryable:!0}}},getSubscribedUsers:{range:"4,202,210,303,1600,1601",errors:{unauthorized:{code:202,httpStatus:401,retryable:!0},forbidden:{code:210,httpStatus:403,aliases:["operation forbidden"],matchField:"error_description",matchPattern:"metadata subscription not allow",retryable:!1},subscriber_limit_exceeded:{code:1600,httpStatus:400,matchField:"error_description",matchPattern:"metadata subscription count exceeds limit",retryable:!1},target_limit_exceeded:{code:1601,httpStatus:400,matchField:"error_description",matchPattern:"metadata subscribed count exceeds limit",retryable:!1},rate_limit:{code:4,httpStatus:429,retryable:!0},server_unknown_error:{code:303,httpStatus:500,retryable:!0}}}}}),O=class{constructor(){this.capabilities=["userInfo:read","rawNotify:userInfo"],this.client=null,this.eventContext=null,this.restClient=null,this.restBaseUrl=null,this.restToken=null}bind(e,t){this.client=e,this.eventContext=t??null}addEventHandler(e,t){this.eventContext?.addEventHandler(e,t)}removeEventHandler(e){this.eventContext?.removeEventHandler(e)}handleRawNotify(e){if("userInfo"!==e.type)return;const t=this.client,r=t?.getCacheManager();if(!t||!r||!t.getCurrentUserId())return;const s=e.payload,a=(e=>{const t={};for(const[r,s]of Object.entries(e.metadata)){const e=S(r,s);e&&Object.assign(t,e)}return{userId:e.userId,attributes:t,lastModified:e.lastModified,source:"subscribe_metadata_updated"===e.notifyType?"subscription":"contact_metadata_updated"===e.notifyType?"contact":"own"}})(s);if(!a)return;const o=r.applyUserInfoNotifyPatch(a);if(!o.applied)return void n.debug("User-info notify ignored",{userId:a.userId,notifyType:s.notifyType,reason:o.reason,lastModified:a.lastModified});if(r.updateUserInfoSummaryFromRuntime(a.userId),t.refreshConversationDisplayFromUserInfos?.([{userId:a.userId,lastModified:a.lastModified}]),"subscription"===a.source)return void this.eventContext?.dispatch?.("onUserInfoUpdated",[o.record.profile]);if("own"===a.source)return void this.eventContext?.dispatch?.("onOwnInfoUpdated",o.record.profile);const i=t.getContactSnapshot()?.items.find(e=>e.userId===a.userId);this.eventContext?.dispatch?.(g.CONTACT_INFO_UPDATED,{userInfo:o.record.profile,contact:i})}async getUserInfoByUserId(e){return this.runOperation("getUserInfoByUserId",async()=>{const t=this.getRestContextOrThrow(),r=this.getRestClient(t),s=l(c(e.userIds,"params.userIds")),a=p(t);n.debug("UserInfoManager getUserInfoByUserId",{userIds:s.targets});const o=await r.request(a,{method:"POST",body:s,operation:"getUserInfoByUserId"}),i=this.normalizeFetchEnvelope(o,s.targets);return this.writeCache(i),this.completeFetchedProfiles(i,s.targets)})}async getUserInfoByAttribute(e){return this.runOperation("getUserInfoByAttribute",async()=>{const t=this.getRestContextOrThrow(),r=this.getRestClient(t),s=h(c(e.userIds,"params.userIds"),f(e.attributes,"params.attributes")),a=p(t);n.debug("UserInfoManager getUserInfoByAttribute",{userIds:s.targets,attributes:s.properties});const o=await r.request(a,{method:"POST",body:s,operation:"getUserInfoByAttribute"}),i=this.normalizeFetchEnvelope(o,s.targets);return this.writeCache(i),this.completeFetchedProfiles(i,s.targets)})}async subscribeUsersInfo(e){return this.runOperation("subscribeUsersInfo",async()=>{const t=this.getRestContextOrThrow(),r=this.getRestClient(t),s=(e=>U(e))(t),a=w(e.userIds);n.debug("UserInfoManager subscribeUsersInfo",{userIds:a.usernames}),await r.request(s,{method:"POST",body:a,operation:"subscribeUsersInfo"})})}async unsubscribeUsersInfo(e){return this.runOperation("unsubscribeUsersInfo",async()=>{const t=this.getRestContextOrThrow(),r=this.getRestClient(t),s=v(t,e.userIds);n.debug("UserInfoManager unsubscribeUsersInfo",{userIds:c(e.userIds,"params.userIds")}),await r.request(s,{method:"DELETE",operation:"unsubscribeUsersInfo"})})}async getSubscribedUsers(){return this.runOperation("getSubscribedUsers",async()=>{const e=this.getRestContextOrThrow(),t=this.getRestClient(e),r=(e=>U(e))(e);n.debug("UserInfoManager getSubscribedUsers",{userId:e.userId});const s=(e=>{const t=e&&"object"==typeof e?e:null;if(!t||!Array.isArray(t.data))return[];const r=[],s=/* @__PURE__ */new Set;for(const a of t.data){if("string"!=typeof a)continue;const e=a.trim();e&&!s.has(e)&&(s.add(e),r.push(e))}return r})(await t.request(r,{method:"GET",operation:"getSubscribedUsers"}));return 0===s.length?[]:await this.getUserInfoByUserId({userIds:s})})}async updateOwnInfo(e){return this.runOperation("updateOwnInfo",async()=>{const t=this.getRestContextOrThrow(),r=this.getRestClient(t),s=m(e),a=b(t);n.debug("UserInfoManager updateOwnInfo",{userId:t.userId,fields:this.getUpdatedFieldNames(e)});const o=await r.request(a,{method:"PUT",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:s,operation:"updateOwnInfo"}),i=this.normalizeUpdateEnvelope(o,t.userId,this.buildFallbackProfile(t.userId,e));return this.writeCache([i]),this.dispatchSelfUpdate(i.profile),i.profile})}async updateOwnInfoByAttribute(e,t){return this.runOperation("updateOwnInfoByAttribute",async()=>{const r=this.getRestContextOrThrow(),s=this.getRestClient(r),a=y(e,t),o=b(r);n.debug("UserInfoManager updateOwnInfoByAttribute",{userId:r.userId,attribute:e});const i=await s.request(o,{method:"PUT",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:a,operation:"updateOwnInfoByAttribute"}),d=this.normalizeUpdateEnvelope(i,r.userId,{userId:r.userId,[e]:t});return this.writeCache([d]),this.dispatchSelfUpdate(d.profile),d.profile})}async runOperation(e,t){try{return await t()}catch(r){throw this.normalizeOperationError(e,r)}}normalizeOperationError(e,t){return t instanceof o?t:(n.warn("UserInfoManager operation failed",{operation:e,message:t instanceof Error?t.message:String(t)}),new o(`${e} failed`))}getRestContextOrThrow(){if(this.client)return this.client.getRestContext();throw new a("UserInfoManager is not bound to client",{code:s.VALIDATION_REQUIRED,details:{fields:[{path:"userInfoManager.client",message:"client is required",rule:"required"}]}})}getRestClient(e){return this.restClient&&this.restBaseUrl===e.restBaseUrl&&this.restToken===e.token||(this.restClient=new i(e.restBaseUrl,{errorMap:x,requestAdapter:this.client?.getRequestAdapter?.(),includeRequestId:!0}),this.restClient.setAuthToken(e.token),this.restBaseUrl=e.restBaseUrl,this.restToken=e.token),this.restClient}normalizeFetchEnvelope(e,t){return I(e,t)}completeFetchedProfiles(e,t){const r=new Map(e.map(e=>[e.profile.userId,e.profile]));return t.map(e=>r.get(e)??{userId:e})}normalizeUpdateEnvelope(t,r,s){const a=e(t)?t:null,n=(a&&e(a.data)?a.data:null)??(a&&this.looksLikeUserInfoAttributes(a)?a:null),o=a&&"number"==typeof a.lastModified?a.lastModified:void 0;return{profile:n?this.toUserInfo(r,n):{userId:r,...s??{}},lastModified:o}}toUserInfo(e,t){const r={userId:e};return"string"==typeof t.nickname&&(r.nickname=t.nickname),"string"==typeof t.avatarurl?r.avatarUrl=t.avatarurl:"string"==typeof t.avatarUrl&&(r.avatarUrl=t.avatarUrl),"string"==typeof t.mail&&(r.mail=t.mail),"string"==typeof t.phone&&(r.phone=t.phone),"string"!=typeof t.gender&&"number"!=typeof t.gender&&"boolean"!=typeof t.gender||(r.gender=t.gender),"string"==typeof t.sign&&(r.sign=t.sign),"string"==typeof t.birth&&(r.birth=t.birth),"string"==typeof t.ext&&(r.ext=t.ext),r}looksLikeUserInfoAttributes(t){return!!e(t)&&("nickname"in t||"avatarurl"in t||"avatarUrl"in t||"mail"in t||"phone"in t||"gender"in t||"sign"in t||"birth"in t||"ext"in t)}buildFallbackProfile(e,t){const r={userId:e},s=r;for(const a of C)a in t&&(s[a]=t[a]);return r}getUpdatedFieldNames(e){return C.filter(t=>t in e)}projectToSummary(e){const r=Date.now(),s=t(e.lastModified);return{userId:e.profile.userId,nickname:e.profile.nickname,avatarUrl:e.profile.avatarUrl,sign:e.profile.sign,ext:e.profile.ext,userInfoUpdateTime:s,lastSyncAt:r,lastAccess:r,lastUpdate:e.lastModified??r}}writeCache(e){if(!this.client||0===e.length)return;const t=this.client.getCacheManager();t&&("function"==typeof t.upsertRuntimeUserInfos&&t.upsertRuntimeUserInfos(e.map(e=>({profile:e.profile,lastModified:e.lastModified,source:e.profile.userId===this.client?.getRestContext().userId?"update":"fetch"}))),t.setUserInfoSummaries(e.map(e=>this.projectToSummary(e))))}dispatchSelfUpdate(e){this.client?.dispatchOwnInfoUpdated?.(e)}};O.key="userInfoManager";let T=O;export{T as UserInfoManager};
@@ -1,4 +1,4 @@
1
- import type { ChatConversationType, Message, MessageType, Sender } from '../types';
1
+ import type { ChatConversationType, Message, MessageBody, MessageType, Sender } from '../types';
2
2
  /** 合并消息解码后的子消息结构(内部类型) */
3
3
  interface CombineMessageItem {
4
4
  msgServerId?: string;
@@ -8,7 +8,7 @@ interface CombineMessageItem {
8
8
  conversationId: string;
9
9
  conversationType: ChatConversationType;
10
10
  timestamp: number;
11
- body: Record<string, unknown>;
11
+ body: MessageBody;
12
12
  ext?: Record<string, unknown>;
13
13
  combineLevel?: number;
14
14
  }
@@ -0,0 +1,11 @@
1
+ import { type RuntimeDetectionInput } from '../platform/env';
2
+ import type { CompatibleFile } from '../types';
3
+ export interface MaterializeCombinePayloadFileOptions {
4
+ readonly payload: Uint8Array;
5
+ readonly filename: string;
6
+ readonly filetype: string;
7
+ readonly fileId: string;
8
+ readonly runtime?: RuntimeDetectionInput;
9
+ }
10
+ export declare const materializeCombinePayloadFile: (options: MaterializeCombinePayloadFileOptions) => Promise<CompatibleFile>;
11
+ export declare const removeCombinePayloadFile: (file: CompatibleFile | undefined, runtime?: RuntimeDetectionInput) => Promise<void>;
@@ -1,21 +1,33 @@
1
1
  /**
2
2
  * 消息创建方法
3
3
  */
4
- import type { // 跨端文件类型
4
+ import type { // 合并消息体
5
+ CompatibleFile, // 跨端文件类型
5
6
  CreateCmdMessageParams, // 命令消息入参
6
7
  CreateCombineMessageParams, // 合并消息入参
7
8
  CreateCustomMessageParams, // 自定义消息入参
8
9
  CreateFileMessageParams, // 文件消息入参
9
10
  CreateImageMessageParams, // 图片消息入参
10
- CreateLocationMessageParams, // 创建消息基础入参
11
+ CreateLocationMessageParams, // 位置消息入参
12
+ CreateMessageBaseParams, // 创建消息基础入参
11
13
  CreateTextMessageParams, // 文本消息入参
12
14
  CreateVideoMessageParams, // 视频消息入参
13
15
  CreateVoiceMessageParams, // 位置消息体
14
- Message, // 消息类型联合
15
- Sender } from '../types';
16
+ Message, // 消息类型
17
+ MessageBody, // 消息体联合类型
18
+ MessageType, // 消息类型联合
19
+ MultiBodySegment, Sender } from '../types';
20
+ export declare const ensureSender: (sender?: Sender) => Sender;
21
+ export declare const resolveMediaUrl: (url: string | undefined, data?: CompatibleFile) => string;
16
22
  interface CreateImageMessageOptions {
17
23
  useCustomAttachmentUpload?: boolean;
18
24
  }
25
+ export declare const buildMessage: (params: CreateMessageBaseParams, // 通用入参
26
+ sender: Sender, // 发送者信息
27
+ type: Exclude<MessageType, "multi_body">, // 消息类型
28
+ body: MessageBody, // 消息体
29
+ msgLocalId?: string) => Message;
30
+ export declare const buildMultiBodyMessage: (params: CreateMessageBaseParams, sender: Sender, bodies: ReadonlyArray<MultiBodySegment>, msgLocalId?: string) => Message;
19
31
  /**
20
32
  * 创建文本消息
21
33
  */
@@ -0,0 +1,31 @@
1
+ import type { CreateMultiBodyMessageParams, CreateCustomMessageBodyParams, CreateFileMessageBodyParams, CreateImageMessageBodyParams, CreateLocationMessageBodyParams, CreateTextMessageBodyParams, CreateVideoMessageBodyParams, CreateVoiceMessageBodyParams, CustomMessageBody, FileMessageBody, ImageMessageBody, LocationMessageBody, Message, Sender, TextMessageBody, VideoMessageBody, VoiceMessageBody } from '../types';
2
+ interface CreateImageMessageBodyOptions {
3
+ readonly useCustomAttachmentUpload?: boolean;
4
+ }
5
+ export declare const createTextMessageBody: (params: CreateTextMessageBodyParams) => {
6
+ type: "text";
7
+ } & TextMessageBody;
8
+ export declare const createImageMessageBody: (params: CreateImageMessageBodyParams, options?: CreateImageMessageBodyOptions) => {
9
+ type: "image";
10
+ } & ImageMessageBody;
11
+ export declare const createFileMessageBody: (params: CreateFileMessageBodyParams) => {
12
+ type: "file";
13
+ } & FileMessageBody;
14
+ export declare const createVoiceMessageBody: (params: CreateVoiceMessageBodyParams) => {
15
+ type: "voice";
16
+ } & VoiceMessageBody;
17
+ export declare const createVideoMessageBody: (params: CreateVideoMessageBodyParams) => {
18
+ type: "video";
19
+ } & VideoMessageBody;
20
+ export declare const createLocationMessageBody: (params: CreateLocationMessageBodyParams) => {
21
+ type: "location";
22
+ } & LocationMessageBody;
23
+ export declare const createCustomMessageBody: (params: CreateCustomMessageBodyParams) => {
24
+ type: "custom";
25
+ } & CustomMessageBody;
26
+ /**
27
+ * 组装复合消息。校验不通过时不取走本地文件。
28
+ * 通过后把文件从分段对象挪到 `msgLocalId:bodyIndex`,发送时按这个 key 找文件。
29
+ */
30
+ export declare const createMultiBodyMessage: (params: CreateMultiBodyMessageParams, sender?: Sender) => Message;
31
+ export {};
@@ -0,0 +1,9 @@
1
+ import type { Message, MessageBody } from '../types';
2
+ import type { MessageProfileVersionSidecar } from '../core/message/profile-sync/profile-version-sidecar';
3
+ /** @internal Validated serializable fields shared by realtime projections and both cache formats. */
4
+ export declare const projectMessageMetadata: (value: unknown) => Omit<Partial<Message>, "body"> & MessageProfileVersionSidecar;
5
+ /** @internal Stable comparison for message snapshots; object key order has no business meaning. */
6
+ export declare const messageSnapshotSignature: (value: unknown) => string;
7
+ /** @internal A shallow serializable body projection without expanding combined-message details. */
8
+ export declare const projectMessageSegments: (bodies: ReadonlyArray<Record<string, unknown>>) => ReadonlyArray<Record<string, unknown>>;
9
+ export declare const projectMessageBody: (body: MessageBody | Record<string, unknown> | ReadonlyArray<Record<string, unknown>>) => Record<string, unknown> | ReadonlyArray<Record<string, unknown>>;
@@ -0,0 +1,35 @@
1
+ import type { CompatibleFile, MultiBody, Message, MessageBody, MessageType } from '../types';
2
+ declare const MULTIBODY_SEGMENT_TYPES: readonly ["text", "image", "file", "voice", "video", "location", "custom"];
3
+ export type MultiBodySegmentType = (typeof MULTIBODY_SEGMENT_TYPES)[number];
4
+ export declare const isMultiBodySegmentType: (type: string) => type is MultiBodySegmentType;
5
+ /** 复合消息附件缓存 key。冒号必须保留,避免本地 id 和分段下标粘在一起。 */
6
+ export declare const attachmentStoreKey: (msgLocalId: string, bodyIndex: number) => string;
7
+ /** 本地文件挂在分段对象本身上。组装时按这个对象取走,复制分段会把文件丢掉。 */
8
+ export declare const holdAttachmentFile: (body: object, file: CompatibleFile) => void;
9
+ export declare const takeAttachmentFile: (body: object) => CompatibleFile | undefined;
10
+ export declare const mapRestSegmentType: (type: string | undefined) => MessageType | null;
11
+ /** REST 合并段经常写成 txt/text,并用 subType 0 或合并启发字段标识。 */
12
+ export declare const isCombineRestSegment: (segment: Record<string, unknown>) => boolean;
13
+ /** 多段无法合成复合消息时只留第一段,并说明原因。第一段本身解不出来则仍丢弃。 */
14
+ export declare const keepFirstBody: <T>(segments: ReadonlyArray<T>, reason: string) => ReadonlyArray<T>;
15
+ export declare const composeSegments: (segments: ReadonlyArray<{
16
+ type: MessageType;
17
+ body: MessageBody;
18
+ } | null>) => {
19
+ type: MessageType;
20
+ body: MessageBody;
21
+ } | null;
22
+ export declare const resolvePublicMessageContent: (contents: ReadonlyArray<unknown>, decodeContent: (content: Record<string, unknown>) => {
23
+ type: MessageType;
24
+ body: MessageBody;
25
+ } | null) => {
26
+ type: MessageType;
27
+ body: MessageBody;
28
+ } | null;
29
+ export declare const isDownloadableSegmentType: (type: string) => type is "image" | "voice" | "video" | "file";
30
+ export declare const attachMultiBodyUploadDetails: (error: unknown, msgLocalId: string) => void;
31
+ export declare const isMultiBodyMessage: (message: Pick<Message, "type" | "body">) => message is Message & {
32
+ type: "multi_body";
33
+ body: MultiBody;
34
+ };
35
+ export {};
@@ -0,0 +1,7 @@
1
+ /** 消息译文的公开映射与现有消息协议数组之间的转换。 */
2
+ export declare const toProtocolTranslations: (translations: Readonly<Record<string, string>> | undefined) => ReadonlyArray<{
3
+ readonly code: string;
4
+ readonly text: string;
5
+ }>;
6
+ /** 非法译文不影响原文解析;重复语言采用最后一个合法条目。 */
7
+ export declare const readTranslations: (metaPayload: unknown) => Record<string, string> | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "easemob-websdk",
3
- "version": "5.1.2",
3
+ "version": "5.2.0",
4
4
  "description": "High-performance, type-safe, extensible instant messaging SDK",
5
5
  "homepage": "https://www.easemob.com/",
6
6
  "type": "module",
@@ -4,18 +4,21 @@ interface MiniAppReadFileSuccessResult {
4
4
  }
5
5
  interface MiniAppGetFileInfoSuccessResult {
6
6
  readonly size: number;
7
+ readonly digest?: string;
8
+ }
9
+ interface MiniAppGetFileInfoOptions {
10
+ readonly filePath: string;
11
+ readonly digestAlgorithm?: 'md5';
12
+ readonly success: (result: MiniAppGetFileInfoSuccessResult) => void;
13
+ readonly fail?: (error: unknown) => void;
7
14
  }
8
15
  interface MiniAppFileSystemManagerLike {
9
- readFile(options: {
16
+ readFile?(options: {
10
17
  readonly filePath: string;
11
18
  readonly success: (result: MiniAppReadFileSuccessResult) => void;
12
19
  readonly fail?: (error: unknown) => void;
13
20
  }): void;
14
- getFileInfo?(options: {
15
- readonly filePath: string;
16
- readonly success: (result: MiniAppGetFileInfoSuccessResult) => void;
17
- readonly fail?: (error: unknown) => void;
18
- }): void;
21
+ getFileInfo?(options: MiniAppGetFileInfoOptions): void;
19
22
  }
20
23
  interface MiniAppImageInfoResponse {
21
24
  readonly width: number;
@@ -40,6 +43,7 @@ export interface MiniAppImageRuntimeLike {
40
43
  readonly fail?: (error: unknown) => void;
41
44
  }): void;
42
45
  getFileSystemManager?(): MiniAppFileSystemManagerLike;
46
+ getFileInfo?(options: MiniAppGetFileInfoOptions): void;
43
47
  }
44
48
  export declare const createMiniAppImageProcessor: (runtime: MiniAppImageRuntimeLike) => ImageProcessor;
45
49
  export {};
@@ -55,7 +55,7 @@ export declare class MsyncCodec {
55
55
  private compressionFailureCount;
56
56
  private readonly maxCompressionFailures;
57
57
  private useReplacedMessageContents;
58
- private readonly useCustomAttachmentUpload;
58
+ private readonly payloadDecoder;
59
59
  constructor(options: MsyncContextOptions);
60
60
  updateToken(token: string): void;
61
61
  updateResource(resource: string | undefined): void;
@@ -149,12 +149,12 @@ export declare class MsyncCodec {
149
149
  private decodeMessage;
150
150
  private resolveMessageProfileVersionFields;
151
151
  private buildMessageMeta;
152
+ private buildMessageMetaFromBody;
153
+ private buildTranslationsMeta;
152
154
  private resolveProfileVersionTime;
153
155
  private isPublicMessageType;
154
156
  private resolveMessageIsOnline;
155
- private decodeStreamMeta;
156
- private mapStreamStatus;
157
- private resolveContentText;
157
+ private buildContents;
158
158
  private buildContent;
159
159
  private decodeContent;
160
160
  private resolveChatType;
@@ -171,23 +171,6 @@ export declare class MsyncCodec {
171
171
  private resolveRouteType;
172
172
  private resolveBroadcast;
173
173
  private resolvePriority;
174
- private resolveContentReplaced;
175
- private resolveMessageMetaPayload;
176
- private resolvePeerRead;
177
- private resolveGroupReadCount;
178
- private resolveModifiedInfo;
179
- private resolveChatThreadMessageContext;
180
- private resolveChatThreadOverview;
181
- private resolveChatThreadOverviewLastMessage;
182
- private resolveThreadLastMessagePayload;
183
- private mapThreadMessageType;
184
- private normalizeThreadLastMessageBody;
185
- private readString;
186
- private numberField;
187
- private parseMetaPayload;
188
- private toFiniteNumber;
189
- private resolveReactions;
190
- private normalizeMetaPayload;
191
174
  private mapPriorityToTag;
192
175
  private getRequestedCompressTypes;
193
176
  private configureCompression;
@@ -0,0 +1,2 @@
1
+ /** Returns a payload user ID when present, falling back to the envelope JID. */
2
+ export declare const resolveMessageJidName: (payloadJid: unknown, envelopeJid: unknown) => string;
@@ -0,0 +1,39 @@
1
+ import type { Message, MessageBody, MessageModifiedInfo, StreamMessageMeta, StreamMessageStatus } from '../../types';
2
+ import type { ChatConversationType } from '../../types/chat-manager';
3
+ import type { ChatThreadMessageContext, ChatThreadOverview } from '../../types/chat-thread';
4
+ export declare const readMessageContent: (body: Record<string, unknown>) => Record<string, unknown> | undefined;
5
+ /** @internal Shared message payload mapping for realtime and session-list protocols. */
6
+ export declare class MessagePayloadDecoder {
7
+ private readonly userId;
8
+ private readonly useCustomAttachmentUpload;
9
+ constructor(userId: string, useCustomAttachmentUpload?: boolean);
10
+ decodeMetadata(body: Record<string, unknown>, conversationType: ChatConversationType, direct: Message['direct'], fallbackMeta?: unknown): Omit<Partial<Message>, 'body'>;
11
+ decodeContent(content: Record<string, unknown>, metaPayload?: unknown): {
12
+ type: Message['type'];
13
+ body: MessageBody;
14
+ } | null;
15
+ decodeStreamMeta(rawStream: unknown, content: Record<string, unknown> | undefined): StreamMessageMeta | undefined;
16
+ mapStreamStatus(rawStatus: number, errorType: number): StreamMessageStatus;
17
+ resolveContentText(content: Record<string, unknown> | undefined): string;
18
+ resolveContentReplaced(metaPayload: unknown): boolean | undefined;
19
+ resolveMessageMetaPayload(primaryPayload: unknown, fallbackPayload: unknown): unknown;
20
+ resolvePeerRead(metaPayload: unknown, conversationType: ChatConversationType, direct: Message['direct']): boolean | undefined;
21
+ resolveGroupReadCount(metaPayload: unknown, conversationType: ChatConversationType): number | undefined;
22
+ resolveModifiedInfo(metaPayload: unknown): MessageModifiedInfo | undefined;
23
+ resolveChatThreadMessageContext(metaPayload: unknown): ChatThreadMessageContext | undefined;
24
+ resolveChatThreadOverview(metaPayload: unknown): ChatThreadOverview | undefined;
25
+ resolveChatThreadOverviewLastMessage(rawLastMessage: unknown, chatThreadId: string): ChatThreadOverview['lastMessage'] | undefined;
26
+ resolveThreadLastMessagePayload(rawLastMessage: Record<string, unknown>): Record<string, unknown> | null;
27
+ composeThreadBodies(bodies: ReadonlyArray<unknown>): {
28
+ type: Message['type'];
29
+ body: MessageBody;
30
+ } | null;
31
+ mapThreadMessageType(rawType: string | undefined): Message['type'];
32
+ normalizeThreadLastMessageBody(type: Message['type'], firstBody: Record<string, unknown> | null, rawLastMessage: Record<string, unknown>): MessageBody;
33
+ readString(value: unknown): string | undefined;
34
+ numberField<Key extends string>(key: Key, value: unknown): Record<Key, number> | object;
35
+ parseMetaPayload(metaPayload: unknown): Record<string, unknown> | null;
36
+ toFiniteNumber(value: unknown): number | undefined;
37
+ resolveReactions(metaPayload: unknown): Message['reactions'];
38
+ normalizeMetaPayload(payload: unknown): Uint8Array | null;
39
+ }
@@ -14,6 +14,13 @@ export declare const toSessionMessageSnippet: (meta: SessionListMeta | undefined
14
14
  readonly conversationId: string;
15
15
  readonly conversationType: ConversationItem["conversationType"];
16
16
  readonly currentUserId: string;
17
+ readonly ext?: Record<string, unknown>;
18
+ readonly payload?: Record<string, unknown> | null;
19
+ readonly useCustomAttachmentUpload?: boolean;
20
+ readonly org: string;
21
+ readonly app: string;
17
22
  readonly singleChatPeerDisplay?: Pick<ConversationItem, "conversationName" | "conversationAvatar">;
18
23
  }) => SessionMessageSnippet | null;
19
- export declare const toSessionListItems: (response: SessionListResponse, currentUserId: string) => ReadonlyArray<ConversationItem>;
24
+ export declare const toSessionListItems: (response: SessionListResponse, currentUserId: string, org: string, app: string, options?: {
25
+ readonly useCustomAttachmentUpload?: boolean;
26
+ }) => ReadonlyArray<ConversationItem>;
@@ -1 +1,13 @@
1
- export declare const decodeSessionListPayloadBody: (payload?: Uint8Array) => Record<string, unknown>;
1
+ import type { SessionMessageSnippet } from '../../types/conversation';
2
+ export declare const decodeSessionListPayload: (payload?: Uint8Array) => {
3
+ readonly body: Record<string, unknown> | ReadonlyArray<Record<string, unknown>>;
4
+ readonly ext: Record<string, unknown>;
5
+ };
6
+ export declare const decodeSessionListPayloadRecord: (payload?: Uint8Array) => Record<string, unknown> | null;
7
+ export declare const mapSessionListPayload: (decoded: Record<string, unknown> | null, options: {
8
+ readonly currentUserId: string;
9
+ readonly conversationType: NonNullable<SessionMessageSnippet["conversationType"]>;
10
+ readonly from: string;
11
+ readonly useCustomAttachmentUpload?: boolean;
12
+ }) => Partial<SessionMessageSnippet>;
13
+ export declare const decodeSessionListPayloadBody: (payload?: Uint8Array) => Record<string, unknown> | ReadonlyArray<Record<string, unknown>>;
@@ -63,7 +63,7 @@ export interface RecallMessageParams extends ConversationLocator {
63
63
  export interface UpdateMessageParams extends ConversationLocator {
64
64
  /** [zh-CN] 待编辑的消息 ID。 [en-US] ID of the message to edit. */
65
65
  messageId: string;
66
- /** [zh-CN] 编辑后的消息内容。当前仅支持文本消息和自定义消息。 [en-US] Updated message content. Currently, only text messages and custom messages are supported. */
66
+ /** [zh-CN] 编辑后的消息内容。文本消息可修改 `content` 和 `ext`;`body.targetLanguages` 非空时先翻译再编辑,译文写入 `body.translations`,翻译失败则抛出异常且不发送编辑。自定义消息可修改 `event`、`params` 和 `ext`;图片、语音、视频、文件、位置和合并消息仅可修改 `ext`。命令消息和复合消息不支持编辑。 [en-US] Updated message content. Text messages can change `content` and `ext`. Non-empty `body.targetLanguages` are translated before the edit and stored in `body.translations`; translation failure throws without sending the edit. Custom messages can change `event`, `params`, and `ext`. Image, voice, video, file, location, and combined messages can change `ext` only. Command and multi_body messages cannot be edited. */
67
67
  message: Pick<Message, 'type' | 'body' | 'ext'>;
68
68
  }
69
69
  /**
@@ -113,7 +113,7 @@ export interface MessageHistoryPage {
113
113
  * [en-US] Parameters for downloading a message attachment.
114
114
  */
115
115
  export interface DownloadAttachmentParams {
116
- /** [zh-CN] 需要下载附件的消息,通常为图片、语音、视频或文件消息。 [en-US] Message whose attachment is to be downloaded, typically an image, voice, video, or file message. */
116
+ /** [zh-CN] 需要下载附件的消息,通常为图片、语音、视频、文件或复合消息。复合消息会下载其中全部图片、语音、视频和文件。 [en-US] Message whose attachment is to be downloaded, typically an image, voice, video, file, or multi_body message. A multi_body message downloads every image, voice, video, and file segment. */
117
117
  message: Message;
118
118
  }
119
119
  /**
@@ -2,8 +2,7 @@
2
2
  * [zh-CN] Conversation 公开类型定义。
3
3
  * [en-US] Public conversation type definitions.
4
4
  */
5
- import type { Sender } from './sender';
6
- import type { Message, MessageDirect, MessageModifiedInfo, MessageSendStatus, MessageType } from './index';
5
+ import type { Message } from './index';
7
6
  export type { Sender } from './sender';
8
7
  export declare const CONVERSATION_TYPE: {
9
8
  readonly SINGLE_CHAT: "singleChat";
@@ -205,38 +204,46 @@ export interface RefreshSessionListParams {
205
204
  */
206
205
  export type SessionListRemindType = 'DEFAULT' | 'ALL' | 'AT' | 'NONE';
207
206
  /**
208
- * [zh-CN] 会话列表最小消息摘要。
209
- * [en-US] Minimal message snippet used by the conversation list.
207
+ * [zh-CN] 会话列表最后消息快照。
208
+ * [en-US] Last-message snapshot used by the conversation list.
209
+ *
210
+ * @remarks
211
+ * [zh-CN] 字段类型复用 Message,保留可获得的正文和元信息。历史摘要可能不完整,因此可选字段保持可选,body 保留对象类型以兼容旧缓存;新解析正文与 MessageBody 一致,不应将历史摘要强制转换为完整 Message。
212
+ * [en-US] Field types are shared with Message and preserve available content and metadata. Legacy snippets can be incomplete, so optional fields remain optional and body keeps an object type for compatibility. Newly decoded content matches MessageBody; legacy snippets should not be cast to complete Message objects.
213
+ * [zh-CN] 从旧版缓存升级时,最后消息可能缺少参与者和方向。若无法从同会话、同消息的记录恢复,`from`、`sender.userId` 及无法确定的单聊 `to` 为空字符串,`direct` 为 `undefined`。UI 应省略未知发送者资料和依赖方向的展示,不能默认认定对端发送。同步服务可用时重新同步会话列表,或等待新消息更新,可补齐能够获得的信息;保留旧消息正文,不通过清空缓存恢复身份。
214
+ * [en-US] After upgrading from a legacy cache, the last message may lack participant and direction data. If it cannot be recovered from a record for the same conversation and message, `from`, `sender.userId`, and an undetermined one-to-one `to` are empty strings, while `direct` is `undefined`. The UI should omit unknown sender details and direction-dependent rendering instead of assuming an incoming message. Re-syncing the conversation list when the sync service is available, or receiving a new message, can populate available information. Legacy message content is retained; clearing the cache does not recover identities.
210
215
  */
211
- export interface SessionMessageSnippet {
216
+ export interface SessionMessageSnippet extends Readonly<Partial<Omit<Message, 'body'>>> {
212
217
  /** [zh-CN] 服务端消息 ID。 [en-US] Message ID on the server. */
213
- readonly msgServerId: string;
214
- /** [zh-CN] 消息发送者的用户 ID。 [en-US] User ID of the message sender. */
215
- readonly from: string;
216
- /** [zh-CN] 消息接收方 ID:单聊时为对方用户 ID,群聊时为群组 ID,聊天室时为聊天室 ID。 [en-US] Message recipient ID: user ID for one-to-one chat, group ID for group chat, or chat room ID for chat room. */
217
- readonly to: string;
218
- /** [zh-CN] 发送者信息。 [en-US] Message sender information. */
219
- readonly sender: Sender;
218
+ readonly msgServerId: Message['msgServerId'];
219
+ /** [zh-CN] 消息发送者的用户 ID;无法恢复的旧缓存身份为空字符串。 [en-US] Sender user ID; empty when identity cannot be recovered from a legacy cache. */
220
+ readonly from: Message['from'];
221
+ /** [zh-CN] 实际接收方 ID:单聊为接收者用户 ID,群聊为群组 ID,聊天室为聊天室 ID;无法确定的旧缓存单聊接收者为空字符串。 [en-US] Actual recipient ID: recipient user ID for one-to-one chat, group ID for group chat, or chat room ID for chat room; empty when a legacy one-to-one recipient is undetermined. */
222
+ readonly to: Message['to'];
223
+ /** [zh-CN] 发送者信息;无法恢复的旧缓存中 userId 为空字符串。 [en-US] Sender information; userId is empty when legacy identity cannot be recovered. */
224
+ readonly sender: Message['sender'];
220
225
  /** [zh-CN] 会话 ID。 [en-US] Conversation ID. */
221
- readonly conversationId?: string;
226
+ readonly conversationId?: Message['conversationId'];
222
227
  /** [zh-CN] 会话类型。 [en-US] Conversation type. */
223
- readonly conversationType?: ConversationType;
228
+ readonly conversationType?: Message['conversationType'];
224
229
  /** [zh-CN] 消息类型。 [en-US] Message type. */
225
- readonly type?: MessageType;
230
+ readonly type?: Message['type'];
226
231
  /** [zh-CN] 消息发送状态。 [en-US] Message sending status. */
227
- readonly sendStatus?: MessageSendStatus;
232
+ readonly sendStatus?: Message['sendStatus'];
228
233
  /** [zh-CN] 消息时间戳(毫秒)。 [en-US] Message timestamp, in milliseconds. */
229
234
  readonly timestamp: number;
230
- /** [zh-CN] 消息方向:发送或接收。 [en-US] Message direction: outgoing or incoming. */
231
- readonly direct?: MessageDirect;
235
+ /** [zh-CN] 消息方向:发送或接收;仅在有可靠记录时提供,未知时为 undefined,不根据发送者推测。 [en-US] Message direction: outgoing or incoming; provided only from a reliable record, undefined when unknown, and never inferred from the sender. */
236
+ readonly direct?: Message['direct'];
232
237
  /** [zh-CN] 消息修改信息;当最后一条消息被编辑后,此字段可能存在。 [en-US] Message modification information; present when the last message has been edited. */
233
- readonly modifiedInfo?: MessageModifiedInfo;
238
+ readonly modifiedInfo?: Message['modifiedInfo'];
234
239
  /** [zh-CN] 发送者用户资料的版本时间戳(秒),用于资料补全。 [en-US] User profile version timestamp (in seconds) of the message sender, used for profile hydration. */
235
240
  readonly userInfoUpdateTime?: number;
236
241
  /** [zh-CN] 群成员名片的版本时间戳(秒),仅群聊会话的最后一条消息可能存在此字段。 [en-US] The version timestamp (in seconds) of the Group member name card. This field may only exist for the last message in group chat conversations. */
237
242
  readonly namecardUpdateTime?: number;
238
- /** [zh-CN] 消息体摘要,不包含 `type` 字段。 [en-US] Message body summary without the `type` field. */
239
- readonly body: Record<string, unknown>;
243
+ /** [zh-CN] 与 MessageBody 一致的消息正文。复合消息时这里直接是分段数组。 [en-US] Message content matching MessageBody. For a multi_body message, this is the segment array. */
244
+ readonly body: Record<string, unknown> | ReadonlyArray<Record<string, unknown>>;
245
+ /** [zh-CN] 消息扩展字段,与 onMessage 的 ext 一致;旧缓存可能缺失。 [en-US] Message extension fields, matching onMessage ext; may be absent in legacy caches. */
246
+ readonly ext?: Message['ext'];
240
247
  }
241
248
  /**
242
249
  * [zh-CN] 公开的会话列表项,来自本地会话列表缓存的投影数据。
package/types/index.d.ts CHANGED
@@ -48,7 +48,7 @@ export type { Sender } from './sender';
48
48
  * [zh-CN] SDK 支持的消息类型。
49
49
  * [en-US] Message types supported by the SDK.
50
50
  */
51
- export type MessageType = 'text' | 'image' | 'file' | 'voice' | 'video' | 'location' | 'custom' | 'cmd' | 'combine';
51
+ export type MessageType = 'text' | 'image' | 'file' | 'voice' | 'video' | 'location' | 'custom' | 'cmd' | 'combine' | 'multi_body';
52
52
  /**
53
53
  * [zh-CN] 图片资源类型常量。
54
54
  * [en-US] Image resource type constants.
@@ -151,9 +151,9 @@ export type CompatibleFile = File | MiniAppFile | ReactNativeFile;
151
151
  export interface TextMessageBody {
152
152
  /** [zh-CN] 消息文本内容。 [en-US] Message text content. */
153
153
  content: string;
154
- /** [zh-CN] 目标翻译语言代码列表。 [en-US] Target translation language codes. */
154
+ /** [zh-CN] 发送前自动翻译的目标语言代码列表,不传或空数组不翻译。 [en-US] Target language codes for automatic translation before sending; omitted or empty arrays disable translation. */
155
155
  targetLanguages?: string[];
156
- /** [zh-CN] 语言代码到翻译文本的映射。 [en-US] Mapping from language codes to translated text. */
156
+ /** [zh-CN] 语言代码到译文的映射,由自动发送翻译或接收解码填充;主动翻译 API 仅返回结果。 [en-US] Language-to-text map populated by automatic translation on send or incoming decoding; the explicit translation API only returns results. */
157
157
  translations?: Record<string, string>;
158
158
  }
159
159
  /**
@@ -307,11 +307,35 @@ export interface CombineMessageBody {
307
307
  /** [zh-CN] 当前合并消息嵌套层级。 [en-US] Current nesting level of the combined message. */
308
308
  combineLevel: number;
309
309
  }
310
+ /**
311
+ * [zh-CN] 复合消息中的一段可组合内容。`type` 只出现在复合消息的分段上。
312
+ * [en-US] One segment inside a multi_body message. `type` exists only on these segments.
313
+ */
314
+ export type MultiBodySegment = ({
315
+ type: 'text';
316
+ } & TextMessageBody) | ({
317
+ type: 'image';
318
+ } & ImageMessageBody) | ({
319
+ type: 'file';
320
+ } & FileMessageBody) | ({
321
+ type: 'voice';
322
+ } & VoiceMessageBody) | ({
323
+ type: 'video';
324
+ } & VideoMessageBody) | ({
325
+ type: 'location';
326
+ } & LocationMessageBody) | ({
327
+ type: 'custom';
328
+ } & CustomMessageBody);
329
+ /**
330
+ * [zh-CN] 复合消息正文。分段按发送顺序排列,至少 2 段。
331
+ * [en-US] Body of a multi_body message. Segments stay in send order and there are at least two.
332
+ */
333
+ export type MultiBody = ReadonlyArray<MultiBodySegment>;
310
334
  /**
311
335
  * [zh-CN] 所有消息体的联合类型。
312
336
  * [en-US] Union of all message body types.
313
337
  */
314
- export type MessageBody = TextMessageBody | ImageMessageBody | FileMessageBody | VoiceMessageBody | VideoMessageBody | LocationMessageBody | CmdMessageBody | CustomMessageBody | CombineMessageBody;
338
+ export type MessageBody = TextMessageBody | ImageMessageBody | FileMessageBody | VoiceMessageBody | VideoMessageBody | LocationMessageBody | CmdMessageBody | CustomMessageBody | CombineMessageBody | MultiBody;
315
339
  /**
316
340
  * [zh-CN] 消息对象。
317
341
  * [en-US] Message object.
@@ -339,7 +363,7 @@ export interface Message {
339
363
  ext: Record<string, unknown>;
340
364
  /** [zh-CN] 消息时间戳,单位毫秒。 [en-US] Message timestamp in milliseconds. */
341
365
  timestamp: number;
342
- /** [zh-CN] 消息体。 [en-US] Message body. */
366
+ /** [zh-CN] 消息体。复合消息时这里直接是分段数组。 [en-US] Message body. For a multi_body message, this is the segment array. */
343
367
  body: MessageBody;
344
368
  /** [zh-CN] 消息方向。 [en-US] Message direction. */
345
369
  direct?: MessageDirect;
@@ -446,11 +470,11 @@ export interface StreamMessage extends Message {
446
470
  * [en-US] File upload progress.
447
471
  */
448
472
  export interface FileUploadProgress {
449
- /** [zh-CN] 已上传字节数。 [en-US] Number of uploaded bytes. */
473
+ /** [zh-CN] 已上传字节数。复合消息按全部附件文件大小合计。 [en-US] Number of uploaded bytes. MultiBody messages use the combined size of all attachments. */
450
474
  loaded: number;
451
- /** [zh-CN] 文件总字节数。 [en-US] Total number of bytes in the file. */
475
+ /** [zh-CN] 附件总字节数。复合消息是各附件文件大小之和。 [en-US] Total attachment size in bytes. MultiBody messages use the sum of every attachment file size. */
452
476
  total?: number;
453
- /** [zh-CN] 上传进度百分比。 [en-US] Upload progress as a percentage. */
477
+ /** [zh-CN] 相对全部附件总大小的上传百分比。 [en-US] Upload percentage relative to the total size of all attachments. */
454
478
  percent?: number;
455
479
  }
456
480
  /**
@@ -498,7 +522,7 @@ export interface SendMessageOptions {
498
522
  onFileUploadComplete?: (result: FileUploadResult) => void;
499
523
  /** [zh-CN] 附件上传失败回调。 [en-US] Triggered when the attachment upload fails. */
500
524
  onFileUploadError?: (error: Error) => void;
501
- /** [zh-CN] 附件上传取消回调。 [en-US] Triggered when the attachment upload is cancelled.*/
525
+ /** [zh-CN] 附件上传取消回调。 [en-US] Triggered when the attachment upload is cancelled. */
502
526
  onFileUploadCanceled?: () => void;
503
527
  }
504
528
  /**