k2-im 0.1.49 → 0.1.51

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,8 +1,10 @@
1
1
  # `@k2/im`
2
2
 
3
- IM SDK 聚合包的 workspace 源码。发布到 npm 官方源时,公共包名为 `k2-im`;workspace 内部名称仍为 `@k2/im`,当前提供一个组合根入口和五个按职责拆分的 subpath:
3
+ IM SDK 聚合包的 workspace 源码。发布到 npm 官方源时,公共包名为 `k2-im`;workspace 内部名称仍为 `@k2/im`,当前提供一个组合根入口和七个按职责拆分的 subpath:
4
4
 
5
5
  - `@k2/im/sdk`:框架无关的产品 SDK。
6
+ - `@k2/im/browser`:浏览器协议客户端和浏览器能力适配。
7
+ - `@k2/im/request-crypto`:普通请求可独立加载的 WASM 加解密入口,不依赖 IM 客户端或框架。
6
8
  - `@k2/im/i18n`:语义文案和 formatter。
7
9
  - `@k2/im/chat-kit`:Core-only Chat Runtime、command service 和纯工具。
8
10
  - `@k2/im/chat-kit/pinia`:按 Pinia 实例隔离的 Context,以及 Session、Conversation List、Thread、Composer、Message Operations、Search、Profile/Directory、Media 八个共享领域 Store。
@@ -12,7 +14,7 @@ IM SDK 聚合包的 workspace 源码。发布到 npm 官方源时,公共包名
12
14
 
13
15
  Chat Kit Core 与 Core-only 入口不依赖 Vue/Pinia/路由,也不自行获取、查询、修改、监听或持有页面 DOM/Canvas 等环境资源。真实跨宿主复用的浏览器能力可以在合同稳定后进入明确的 Browser/Vue 子层,不要求整个 Chat Kit Worker-safe。`chat-kit/pinia` 按领域提供 PC/H5 直接消费的共享 Stores,但不读取宿主认证、路由或反馈,不拥有页面节点/Observer 生命周期、不在 `$state` 中持有 DOM/Canvas 对象,不持有模块级全局 Runtime,也不提供完整产品页面。Web PC 与 H5 各自维护平台 Store 和最终 UI;Pinia/Vue 作为 peer dependency 由宿主提供,避免发布产物打包第二份框架运行时。
14
16
 
15
- 当前只共享已经由 PC/H5 消费的无产品样式 Thread Surface、viewport、结构 slots 和 renderer registry,不建设带品牌视觉或平台交互的公共 UI Kit。其它 Vue 组件仍需两个真实消费者证明合同一致后再独立评估。当前也不预建 Worker 入口或要求整个 Chat Kit Worker-safe;未来只有真实 Worker 切片的专用入口和依赖闭包需要排除 Vue/Pinia,并继续由 SDK/Core 对应能力 owner 提供执行 adapter。
17
+ 当前只共享已经由 PC/H5 消费的无产品样式 Thread Surface、viewport、结构 slots 和 renderer registry,不建设带品牌视觉或平台交互的公共 UI Kit。其它 Vue 组件仍需两个真实消费者证明合同一致后再独立评估。SDK 的账号级计算 Worker 使用私有资产入口;只有该执行入口及依赖闭包需要排除 Vue/Pinia,不要求整个 Chat Kit Worker-safe。Worker 执行端口仍由 SDK/Core 对应能力提供。
16
18
 
17
19
  当前版本 `0.1.0` 仅用于预稳定发布组织验证,不代表稳定版。开发期 Web PC 通过 `workspace:*` 使用 source mode;发布候选通过 [`docs/im-sdk/public-release-runbook.md`](../../docs/im-sdk/public-release-runbook.md) 生成 `k2-im` 的 dist-only tarball 并上传 npm 官方源,H5 在包可查询后通过 alias 使用精确版本和 lockfile integrity。
18
20
 
@@ -20,6 +22,23 @@ Chat Kit Core 与 Core-only 入口不依赖 Vue/Pinia/路由,也不自行获
20
22
 
21
23
  发布候选已通过独立 Rspack clean-consumer Gate:AWS 仅在首次上传时加载,crypto WASM 与 Speex 由 dist 自持,fixture 不需要 Node shim、WASM loader、asset copy 或 postinstall。该证据只证明普通 Rspack 消费者可以编译,不等于 H5 已同步当前 tarball 或真实媒体链路已验收;H5 开发期通过本地同步脚本完成自身 typecheck/Rspack 验证,上线准备阶段再使用内部 Registry 精确版本完成 Docker/Jenkins 验证。
22
24
 
25
+ ## 普通请求加解密
26
+
27
+ 非 IM 页面使用独立入口,避免从组合根入口加载 SDK:
28
+
29
+ ```ts
30
+ import { loadBrowserCryptoDriver } from '@k2/im/request-crypto';
31
+
32
+ const crypto = await loadBrowserCryptoDriver();
33
+ const encrypted = crypto.encryptAes(new TextEncoder().encode(JSON.stringify(payload)));
34
+ ```
35
+
36
+ 导入子入口本身不会请求 WASM;调用 loader 后才加载请求加解密实现,并返回同步 driver。并发调用复用模块内的加载 Promise 和实例,失败向调用方传播。SDK Core 请求客户端在 local/public 产物中引用同一个入口,同一页面、同一包实例只初始化一次请求 WASM。不同页面或 Worker 不共享 JavaScript 模块实例。
37
+
38
+ H5 的 `common/net/crypto` 仅保留原函数名的兼容转发,不再保存另一份 WASM 或生成胶水。PC 的 workspace 协议客户端仍直接消费 `@k2/crypto/browser`,其请求客户端由宿主注入 IM,共享该实现的模块缓存。请求 WASM 与消息 RSA/TEA、媒体指纹 `hash-wasm` 职责不同。
39
+
40
+ 可以运行 `node scripts/check-im-request-crypto.mjs --consumer-root <已安装 SDK 的消费者目录>` 检查独立入口的真实 Rspack 依赖图;生成页面验证普通请求初始化后,延后加载 IM 并发请求不会再次获取和实例化 WASM。此验证使用合成数据和 mock transport,不代表真实后端或首屏耗时测试。
41
+
23
42
  ## 国际化(i18n)
24
43
 
25
44
  SDK 内置领域文案,开箱即用,宿主渲染层无需自建系统消息 / 失败气泡 / 会话预览的文案表。
@@ -0,0 +1 @@
1
+ import'./chunk-WOT6VMZA.js';import {loadBrowserCryptoDriver}from'./request-crypto.js';import {parse,isInteger,isSafeNumber}from'lossless-json';import qt from'axios';var W="b097f885e9577cdb04d9faf91aa08329";function bt(t){return Object.keys(t).sort((r,o)=>r.localeCompare(o,void 0,{numeric:true})).map(r=>r+t[r]).join("")}function V(t,e){let r=bt(e);return t.cryptMd5(W+r+W).toUpperCase()}var J="123456789";var Y={core:"/api/h5",wallet:"/api/h5",bi:"/bi/h5",im:""};function wt(t){return isInteger(t)?isSafeNumber(t)?Number.parseInt(t,10):t:Number.parseFloat(t)}function S(t){return parse(t,void 0,{parseNumber:wt})}var G="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";function K(t){return t?t instanceof Headers?Object.fromEntries(t.entries()):Array.isArray(t)?Object.fromEntries(t):{...t}:{}}function X(t){return t?t instanceof Headers?Object.fromEntries(t.entries()):Object.fromEntries(Object.entries(t).map(([e,r])=>[e,Array.isArray(r)?r.join(", "):String(r)])):{}}function Q(t,e){let r=e.toLowerCase();return Object.entries(t).find(([n])=>n.toLowerCase()===r)?.[1]??null}function x(t,e=2){return t.toString().padStart(e,"0")}function Z(t){return `${t.getFullYear()}-${x(t.getMonth()+1)}-${x(t.getDate())} ${x(t.getHours())}:${x(t.getMinutes())}:${x(t.getSeconds())}`}function ht(t){if(t!=null)return typeof t=="string"?t:typeof t=="number"||typeof t=="boolean"||typeof t=="bigint"?String(t):t&&typeof t=="object"?JSON.stringify(t):String(t)}function tt(t){let e={};for(let[r,o]of Object.entries(t)){let n=ht(o);n!==void 0&&(e[r]=n);}return e}function et(t=16){let e="";for(let r=0;r<t;r+=1){let o=Math.floor(Math.random()*G.length);e+=G.charAt(o);}return e}function rt(t){let e={deviceBrand:"web",deviceInfo:"web",deviceNumber:"",deviceToken:"web"},r=t||(typeof navigator<"u"?navigator.userAgent:"");if(!r)return e;let o=r.match(/(Chrome|Firefox|Safari|Edg)\/([\d.]+)/i);return o?{deviceBrand:"web",deviceInfo:`${o[1]}/${o[2]}`,deviceNumber:o[2]??"",deviceToken:"web"}:{...e,deviceInfo:r}}function ot(t){let e=String(t||"").trim().toLowerCase();return e==="cn"||e.startsWith("zh-cn")?"cn":e==="tc"||e.startsWith("zh-tw")||e.startsWith("zh-hk")?"tc":e==="vn"||e.startsWith("vi")?"vn":e==="th"||e.startsWith("th")?"th":e==="en"||e.startsWith("en")?"en":"cn"}function b(t,e){return {baseUrl:e?.baseUrl??"",prefix:e?.prefix??Y[t]}}function Ot(t){return t.replace(/^\/+|\/+$/g,"")}function nt(t,e,r){let o=t.replace(/\/+$/g,""),n=Ot(e),s=r.startsWith("/")?r:`/${r}`,a=n?`/${n}`:"";return `${o}${a}${s}`}function st(t,e){let r=new URLSearchParams(Object.entries(e).map(([l,c])=>[l,String(c)])).toString();if(!r)return t;let o=t.indexOf("#"),n=o>=0?t.slice(0,o):t,s=o>=0?t.slice(o):"",a=n.includes("?")?n.endsWith("?")||n.endsWith("&")?"":"&":"?";return `${n}${a}${r}${s}`}function N(t){return !!t&&t.toLowerCase().includes("application/json")}function H(t){return !!t&&t.toLowerCase().startsWith("text/")}function _(t){return t==null?new Uint8Array:typeof t=="string"?new TextEncoder().encode(t):t instanceof ArrayBuffer?new Uint8Array(t):ArrayBuffer.isView(t)?new Uint8Array(t.buffer,t.byteOffset,t.byteLength):new TextEncoder().encode(JSON.stringify(t))}function k(t){return typeof t=="string"?t:new TextDecoder().decode(_(t))}function it(t){return typeof FormData<"u"&&t instanceof FormData}var St=new Set(["content-etag","token","req-id"]);function lt(t){let e=t.rawBody,r=e!==null&&typeof e=="object"&&"code"in e?e.code:void 0;return {transportStatus:t.transportStatus,...t.httpStatus===void 0?{}:{httpStatus:t.httpStatus},...typeof r=="number"&&Number.isFinite(r)?{businessCode:r}:{}}}function kt(t){return Number.isInteger(t)&&t>=400&&t<=599?t:502}function Bt(t,e){return {code:t.status,msg:t.statusText,data:null,time:Math.floor(e/1e3)}}function Mt(t,e){return {code:502,msg:t instanceof Error?t.message:String(t),data:null,time:Math.floor(e/1e3)}}function Ut(t){if(!t||typeof t!="object")return false;let e=t;return Number.isFinite(e.code)&&"data"in e}function ct(t,e,r,o){if(N(r))return S(k(e.data));if(H(r))return k(e.data);let n=_(e.data),s=o?t.crypto.decryptAes(n):n,a=new TextDecoder().decode(s);return S(a)}async function pt(t,e,r){if(!t.onDebugRecord)return;let o={...e.meta,response:e.response,...r?{error:{name:r instanceof Error?r.name:void 0,message:r instanceof Error?r.message:String(r)}}:{}};try{await t.onDebugRecord(o);}catch{}}function vt(t,e,r,o,n){if(r.customUrl)return t;let s=b(o,e.config.targets[o]),a=r.baseUrl??s.baseUrl;return nt(a,n,t)}function Lt(t,e){let r=new AbortController,o=setTimeout(()=>r.abort(),t),n=()=>r.abort();return e&&(e.aborted?r.abort():e.addEventListener("abort",n,{once:true})),{signal:r.signal,dispose:()=>{clearTimeout(o),e?.removeEventListener("abort",n);}}}async function Nt(t,e,r,o,n,s,a){let l=X(e.headers),c=Q(l,"content-type");if(e.status<200||e.status>=300){let i;if(o)i=void 0;else try{i=ct(t,e,c,r);}catch{i=void 0;}return {response:Ut(i)?i:Bt(e,a),meta:{...n,transportStatus:"http_error",httpStatus:e.status,contentType:c,responseHeaders:l,...i!==void 0?{rawBody:i}:{},timings:{totalMs:a-s,upstreamMs:a-s}}}}let u=Date.now();if(o){let i=_(e.data),p=Date.now();return {response:{code:e.status,data:i,time:Math.floor(p/1e3)},meta:{...n,transportStatus:"success",httpStatus:e.status,contentType:c,responseHeaders:l,rawBody:i,timings:{totalMs:p-s,upstreamMs:a-s,parseMs:p-u}}}}try{if(N(c)){let P=ct(t,e,c,!1),f=Date.now();return {response:P,meta:{...n,transportStatus:"success",httpStatus:e.status,contentType:c,responseHeaders:l,rawBody:P,timings:{totalMs:f-s,upstreamMs:a-s,parseMs:f-u}}}}if(H(c)){let P=k(e.data),f=Date.now();return {response:{code:e.status,msg:e.statusText,data:P,time:Math.floor(f/1e3)},meta:{...n,transportStatus:"success",httpStatus:e.status,contentType:c,responseHeaders:l,rawBody:P,timings:{totalMs:f-s,upstreamMs:a-s,parseMs:f-u}}}}let i=_(e.data),p=Date.now(),R=r?t.crypto.decryptAes(i):i,C=Date.now(),y=new TextDecoder().decode(R),h=S(y),E=Date.now();return {response:h,meta:{...n,transportStatus:"success",httpStatus:e.status,contentType:c,responseHeaders:l,rawBody:h,timings:{totalMs:E-s,upstreamMs:a-s,parseMs:E-u,...r?{decryptMs:C-p}:{}}}}}catch(i){let p=Date.now();return {response:{code:kt(e.status),msg:i instanceof Error?i.message:String(i),data:null,time:Math.floor(p/1e3)},meta:{...n,transportStatus:"invalid_response",httpStatus:e.status,contentType:c,responseHeaders:l,timings:{totalMs:p-s,upstreamMs:a-s,parseMs:p-u}}}}}function Ht(t){return async function(r,o={}){let n=t.now??(()=>new Date),s=t.randomNonce??(()=>J),a=t.randomRequestId??(()=>`req-${crypto.randomUUID().replaceAll("-","")}`),l=t.randomETag??(()=>et()),c=(o.method??"POST").toUpperCase(),u=o.target??"core",i=b(u,t.config.targets[u]),p=o.prefix??i.prefix??"",R=o.body!=null,C=tt(o.data??{}),y=r==="/v1/feedback"&&p==="/bi/h5",h=y?false:o.encrypt??!R,E=y?false:o.sign??!R,P=n(),f=l(),q=a(),mt=o.token??await t.getToken?.()??"",dt=ot(o.locale??t.getLocale?.()),O=t.getBrowserInfo?.()??rt(o.userAgent),B={app_id:"5",os_type:"4",app_version:t.config.appVersion,sign_method:"md5",timestamp:Z(P),nonce:s()},g={...B,...C},M=B,F=C,w=g;!y&&E&&(g.sign=t.crypto.generateSign("api",t.config.envType,g,f),B.sign=g.sign);let Pt={app_version:t.config.appVersion,"X-ENC-VERSION":t.config.appVersion,"X-APP-TYPE":"h5","Client-type":"2","Content-ETag":f,"brand-id":t.config.brandId,"channel-id":o.channelId||t.config.channelId,device_brand:O.deviceBrand,device_info:O.deviceInfo,device_number:O.deviceNumber,device_token:O.deviceToken,lang:dt,"mch-id":t.config.merchantId,"req-id":q,token:mt},Tt=K(o.headers),At=Object.fromEntries(Object.entries(Tt).filter(([T])=>!St.has(T.toLowerCase()))),U=r,I,m={...Pt,...At};if(R)I=o.body??void 0,M=null,F=o.data??null,w=o.data??null;else if(y){let T=String(P.getTime()),d={"app-id":"1","app-version":t.config.appVersion,"sys-type":"h5","sys-version":O.deviceNumber,time:T};M=d,m={...m,...d,sign:V(t.crypto,d),"Content-Type":"application/json"},I=JSON.stringify(C),w=C;}else c==="GET"?(U=st(U,g),w=g):h?(I=t.crypto.encryptAes(new TextEncoder().encode(JSON.stringify(g))),m["Content-Type"]=m["Content-Type"]||"application/octet-stream",w=g):(I=JSON.stringify(g),m["Content-Type"]=m["Content-Type"]||"application/json",w=g);it(I)&&delete m["Content-Type"];let v=vt(U,t,o,u,p),L=Date.now(),j=o.timeoutMs??6e4,$=Lt(j,o.signal),z={requestId:q,eTag:f,method:c,upstreamUrl:v,requestHeaders:m,requestCommonParams:M,requestBusinessParams:F,requestParams:w},D;try{let T=await t.transport({url:v,method:c,headers:m,body:I,signal:$.signal,timeoutMs:j,onUploadProgress:o.onUploadProgress}),d=Date.now(),A=await Nt(t,T,h&&!y,o.responseMode==="raw",z,L,d);return o.onResponse&&(D=lt(A.meta),A.observation=D),await pt(t,A),!o.skipBusinessResponseHandler&&t.onBusinessResponse&&await t.onBusinessResponse(A.response,{request:{path:r,method:c,target:u,url:v},options:o}),A}catch(T){let d=Date.now(),A={response:Mt(T,d),...D===void 0?{}:{observation:D},meta:{...z,transportStatus:"network_error",timings:{totalMs:d-L,upstreamMs:d-L}}};return await pt(t,A,T),A}finally{$.dispose();}}}function ut(t){let e=Ht(t);return async function(o,n={}){let s=await e(o,n);return await n.onResponse?.(s.observation??lt(s.meta)),s.response}}function ft(t={}){let e={core:b("core",{baseUrl:t.coreBaseUrl||"",...t.targets?.core}),wallet:b("wallet",{baseUrl:t.walletBaseUrl||"",...t.targets?.wallet}),bi:b("bi",{baseUrl:t.biBaseUrl||"",...t.targets?.bi}),im:b("im",{baseUrl:t.imBaseUrl||"",...t.targets?.im})};return {appVersion:t.appVersion||"",brandId:t.brandId||"",merchantId:t.merchantId||"",channelId:t.channelId||"",envType:t.envType||"prod",targets:e}}function gt(t={}){let e=t.axios??qt.create();return async r=>{let o=await e.request({url:r.url,method:r.method,headers:r.headers,data:r.body,signal:r.signal,timeout:r.timeoutMs,onUploadProgress:r.onUploadProgress?n=>{let s=typeof n.progress=="number"?n.progress:typeof n.total=="number"&&n.total>0?n.loaded/n.total:void 0;r.onUploadProgress?.({loaded:n.loaded,...typeof n.total=="number"?{total:n.total}:{},...typeof s=="number"?{progress:s}:{},event:n});}:void 0,responseType:"arraybuffer",transformResponse:n=>n,validateStatus:()=>true});return {status:o.status,statusText:o.statusText,headers:o.headers,data:o.data}}}async function ae(t={}){let e=await loadBrowserCryptoDriver(),r=ft(t.config);return ut({...t,config:r,crypto:e,transport:t.transport??gt()})}export{ae as createBrowserApiProtocolClient};
@@ -0,0 +1,2 @@
1
+ import {a as a$1}from'./chunk-WOT6VMZA.js';function M(e){return {cryptMd5:e.crypt_md5,generateSign:e.generate_sign,encryptAes:e.encrypt_aes,decryptAes:e.decrypt_aes}}var m={};a$1(m,{__wbg_get_67b2ba62fc30de12:()=>P,__wbg_get_b9b93047fe3cf45b:()=>z,__wbg_length_e2d2a49132c1b256:()=>G,__wbg_ownKeys_3930041068756f1f:()=>Q,__wbg_set_wasm:()=>E,__wbindgen_debug_string:()=>X,__wbindgen_init_externref_table:()=>Y,__wbindgen_is_object:()=>Z,__wbindgen_string_get:()=>v,__wbindgen_string_new:()=>ee,__wbindgen_throw:()=>te,crypt_md5:()=>V,decrypt_aes:()=>J,encrypt_aes:()=>H,generate_sign:()=>K});var c;function E(e){c=e;}function U(e){let t=c.__externref_table_alloc();return c.__wbindgen_export_2.set(t,e),t}function C(e,t){try{return e.apply(this,t)}catch(n){let r=U(n);c.__wbindgen_exn_store(r);}}function S(e){let t=typeof e;if(t=="number"||t=="boolean"||e==null)return `${e}`;if(t=="string")return `"${e}"`;if(t=="symbol"){let o=e.description;return o==null?"Symbol":`Symbol(${o})`}if(t=="function"){let o=e.name;return typeof o=="string"&&o.length>0?`Function(${o})`:"Function"}if(Array.isArray(e)){let o=e.length,s="[";o>0&&(s+=S(e[0]));for(let i=1;i<o;i++)s+=`, ${S(e[i])}`;return s+="]",s}let n=/\[object ([^\]]+)\]/.exec(toString.call(e)),r;if(n&&n.length>1)r=n[1];else return toString.call(e);if(r=="Object")try{return `Object(${JSON.stringify(e)})`}catch{return "Object"}return e instanceof Error?`${e.name}: ${e.message}
2
+ ${e.stack}`:r}var u=0,g=null;function a(){return (g===null||g.byteLength===0)&&(g=new Uint8Array(c.memory.buffer)),g}var L=typeof TextEncoder>"u"?(0, module.require)("util").TextEncoder:TextEncoder,p=new L("utf-8"),N=typeof p.encodeInto=="function"?function(e,t){return p.encodeInto(e,t)}:function(e,t){let n=p.encode(e);return t.set(n),{read:e.length,written:n.length}};function d(e,t,n){if(n===void 0){let _=p.encode(e),f=t(_.length,1)>>>0;return a().subarray(f,f+_.length).set(_),u=_.length,f}let r=e.length,o=t(r,1)>>>0,s=a(),i=0;for(;i<r;i++){let _=e.charCodeAt(i);if(_>127)break;s[o+i]=_;}if(i!==r){i!==0&&(e=e.slice(i)),o=n(o,r,r=i+e.length*3,1)>>>0;let _=a().subarray(o+i,o+r),f=N(e,_);i+=f.written,o=n(o,r,i,1)>>>0;}return u=i,o}var l=null;function y(){return (l===null||l.buffer.detached===true||l.buffer.detached===void 0&&l.buffer!==c.memory.buffer)&&(l=new DataView(c.memory.buffer)),l}function $(e){return e==null}var R=typeof TextDecoder>"u"?(0, module.require)("util").TextDecoder:TextDecoder,W=new R("utf-8",{ignoreBOM:true,fatal:true});W.decode();function w(e,t){return e=e>>>0,W.decode(a().subarray(e,e+t))}function V(e){let t,n;try{let r=d(e,c.__wbindgen_malloc,c.__wbindgen_realloc),o=u,s=c.crypt_md5(r,o);return t=s[0],n=s[1],w(s[0],s[1])}finally{c.__wbindgen_free(t,n,1);}}function q(e){let t=c.__wbindgen_export_2.get(e);return c.__externref_table_dealloc(e),t}function K(e,t,n,r){let o,s;try{let i=d(e,c.__wbindgen_malloc,c.__wbindgen_realloc),_=u,f=d(t,c.__wbindgen_malloc,c.__wbindgen_realloc),I=u,k=$(r)?0:d(r,c.__wbindgen_malloc,c.__wbindgen_realloc),F=u,b=c.generate_sign(i,_,f,I,n,k,F),T=b[0],D=b[1];if(b[3])throw T=0,D=0,q(b[2]);return o=T,s=D,w(T,D)}finally{c.__wbindgen_free(o,s,1);}}function j(e,t){let n=t(e.length*1,1)>>>0;return a().set(e,n/1),u=e.length,n}function B(e,t){return e=e>>>0,a().subarray(e/1,e/1+t)}function H(e){let t=j(e,c.__wbindgen_malloc),n=u,r=c.encrypt_aes(t,n),o=B(r[0],r[1]).slice();return c.__wbindgen_free(r[0],r[1]*1,1),o}function J(e){let t=j(e,c.__wbindgen_malloc),n=u,r=c.decrypt_aes(t,n),o=B(r[0],r[1]).slice();return c.__wbindgen_free(r[0],r[1]*1,1),o}function P(){return C((e,t)=>Reflect.get(e,t),arguments)}function z(e,t){return e[t>>>0]}function G(e){return e.length}function Q(){return C(e=>Reflect.ownKeys(e),arguments)}function X(e,t){let n=S(t),r=d(n,c.__wbindgen_malloc,c.__wbindgen_realloc),o=u;y().setInt32(e+4,o,true),y().setInt32(e+0,r,true);}function Y(){let e=c.__wbindgen_export_2,t=e.grow(4);e.set(0,void 0),e.set(t+0,void 0),e.set(t+1,null),e.set(t+2,true),e.set(t+3,false);}function Z(e){let t=e;return typeof t=="object"&&t!==null}function v(e,t){let n=t,r=typeof n=="string"?n:void 0,o=$(r)?0:d(r,c.__wbindgen_malloc,c.__wbindgen_realloc),s=u;y().setInt32(e+4,s,true),y().setInt32(e+0,o,true);}function ee(e,t){return w(e,t)}function te(e,t){throw new Error(w(e,t))}var h=null,x=null;async function ne(){let e=await fetch(new URL("./vendor/crypto/index_bg.wasm",import.meta.url));if(!e.ok)throw new Error(`crypto wasm \u52A0\u8F7D\u5931\u8D25\uFF1AHTTP ${e.status}`);let t={"./index_bg.js":m},n;if(typeof WebAssembly.instantiateStreaming=="function")try{n=await WebAssembly.instantiateStreaming(e.clone(),t);}catch{n=await WebAssembly.instantiate(await e.arrayBuffer(),t);}else n=await WebAssembly.instantiate(await e.arrayBuffer(),t);return E(n.instance.exports),n.instance.exports.__wbindgen_start(),M(m)}async function re(){return x||(x=ne().catch(e=>{throw x=null,e})),h=await x,h}await re();function A(){if(!h)throw new Error("crypto driver \u5C1A\u672A\u521D\u59CB\u5316\uFF0C\u8BF7\u5148 await loadBrowserCryptoDriver()");return h}function ie(e){return A().cryptMd5(e)}function se(e,t,n,r=null){return A().generateSign(e,t,n,r)}function _e(e){return A().encryptAes(e)}function ue(e){return A().decryptAes(e)}export{ie as cryptMd5,ue as decryptAes,_e as encryptAes,se as generateSign,re as loadBrowserCryptoDriver};
package/dist/browser.js CHANGED
@@ -1 +1 @@
1
- export{Ua as canCopyImageToClipboard,Va as copyImageToClipboard,B as createLazyBrowserApiProtocolClient}from'./chunk-SWWW7C2U.js';import'./chunk-527YCOBJ.js';import'./chunk-WOT6VMZA.js';
1
+ export{Va as canCopyImageToClipboard,Wa as copyImageToClipboard,C as createLazyBrowserApiProtocolClient}from'./chunk-KV7I2SAC.js';import'./chunk-Q6TFPZMF.js';import'./chunk-WOT6VMZA.js';
@@ -114,8 +114,11 @@ type LastMessageSummary = {
114
114
  kind: 'transfer';
115
115
  } | {
116
116
  kind: 'gift';
117
- } | {
117
+ }
118
+ /** 直播间分享。`title` 是房间标题,没有房间标题再用主播昵称。 */
119
+ | {
118
120
  kind: 'liveRoom';
121
+ title?: string;
119
122
  } | {
120
123
  kind: 'call';
121
124
  callKind: ImCallKind;
@@ -3120,6 +3123,8 @@ interface ImSdkErrorShape {
3120
3123
  category: ImSdkErrorCategory;
3121
3124
  operation: string;
3122
3125
  retryable: boolean;
3126
+ /** 执行设施已终止;宿主可在保存草稿后提供用户主动整页刷新,不得自动重试。 */
3127
+ recovery?: 'reload-app';
3123
3128
  conversation?: ConversationRef;
3124
3129
  details?: Readonly<Record<string, unknown>>;
3125
3130
  cause?: unknown;
@@ -4414,8 +4419,11 @@ type ConversationLastMessageSummary = {
4414
4419
  kind: 'transfer';
4415
4420
  } | {
4416
4421
  kind: 'gift';
4417
- } | {
4422
+ }
4423
+ /** 直播间分享。`title` 是房间标题,没有房间标题再用主播昵称。 */
4424
+ | {
4418
4425
  kind: 'liveRoom';
4426
+ title?: string;
4419
4427
  } | {
4420
4428
  kind: 'redPacket';
4421
4429
  } | {
@@ -4914,7 +4922,7 @@ interface ForwardMessagesResult {
4914
4922
  * 只有 `sendText` 消费文本草稿;媒体、语音、文件、GIF 与名片拥有独立选择态,不清理 Composer 文本。
4915
4923
  */
4916
4924
  interface ConversationMessageApi {
4917
- /** 发送文本;mention 是否可用及其编码由精确会话 sibling 决定。 */
4925
+ /** 发送文本;单聊新文本固定加密,mention 与其他会话的加密方式由各自 sibling 决定。 */
4918
4926
  sendText: (text: string, options?: TextMessageOptions) => Promise<SendMessageResult>;
4919
4927
  /** 分享个人、普通群或社群名片;发送目标仍由当前 handle 的精确会话身份决定。 */
4920
4928
  sendContact: (contact: ContactCardInput, options?: ContactMessageOptions) => Promise<SendMessageResult>;
@@ -5178,14 +5186,16 @@ interface ConversationHandle<TType extends ProductHandleConversationType = Produ
5178
5186
  /**
5179
5187
  * 按稳定消息 id 打开目标附近的连续历史窗口。
5180
5188
  *
5181
- * @remarks GROUP 完整置顶预览可按消息时间定位;CHANNEL 置顶未缓存时先按 ID 取回正式历史记录,
5182
- * 以服务端游标准备前后相邻页后一次替换窗口;准备失败保留原窗口。只含控制行的页每侧最多继续四页,
5183
- * 预算用完仍保留分页能力。短期搜索结果只作为只读锚点;partial 置顶和过期搜索结果明确拒绝定位。
5184
- * @throws 没有可用锚点、远端确认缺失或目标不属于当前复合会话时,抛出稳定 `IM_SDK_MESSAGE_NOT_FOUND`
5185
- * (`category: 'state'`、`retryable: false`、`details.messageId`)。这是常见产品结果而非链路异常,
5186
- * 调用方应据此与真正的存储/协议失败区分反馈,不要解析错误文案。
5189
+ * @remarks 冷启动置顶和搜索结果只提供目标 id;目标不在当前窗口或本地原文时,须经精确会话
5190
+ * `by_id` 确认真实原文及排序位置。预览或搜索位置不能充当历史游标。
5191
+ * GROUP/CHANNEL 使用远端相邻页准备两侧上下文后一次替换窗口;失败保留原窗口。
5192
+ * `signal` 取消本次定位,阻止提交准备中的窗口;不会撤销共享按 ID 查询已取得的合法缓存。
5193
+ * @throws 仅 `by_id` 明确确认缺失时抛 `IM_SDK_MESSAGE_NOT_FOUND`;没有可信原文时抛
5194
+ * `IM_SDK_MESSAGE_ANCHOR_UNAVAILABLE`。存储、权限与协议异常保留原错误身份。
5187
5195
  */
5188
- loadAround: (messageId: string) => Promise<void>;
5196
+ loadAround: (messageId: string, options?: {
5197
+ signal?: AbortSignal;
5198
+ }) => Promise<void>;
5189
5199
  /**
5190
5200
  * 清空当前账号下此精确会话的本机历史。
5191
5201
  *
@@ -5480,6 +5490,18 @@ interface ImMediaTasksApi {
5480
5490
  file: Blob;
5481
5491
  signal?: AbortSignal;
5482
5492
  }) => Promise<Blob | null>;
5493
+ /**
5494
+ * 为本地图片生成仅供胶片条/相册格显示的缩略图,不上传、不写缓存、不改变发送文件与元信息。
5495
+ * JPEG 按文件签名识别,最长边最多 256px 且不放大,与发送预读共用账号的图片解码预算。
5496
+ * 其它格式直接返回原 Blob,保留透明与动画;宿主负责返回 Blob 的 object URL 生命周期。
5497
+ * 生成失败返回 null 并记录诊断;取消、账号失效和未装配能力保持明确错误。
5498
+ * @param input.file 原始本地图片;返回的显示资源不得代替它传入发送命令。
5499
+ * @param input.signal 删除文件、关闭预览或切换账号时取消请求。
5500
+ */
5501
+ prepareLocalImageThumbnail: (input: {
5502
+ file: Blob;
5503
+ signal?: AbortSignal;
5504
+ }) => Promise<Blob | null>;
5483
5505
  }
5484
5506
 
5485
5507
  /**
@@ -7790,6 +7812,17 @@ type ImMediaStoreError = Readonly<Omit<ImSdkErrorShape, 'cause' | 'details'>>;
7790
7812
  type ImMediaTaskPresentation = Readonly<Omit<ImMediaTask, 'error'> & Partial<MediaTransferEstimate> & {
7791
7813
  error?: ImMediaStoreError;
7792
7814
  }>;
7815
+ /** 一条消息的上传展示;仅由 SDK 任务投影,不参与上传控制或状态流转。 */
7816
+ interface ImMediaUploadPresentation {
7817
+ /** SDK 快照中最后一个上传任务,含终态;用于暂停、剩余时间及失败反馈。 */
7818
+ readonly task?: ImMediaTaskPresentation;
7819
+ /** 最后一个 queued/running/paused 任务的进度;queued 为 0,没有活动任务时缺省。 */
7820
+ readonly percent?: number;
7821
+ /** 任一上传任务明确处于 queued + waiting-upload-slot。 */
7822
+ readonly waiting: boolean;
7823
+ /** 任一上传任务明确处于 running + finalizing。 */
7824
+ readonly finalizing: boolean;
7825
+ }
7793
7826
  /** 刷新页面导致上传源丢失后,通知宿主重新打开文件选择器所需的信息;File 本身不进入 Store。 */
7794
7827
  interface ImMediaReplacementRequest {
7795
7828
  messageId: string;
@@ -7807,6 +7840,7 @@ type ImMediaOperationResult = Readonly<{
7807
7840
  reason: 'not-ready' | 'not-found' | 'ambiguous' | 'failed' | 'stale';
7808
7841
  error?: ImMediaStoreError;
7809
7842
  }>;
7843
+ type MediaMessageIdentity = Pick<ImMessageView, 'conversationType' | 'conversationId' | 'id'>;
7810
7844
  /**
7811
7845
  * 下载任务在 `downloadTasks` / `downloadProgress` 里的键。
7812
7846
  *
@@ -7817,6 +7851,8 @@ type ImMediaOperationResult = Readonly<{
7817
7851
  declare function imMediaDownloadKey(messageId: string, itemIndex?: number): string;
7818
7852
  declare function defineImMediaStore(pinia: Pinia): pinia.StoreDefinition<"k2-im-media", Pick<{
7819
7853
  tasks: any;
7854
+ tasksForMessage: (message: MediaMessageIdentity) => readonly ImMediaTaskPresentation[];
7855
+ uploadForMessage: (message: MediaMessageIdentity) => ImMediaUploadPresentation;
7820
7856
  error: any;
7821
7857
  uploadProgress: any;
7822
7858
  uploadTasks: any;
@@ -7858,6 +7894,8 @@ declare function defineImMediaStore(pinia: Pinia): pinia.StoreDefinition<"k2-im-
7858
7894
  dispose: () => void;
7859
7895
  }, any>, Pick<{
7860
7896
  tasks: any;
7897
+ tasksForMessage: (message: MediaMessageIdentity) => readonly ImMediaTaskPresentation[];
7898
+ uploadForMessage: (message: MediaMessageIdentity) => ImMediaUploadPresentation;
7861
7899
  error: any;
7862
7900
  uploadProgress: any;
7863
7901
  uploadTasks: any;
@@ -7899,6 +7937,8 @@ declare function defineImMediaStore(pinia: Pinia): pinia.StoreDefinition<"k2-im-
7899
7937
  dispose: () => void;
7900
7938
  }, any>, Pick<{
7901
7939
  tasks: any;
7940
+ tasksForMessage: (message: MediaMessageIdentity) => readonly ImMediaTaskPresentation[];
7941
+ uploadForMessage: (message: MediaMessageIdentity) => ImMediaUploadPresentation;
7902
7942
  error: any;
7903
7943
  uploadProgress: any;
7904
7944
  uploadTasks: any;
@@ -9023,7 +9063,7 @@ declare function useImProfileDirectoryStore(pinia?: Pinia): ImProfileDirectorySt
9023
9063
  * 解析顺序固定三步,前两步不发请求:
9024
9064
  * 1. 当前窗口里有就直接用,这也是唯一能拿到最新撤回/编辑状态的来源;
9025
9065
  * 2. 调 SDK `resolveMessage` 查本机历史;
9026
- * 3. 本机确实没有,才把这一屏缺失的 id 攒成批交给 SDK 回源(`messages.fetchByIds`)。
9066
+ * 3. 本机缺失或单聊原文解密失败时,批量交给 SDK 补取;已有失败占位每个活动句柄只自动尝试一次。
9027
9067
  *
9028
9068
  * 第三步是批量的:一屏引用可能几十条,逐条打接口会给服务端制造无谓压力。回源不会改会话摘要、
9029
9069
  * 不产生未读、不推进已读游标,所以为了渲染而调用是安全的。
@@ -9224,6 +9264,7 @@ interface ImThreadStoreActions {
9224
9264
  /** 默认重新定位;skipIfLoaded 仅在目标已显示且没有在途窗口操作时复用当前窗口。 */
9225
9265
  loadAround: (messageId: string, options?: {
9226
9266
  skipIfLoaded?: boolean;
9267
+ signal?: AbortSignal;
9227
9268
  }) => Promise<boolean>;
9228
9269
  resolveMessage: (messageId: string) => Promise<ImMessageView | null>;
9229
9270
  retainHandle: (handle?: ProductConversationHandle) => () => void;
@@ -9274,7 +9315,7 @@ type ImSearchOperationResult = Readonly<{
9274
9315
  } | {
9275
9316
  ok: false;
9276
9317
  operation: SearchOperation;
9277
- reason: 'not-ready' | 'no-condition' | 'busy' | 'exhausted' | 'stale' | 'failed';
9318
+ reason: 'not-ready' | 'no-condition' | 'busy' | 'exhausted' | 'stale' | 'failed' | 'not-found' | 'anchor-unavailable' | 'cancelled';
9278
9319
  error?: ImSdkErrorShape;
9279
9320
  }>;
9280
9321
  declare function defineImSearchStore(pinia: Pinia): pinia.StoreDefinition<"k2-im-search", Pick<{
@@ -9555,4 +9596,4 @@ type MessageNotificationDecision = {
9555
9596
  declare function resolveMessageNotificationDecision(input: MessageNotificationInput): MessageNotificationDecision;
9556
9597
 
9557
9598
  export { applicationKey, createAndInstallImChatKit, createImChatKitContext, disposeImChatKit, ensureImChatKitSelfProfile, getImChatKitContext, imMediaDownloadKey, installImChatKit, resolveMessageNotificationDecision, useImApplicationsStore, useImCapabilitiesStore, useImComposerStore, useImConversationActivationCoordinator, useImConversationDeletionCoordinator, useImConversationListStore, useImExpressionStore, useImHistoryReturnAfterSendCoordinator, useImMediaStore, useImMessageOperationsStore, useImProfileDirectoryStore, useImQuotedMessageStore, useImReactionStore, useImSearchStore, useImSessionFavoritesStore, useImSessionStore, useImThreadPagingStore, useImThreadReturnToLatestCoordinator, useImThreadStore };
9558
- export type { ApplyImSessionInput, ChatRuntimeFactories, ChatRuntimeHostDeps, ImApplicationDomainCounts, ImApplicationReviewOperation, ImApplicationReviewResult, ImApplicationsReloadResult, ImApplicationsStoreError, ImCapabilitiesStore, ImChannelManagementMemberTarget, ImChannelMemberManagementOperation, ImChannelMemberManagementResult, ImChannelMemberManagementResultFor, ImChannelProfileActionResult, ImChannelProfileActionResultFor, ImChannelProfileOperation, ImChatKitContext, ImChatKitInstallation, ImComposerStore, ImComposerStoreActions, ImComposerStoreState, ImComposerSubmitResult, ImConversationActivationCoordinator, ImConversationActivationError, ImConversationActivationResult, ImConversationDeletionCoordinator, ImConversationDeletionError, ImConversationDeletionResult, ImConversationListActionError, ImConversationListMutationOperation, ImConversationListMutationResult, ImConversationListRefreshResult, ImConversationListStore, ImDirectContactActionResult, ImDirectContactActionResultFor, ImDirectContactOperation, ImDirectoryState, ImEmojiPanelRecentEntry, ImEmojiRecentEntry, ImExpressionLoadStatus, ImExpressionOperation, ImExpressionOperationResult, ImExpressionRecentPersistencePort, ImExpressionRecentPersistenceSnapshot, ImExpressionRecentViewItem, ImExpressionResetOptions, ImExpressionSendResult, ImExpressionStore, ImExpressionStoreActions, ImExpressionStoreError, ImExpressionStoreGetters, ImExpressionStoreState, ImForwardPreparationResult, ImGameRecentEntry, ImGroupMemberManagementOperation, ImGroupMemberManagementResult, ImGroupMemberManagementResultFor, ImGroupProfileActionResult, ImGroupProfileActionResultFor, ImGroupProfileOperation, ImHistoryReturnAfterSendCoordinator, ImHistoryReturnError, ImHistoryReturnExecution, ImHistoryReturnResult, ImMediaOperationResult, ImMediaReplacementRequest, ImMediaStore, ImMediaStoreError, ImMediaTaskPresentation, ImMembershipJoinActionResult, ImMembershipJoinOperation, ImMessageOperationsStore, ImMessageOperationsStoreActions, ImMessageOperationsStoreState, ImMessageSelectionResult, ImPersistedEmojiPanelRecentEntry, ImPersistedEmojiRecentEntry, ImPersistedExpressionAssetRecentEntry, ImPersistedGameRecentEntry, ImProfileAccessDestination, ImProfileAccessResult, ImProfileDirectoryResult, ImQuotedMessageState, ImQuotedMessageStore, ImQuotedMessageStoreActions, ImQuotedMessageStoreState, ImReactionOperationResult, ImReactionStore, ImReactionStoreActions, ImReactionStoreError, ImReactionStoreState, ImSearchOperationResult, ImSearchSenderPresentation, ImSearchStore, ImSessionFavoritesReloadResult, ImSessionFavoritesStoreError, ImSessionFavoritesWriteResult, ImSessionPhase, ImSessionStore, ImThreadPagingActions, ImThreadPagingState, ImThreadPagingStore, ImThreadReturnToLatestCoordinator, ImThreadStore, ImThreadStoreActions, ImThreadStoreState };
9599
+ export type { ApplyImSessionInput, ChatRuntimeFactories, ChatRuntimeHostDeps, ImApplicationDomainCounts, ImApplicationReviewOperation, ImApplicationReviewResult, ImApplicationsReloadResult, ImApplicationsStoreError, ImCapabilitiesStore, ImChannelManagementMemberTarget, ImChannelMemberManagementOperation, ImChannelMemberManagementResult, ImChannelMemberManagementResultFor, ImChannelProfileActionResult, ImChannelProfileActionResultFor, ImChannelProfileOperation, ImChatKitContext, ImChatKitInstallation, ImComposerStore, ImComposerStoreActions, ImComposerStoreState, ImComposerSubmitResult, ImConversationActivationCoordinator, ImConversationActivationError, ImConversationActivationResult, ImConversationDeletionCoordinator, ImConversationDeletionError, ImConversationDeletionResult, ImConversationListActionError, ImConversationListMutationOperation, ImConversationListMutationResult, ImConversationListRefreshResult, ImConversationListStore, ImDirectContactActionResult, ImDirectContactActionResultFor, ImDirectContactOperation, ImDirectoryState, ImEmojiPanelRecentEntry, ImEmojiRecentEntry, ImExpressionLoadStatus, ImExpressionOperation, ImExpressionOperationResult, ImExpressionRecentPersistencePort, ImExpressionRecentPersistenceSnapshot, ImExpressionRecentViewItem, ImExpressionResetOptions, ImExpressionSendResult, ImExpressionStore, ImExpressionStoreActions, ImExpressionStoreError, ImExpressionStoreGetters, ImExpressionStoreState, ImForwardPreparationResult, ImGameRecentEntry, ImGroupMemberManagementOperation, ImGroupMemberManagementResult, ImGroupMemberManagementResultFor, ImGroupProfileActionResult, ImGroupProfileActionResultFor, ImGroupProfileOperation, ImHistoryReturnAfterSendCoordinator, ImHistoryReturnError, ImHistoryReturnExecution, ImHistoryReturnResult, ImMediaOperationResult, ImMediaReplacementRequest, ImMediaStore, ImMediaStoreError, ImMediaTaskPresentation, ImMediaUploadPresentation, ImMembershipJoinActionResult, ImMembershipJoinOperation, ImMessageOperationsStore, ImMessageOperationsStoreActions, ImMessageOperationsStoreState, ImMessageSelectionResult, ImPersistedEmojiPanelRecentEntry, ImPersistedEmojiRecentEntry, ImPersistedExpressionAssetRecentEntry, ImPersistedGameRecentEntry, ImProfileAccessDestination, ImProfileAccessResult, ImProfileDirectoryResult, ImQuotedMessageState, ImQuotedMessageStore, ImQuotedMessageStoreActions, ImQuotedMessageStoreState, ImReactionOperationResult, ImReactionStore, ImReactionStoreActions, ImReactionStoreError, ImReactionStoreState, ImSearchOperationResult, ImSearchSenderPresentation, ImSearchStore, ImSessionFavoritesReloadResult, ImSessionFavoritesStoreError, ImSessionFavoritesWriteResult, ImSessionPhase, ImSessionStore, ImThreadPagingActions, ImThreadPagingState, ImThreadPagingStore, ImThreadReturnToLatestCoordinator, ImThreadStore, ImThreadStoreActions, ImThreadStoreState };