k2-im 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3254,9 +3254,9 @@ interface TextMessageOptions {
3254
3254
  mentions?: readonly MentionInput[];
3255
3255
  replyToMessageId?: string;
3256
3256
  }
3257
- /** 首期允许主动分享的名片 DTO;机器人和未知 wire 类别只保留接收兼容。 */
3257
+ /** 可主动分享的名片。未知 wire 类别只保留接收,不能放进发送入参。 */
3258
3258
  type ContactCardInput = Omit<ImContactBody, 'kind' | 'rawKind'> & {
3259
- kind: Extract<ImContactBody['kind'], 'individual' | 'group' | 'channel'>;
3259
+ kind: Extract<ImContactBody['kind'], 'individual' | 'group' | 'channel' | 'robot'>;
3260
3260
  };
3261
3261
  /** 名片发送附加数据;回复关系与其它可发送消息使用同一稳定消息 id。 */
3262
3262
  interface ContactMessageOptions {
@@ -3355,9 +3355,9 @@ interface TextMessageOptions {
3355
3355
  mentions?: readonly MentionInput[];
3356
3356
  replyToMessageId?: string;
3357
3357
  }
3358
- /** 首期允许主动分享的名片 DTO;机器人和未知 wire 类别只保留接收兼容。 */
3358
+ /** 可主动分享的名片。未知 wire 类别只保留接收,不能放进发送入参。 */
3359
3359
  type ContactCardInput = Omit<ImContactBody, 'kind' | 'rawKind'> & {
3360
- kind: Extract<ImContactBody['kind'], 'individual' | 'group' | 'channel'>;
3360
+ kind: Extract<ImContactBody['kind'], 'individual' | 'group' | 'channel' | 'robot'>;
3361
3361
  };
3362
3362
  /** 名片发送附加数据;回复关系与其它可发送消息使用同一稳定消息 id。 */
3363
3363
  interface ContactMessageOptions {
package/dist/index.d.ts CHANGED
@@ -3888,9 +3888,9 @@ interface TextMessageOptions {
3888
3888
  mentions?: readonly MentionInput[];
3889
3889
  replyToMessageId?: string;
3890
3890
  }
3891
- /** 首期允许主动分享的名片 DTO;机器人和未知 wire 类别只保留接收兼容。 */
3891
+ /** 可主动分享的名片。未知 wire 类别只保留接收,不能放进发送入参。 */
3892
3892
  type ContactCardInput = Omit<ImContactBody, 'kind' | 'rawKind'> & {
3893
- kind: Extract<ImContactBody['kind'], 'individual' | 'group' | 'channel'>;
3893
+ kind: Extract<ImContactBody['kind'], 'individual' | 'group' | 'channel' | 'robot'>;
3894
3894
  };
3895
3895
  /** 名片发送附加数据;回复关系与其它可发送消息使用同一稳定消息 id。 */
3896
3896
  interface ContactMessageOptions {
@@ -1 +1 @@
1
- import{encodeConversationKey as e}from"../conversation-identity.js";import{assertOutgoingContentBodyMatchesType as t}from"../mapping/content-body.js";import{normalizeStoredVideoBody as n}from"../mapping/video-body.js";import{createDefaultSendStrategyRegistry as o}from"../message-flow/send/send-strategy-registry.js";import{classifyAckFailure as s}from"../mapping/message-mapper.js";import{computeSortKey as r,computeMessageSortKey as a,resolveMessageTimestamp as i}from"../mapping/sort.js";import{describeUnsupportedConversation as d}from"../message-flow/conversation-support.js";import{newClientMessageId as c}from"../mapping/message-id.js";const u=/* @__PURE__ */new Set([32768,0]),p=/* @__PURE__ */new Set([33281,33299]),l=33298,y=/* @__PURE__ */new Set([...p,l]);function f(e){if(e instanceof Uint8Array)return e.slice();if(Array.isArray(e))return e.map(e=>f(e));if(null!==e&&"object"==typeof e){const t={};for(const[n,o]of Object.entries(e))t[n]=f(o);return t}return e}function v(e){return f(e)}function g(e){return f(e)}function m(m,b={}){const I=b.ackTimeoutMs??3e4,T=b.maxAttempts??5,h=m.now??(()=>Date.now()),M=m.sendStrategies??o(),w=/* @__PURE__ */new Map,S=/* @__PURE__ */new Map,A=/* @__PURE__ */new Map,x=/* @__PURE__ */new Map;let C=0;const E=/* @__PURE__ */new Set,k=/* @__PURE__ */new Map,q=/* @__PURE__ */new Set,O=/* @__PURE__ */new Set,F=/* @__PURE__ */new Set,j=/* @__PURE__ */new Map;function P(t){return e(t)}function R(e,t){return e.createdAt!==t.createdAt?e.createdAt-t.createdAt:e.id<t.id?-1:e.id>t.id?1:0}function U(e){const t=P(e),n=S.get(t);if(void 0!==n)return n;let o=0;for(const t of m.domainState.getMessagesByConversation({conversationType:e.conversationType,conversationId:e.conversationId}))o=Math.max(o,i(t));for(const e of w.values())P(e)===t&&(o=Math.max(o,e.createdAt));return S.set(t,o),o}function K(e,t){const n=P(e);t>(S.get(n)??U(e))&&S.set(n,t)}function D(e){const t=U(e),n=Math.max(h(),t+1);return S.set(P(e),n),{id:c(),createdAt:n}}function N(e,t){m.onUpdated?.({messageId:e.id,conversation:{conversationType:e.conversationType,conversationId:e.conversationId},state:t,...void 0===e.lastError?{}:{failure:{reason:e.lastError.reason,retryable:e.lastError.retryable}}})}function $(e){void 0!==m.storage&&m.storage.saveOutboxMessage(v(e)).catch(e=>m.onStorageError?.("Outbox 落盘失败",e))}function B(e){void 0!==m.storage&&m.storage.removeOutboxMessage(e).catch(e=>m.onStorageError?.("Outbox 删除失败",e))}function L(e){const t=A.get(e);void 0!==t&&(clearTimeout(t),A.delete(e))}function z(e,t,n){const o=!0===n?.authoritativeFailure?{authoritativeOutboxFailure:!0}:void 0,s=n?.entry??w.get(e);if(void 0===s)return;const i=m.domainState.getConversationMessage(s,e);if(void 0!==i)return void m.domainState.upsertMessages([{...i,...t}],"live",o);const d={...{id:s.id,conversationId:s.conversationId,conversationType:s.conversationType,senderId:m.getSelfId(),contentType:s.contentType,body:g(s.body),createdAt:s.createdAt,sortKey:r(s.createdAt,s.id),status:s.status,receipt:"unread",encryption:f(s.encryption),failure:void 0===s.lastError?void 0:f(s.lastError),...void 0!==s.mention?{mention:f(s.mention)}:{},...void 0!==s.refMessageId?{refMessageId:s.refMessageId}:{}},...t};m.domainState.upsertMessages([{...d,sortKey:a(d)}],"live",o)}function G(e,t){L(e);const n=w.get(e);void 0!==n&&(n.status="failed",n.lastError=t,z(e,{status:"failed",failure:t}),$(n),N(n,"failed"),J(e))}async function H(e){if(x.has(e.id))return;if(E.has(P(e)))return void G(e.id,{code:-4,reason:"unknown",displayCode:"unknown",message:"会话成员关系已终止,消息不会继续发送",retryable:!1});const t=/* @__PURE__ */Symbol("outbox-dispatch");x.set(e.id,t);const n=C;try{const t=M.resolve(e.conversationType);if(void 0===t)return void G(e.id,{code:-3,reason:"unsupported",displayCode:"conversationUnsupported",params:{type:e.conversationType},message:d(e.conversationType,"outbox.dispatch"),retryable:!1});let o;try{const s={selfId:m.getSelfId(),crypto:m.crypto,resolveEncryptionKey:m.resolvePublicKey},r=void 0===t.buildOutgoingFrames?t.buildOutgoingFrame(e,s):t.buildOutgoingFrames(e,s);if(r instanceof Uint8Array)o=[r];else if(Array.isArray(r))o=r;else{const e=await r;o=e instanceof Uint8Array?[e]:e}if(n!==C)return;if(0===o.length||o.some(e=>!(e instanceof Uint8Array)))throw new Error(`发送策略返回了非法帧序列(conversationType=${e.conversationType})`)}catch{if(n!==C)return;return e.attempts+=1,void G(e.id,{code:-2,reason:"unknown"})}if(E.has(P(e)))return void G(e.id,{code:-4,reason:"unknown",displayCode:"unknown",message:"会话成员关系已终止,消息不会继续发送",retryable:!1});if(!m.isAuthenticated())return e.status="pending",z(e.id,{status:"pending"}),$(e),void N(e,"queued");e.status="sending",e.attempts+=1,z(e.id,{status:"sending"}),$(e),N(e,"sending");for(const e of o)m.sendFrame(e);L(e.id),A.set(e.id,setTimeout(()=>G(e.id,{code:-1,reason:"timeout"}),I))}finally{x.get(e.id)===t&&x.delete(e.id)}}function J(e){const t=j.get(e);void 0!==t&&(j.delete(e),t())}function Q(e){const t=w.get(e);return void 0===t||"sending"!==t.status?Promise.resolve():new Promise(t=>j.set(e,t))}function V(e){return("pending"===e.status||"failed"===e.status)&&!("failed"===e.status&&!1===e.lastError?.retryable)&&"manual"!==e.resumePolicy&&(e.attempts<T||O.has(e.id))&&void 0!==M.resolve(e.conversationType)}function W(e,t){let n;for(const o of w.values())!t.has(o.id)&&P(o)===e&&V(o)&&(void 0===n||R(o,n)<0)&&(n=o);return n}function X(e,t=!1){const n=P(e);t&&F.add(n),k.has(n)?q.add(n):Y(n)}async function Y(e){const t=/* @__PURE__ */Symbol("outbox-drain"),n=C;k.set(e,t);try{do{q.delete(e);const t=/* @__PURE__ */new Set;for(;n===C&&m.isAuthenticated();){const o=W(e,t);if(void 0===o)break;if(t.add(o.id),O.delete(o.id),await H(o),n!==C)return;F.has(e)&&await Q(o.id)}}while(n===C&&q.has(e)&&m.isAuthenticated())}finally{k.get(e)===t&&(k.delete(e),F.delete(e))}}function Z(e,t=!1){t&&F.add(e),k.has(e)?q.add(e):Y(e)}function _(e){const t={code:-2,reason:"unknown",displayCode:"sendCanceled",retryable:!1};e.lastError=t,z(e.id,{status:"failed",failure:t}),O.delete(e.id),w.delete(e.id),B(e.id),N(e,"canceled"),J(e.id)}return{allocateMessageIdentity:D,enqueueText(e){const{id:t,createdAt:n}=D(e),o={encrypted:e.encrypted??!1},s=void 0!==M.resolve(e.conversationType),a=s&&m.isAuthenticated(),i={id:t,conversationId:e.conversationId,conversationType:e.conversationType,senderId:m.getSelfId(),contentType:"text",body:{text:e.text},createdAt:n,sortKey:r(n,t),status:"pending",receipt:"unread",encryption:f(o),...void 0!==e.mention?{mention:f(e.mention)}:{},...void 0!==e.refMessageId?{refMessageId:e.refMessageId}:{}},c={id:t,conversationId:e.conversationId,conversationType:e.conversationType,contentType:"text",body:{text:e.text},encryption:f(o),status:"pending",attempts:0,createdAt:n,...void 0!==e.mention?{mention:f(e.mention)}:{},...void 0!==e.refMessageId?{refMessageId:e.refMessageId}:{}};return w.set(t,c),$(c),N(c,"queued"),m.domainState.upsertMessages([i],"live",{newOutgoing:!0}),s?(a&&X(c),v(c)):(G(t,{code:-3,reason:"unsupported",displayCode:"conversationUnsupported",params:{type:e.conversationType},message:d(e.conversationType,"outbox.enqueueText"),retryable:!1}),v(c))},enqueueContact(e){const t=e.contact.targetId.trim(),n=e.contact.displayName.trim();if(""===t)throw new Error("outbox.enqueueContact: targetId 不能为空");if(""===n)throw new Error("outbox.enqueueContact: displayName 不能为空");if("individual"!==e.contact.kind&&"group"!==e.contact.kind&&"channel"!==e.contact.kind)throw new Error(`outbox.enqueueContact: 不支持发送 ${String(e.contact.kind)} 名片`);const{id:o,createdAt:s}=D(e),a={encrypted:e.encrypted??!1},i=void 0!==M.resolve(e.conversationType),c=i&&m.isAuthenticated(),u={contact:{...e.contact,targetId:t,displayName:n}},p={id:o,conversationId:e.conversationId,conversationType:e.conversationType,senderId:m.getSelfId(),contentType:"contact",body:g(u),createdAt:s,sortKey:r(s,o),status:"pending",receipt:"unread",encryption:f(a),...void 0===e.refMessageId?{}:{refMessageId:e.refMessageId}},l={id:o,conversationId:e.conversationId,conversationType:e.conversationType,contentType:"contact",body:g(u),encryption:f(a),status:"pending",attempts:0,createdAt:s,...void 0===e.refMessageId?{}:{refMessageId:e.refMessageId}};return w.set(o,l),$(l),N(l,"queued"),m.domainState.upsertMessages([p],"live",{newOutgoing:!0}),i?(c&&X(l),v(l)):(G(o,{code:-3,reason:"unsupported",displayCode:"conversationUnsupported",params:{type:e.conversationType},message:d(e.conversationType,"outbox.enqueueContact"),retryable:!1}),v(l))},enqueueMedia(e){const{id:n,createdAt:o}=e,s=g(e.body);t(e.contentType,s,"outbox.enqueueMedia"),K(e,o);const a=void 0!==M.resolve(e.conversationType),i=a&&m.isAuthenticated(),c={id:n,conversationId:e.conversationId,conversationType:e.conversationType,senderId:m.getSelfId(),contentType:e.contentType,body:g(s),createdAt:o,sortKey:r(o,n),status:"pending",receipt:"unread",encryption:f(e.encryption),...void 0!==e.mention?{mention:f(e.mention)}:{},...void 0!==e.refMessageId?{refMessageId:e.refMessageId}:{}},u={id:n,conversationId:e.conversationId,conversationType:e.conversationType,contentType:e.contentType,body:s,encryption:f(e.encryption),status:"pending",attempts:0,createdAt:o,...void 0!==e.mention?{mention:f(e.mention)}:{},...void 0!==e.refMessageId?{refMessageId:e.refMessageId}:{}};return w.set(n,u),$(u),N(u,"queued"),m.domainState.upsertMessages([c],"live",{newOutgoing:!0!==e.optimisticAlreadyCreated}),a?(i&&X(u),v(u)):(G(n,{code:-3,reason:"unsupported",displayCode:"conversationUnsupported",params:{type:e.conversationType},message:d(e.conversationType,"outbox.enqueueMedia"),retryable:!1}),X(u,!0),v(u))},onChatAck(e){L(e.id);const t=w.get(e.id);if(void 0!==t)if(u.has(e.errcode))w.delete(e.id),B(e.id),z(e.id,{status:"sent",serverTime:e.serverTime>0?e.serverTime:void 0,...void 0===e.extensions?{}:{extensions:{...m.domainState.getConversationMessage(t,e.id)?.extensions,...e.extensions}}},{entry:t}),N(t,"sent"),J(e.id);else{t.status="failed",t.lastError=e.failure??s(e.errcode);const n="direct"===t.conversationType&&p.has(e.errcode),o="direct"===t.conversationType&&e.errcode===l;n?t.lastError={...t.lastError,displayCode:"contactRequired",retryable:!1}:o&&(t.lastError={...t.lastError,displayCode:"messageRefused",retryable:!1}),z(e.id,{status:"failed",failure:t.lastError},{authoritativeFailure:!0}),n?m.domainState.appendDirectContactRequiredNotice(t.conversationId,t.id):o&&m.domainState.appendDirectMessageRefusedNotice(t.conversationId,t.id),$(t),N(t,"failed"),J(e.id)}else u.has(e.errcode)||function(e){if(!y.has(e.errcode))return;const t=m.getSelfId(),n=m.domainState.getMessagesById(e.id).filter(e=>"direct"===e.conversationType&&e.senderId===t);if(n.length>1)throw new Error(`DIRECT 终态 ACK 消息 ID 存在多会话歧义:${e.id}`);const o=n[0];if(void 0===o)return;const r=p.has(e.errcode),a=e.errcode===l;let i=e.failure??s(e.errcode);r?i={...i,displayCode:"contactRequired",retryable:!1}:a&&(i={...i,displayCode:"messageRefused",retryable:!1}),m.domainState.upsertMessages([{...o,status:"failed",failure:i}],"live",{authoritativeOutboxFailure:!0}),r?m.domainState.appendDirectContactRequiredNotice(o.conversationId,o.id):a&&m.domainState.appendDirectMessageRefusedNotice(o.conversationId,o.id)}(e)},onAuthenticated(){!function(){const e=/* @__PURE__ */new Set;for(const t of w.values())V(t)&&e.add(P(t));for(const t of e)Z(t,!0)}()},retry(e){const t=w.get(e);return void 0===t?{accepted:!1,reason:"not-found"}:"sending"===t.status||x.has(e)?{accepted:!1,reason:"in-flight"}:"failed"===t.status&&!1===t.lastError?.retryable?{accepted:!1,reason:"not-retryable"}:"failed"===t.status||"pending"===t.status&&"manual"===t.resumePolicy?("manual"===t.resumePolicy&&(t.resumePolicy="automatic",$(t)),O.add(t.id),X(t),{accepted:!0}):{accepted:!1,reason:"invalid-state"}},cancel(e){const t=w.get(e);return void 0===t?{accepted:!1,reason:"not-found"}:"sending"===t.status||x.has(e)?{accepted:!1,reason:"in-flight"}:"pending"!==t.status?{accepted:!1,reason:"invalid-state"}:t.attempts>0?{accepted:!1,reason:"delivery-uncertain"}:(_(t),{accepted:!0})},markConversationTerminated(e,t){const n=P(e);E.add(n);for(const e of[...w.values()])P(e)===n&&("pending"!==e.status||0!==e.attempts||x.has(e.id)?G(e.id,{code:-4,reason:"unknown",displayCode:"unknown",message:`会话成员关系已终止:${t}`,retryable:!1}):_(e))},clearConversationTerminated(e){const t=P(e);E.delete(t),Z(t)},markForLogout(e){for(const t of w.values())if("pending"===t.status||"sending"===t.status||"failed"===t.status){if(L(t.id),t.resumePolicy="manual","fail"===e.policy){const e={code:-2,reason:"unknown",displayCode:"sessionLoggedOut",retryable:!0};t.status="failed",t.lastError=e,z(t.id,{status:"failed",failure:e}),N(t,"failed")}$(t),J(t.id)}},getPending:()=>[...w.values()].map(v),reset(e){e?.discardInFlight&&(C+=1,x.clear(),k.clear(),q.clear(),F.clear());for(const e of[...A.keys()])L(e);for(const e of w.values())"sending"===e.status&&(e.status="pending",z(e.id,{status:"pending"}),$(e),N(e,"queued"),J(e.id))},async hydrate(e=()=>!0){if(void 0!==m.storage&&e())try{const o=await m.storage.getOutbox();if(!e())return;for(const s of o){if(!e())return;const o=v(s);if("sent"===o.status){B(o.id);continue}try{t(o.contentType,o.body,`outbox.hydrate(${o.id})`),void 0!==o.body.video&&(o.body={...o.body,video:n(o.body.video)})}catch(t){if(m.onStorageError?.(`Outbox 持久化条目正文损坏,已删除(id=${o.id})`,t),!e())return;if(B(o.id),void 0!==m.domainState.getConversationMessage(o,o.id)){const e={code:-2,reason:"unknown",displayCode:"unknown",message:"发送账本正文损坏,已停止重试",retryable:!1};z(o.id,{status:"failed",failure:e},{authoritativeFailure:!0,entry:o})}continue}const r="sending"===o.status?"pending":o.status,a={...o,status:r};if(w.set(o.id,a),K(a,a.createdAt),N(a,"pending"===r?"queued":r),!e())return;void 0!==m.domainState.getConversationMessage(o,o.id)&&z(o.id,{status:r,failure:void 0===o.lastError?void 0:f(o.lastError)})}}catch(t){e()&&m.onStorageError?.("Outbox 回读失败",t)}}}}export{m as createOutboxManager};
1
+ import{encodeConversationKey as e}from"../conversation-identity.js";import{assertOutgoingContentBodyMatchesType as t}from"../mapping/content-body.js";import{normalizeStoredVideoBody as n}from"../mapping/video-body.js";import{createDefaultSendStrategyRegistry as o}from"../message-flow/send/send-strategy-registry.js";import{classifyAckFailure as r}from"../mapping/message-mapper.js";import{computeSortKey as s,computeMessageSortKey as a,resolveMessageTimestamp as i}from"../mapping/sort.js";import{describeUnsupportedConversation as d}from"../message-flow/conversation-support.js";import{newClientMessageId as c}from"../mapping/message-id.js";const u=/* @__PURE__ */new Set([32768,0]),p=/* @__PURE__ */new Set([33281,33299]),l=33298,y=/* @__PURE__ */new Set([...p,l]);function f(e){if(e instanceof Uint8Array)return e.slice();if(Array.isArray(e))return e.map(e=>f(e));if(null!==e&&"object"==typeof e){const t={};for(const[n,o]of Object.entries(e))t[n]=f(o);return t}return e}function v(e){return f(e)}function g(e){return f(e)}function m(m,b={}){const I=b.ackTimeoutMs??3e4,T=b.maxAttempts??5,h=m.now??(()=>Date.now()),M=m.sendStrategies??o(),w=/* @__PURE__ */new Map,S=/* @__PURE__ */new Map,A=/* @__PURE__ */new Map,x=/* @__PURE__ */new Map;let C=0;const E=/* @__PURE__ */new Set,k=/* @__PURE__ */new Map,q=/* @__PURE__ */new Set,O=/* @__PURE__ */new Set,F=/* @__PURE__ */new Set,j=/* @__PURE__ */new Map;function P(t){return e(t)}function R(e,t){return e.createdAt!==t.createdAt?e.createdAt-t.createdAt:e.id<t.id?-1:e.id>t.id?1:0}function U(e){const t=P(e),n=S.get(t);if(void 0!==n)return n;let o=0;for(const t of m.domainState.getMessagesByConversation({conversationType:e.conversationType,conversationId:e.conversationId}))o=Math.max(o,i(t));for(const e of w.values())P(e)===t&&(o=Math.max(o,e.createdAt));return S.set(t,o),o}function K(e,t){const n=P(e);t>(S.get(n)??U(e))&&S.set(n,t)}function D(e){const t=U(e),n=Math.max(h(),t+1);return S.set(P(e),n),{id:c(),createdAt:n}}function N(e,t){m.onUpdated?.({messageId:e.id,conversation:{conversationType:e.conversationType,conversationId:e.conversationId},state:t,...void 0===e.lastError?{}:{failure:{reason:e.lastError.reason,retryable:e.lastError.retryable}}})}function $(e){void 0!==m.storage&&m.storage.saveOutboxMessage(v(e)).catch(e=>m.onStorageError?.("Outbox 落盘失败",e))}function B(e){void 0!==m.storage&&m.storage.removeOutboxMessage(e).catch(e=>m.onStorageError?.("Outbox 删除失败",e))}function L(e){const t=A.get(e);void 0!==t&&(clearTimeout(t),A.delete(e))}function z(e,t,n){const o=!0===n?.authoritativeFailure?{authoritativeOutboxFailure:!0}:void 0,r=n?.entry??w.get(e);if(void 0===r)return;const i=m.domainState.getConversationMessage(r,e);if(void 0!==i)return void m.domainState.upsertMessages([{...i,...t}],"live",o);const d={...{id:r.id,conversationId:r.conversationId,conversationType:r.conversationType,senderId:m.getSelfId(),contentType:r.contentType,body:g(r.body),createdAt:r.createdAt,sortKey:s(r.createdAt,r.id),status:r.status,receipt:"unread",encryption:f(r.encryption),failure:void 0===r.lastError?void 0:f(r.lastError),...void 0!==r.mention?{mention:f(r.mention)}:{},...void 0!==r.refMessageId?{refMessageId:r.refMessageId}:{}},...t};m.domainState.upsertMessages([{...d,sortKey:a(d)}],"live",o)}function G(e,t){L(e);const n=w.get(e);void 0!==n&&(n.status="failed",n.lastError=t,z(e,{status:"failed",failure:t}),$(n),N(n,"failed"),J(e))}async function H(e){if(x.has(e.id))return;if(E.has(P(e)))return void G(e.id,{code:-4,reason:"unknown",displayCode:"unknown",message:"会话成员关系已终止,消息不会继续发送",retryable:!1});const t=/* @__PURE__ */Symbol("outbox-dispatch");x.set(e.id,t);const n=C;try{const t=M.resolve(e.conversationType);if(void 0===t)return void G(e.id,{code:-3,reason:"unsupported",displayCode:"conversationUnsupported",params:{type:e.conversationType},message:d(e.conversationType,"outbox.dispatch"),retryable:!1});let o;try{const r={selfId:m.getSelfId(),crypto:m.crypto,resolveEncryptionKey:m.resolvePublicKey},s=void 0===t.buildOutgoingFrames?t.buildOutgoingFrame(e,r):t.buildOutgoingFrames(e,r);if(s instanceof Uint8Array)o=[s];else if(Array.isArray(s))o=s;else{const e=await s;o=e instanceof Uint8Array?[e]:e}if(n!==C)return;if(0===o.length||o.some(e=>!(e instanceof Uint8Array)))throw new Error(`发送策略返回了非法帧序列(conversationType=${e.conversationType})`)}catch{if(n!==C)return;return e.attempts+=1,void G(e.id,{code:-2,reason:"unknown"})}if(E.has(P(e)))return void G(e.id,{code:-4,reason:"unknown",displayCode:"unknown",message:"会话成员关系已终止,消息不会继续发送",retryable:!1});if(!m.isAuthenticated())return e.status="pending",z(e.id,{status:"pending"}),$(e),void N(e,"queued");e.status="sending",e.attempts+=1,z(e.id,{status:"sending"}),$(e),N(e,"sending");for(const e of o)m.sendFrame(e);L(e.id),A.set(e.id,setTimeout(()=>G(e.id,{code:-1,reason:"timeout"}),I))}finally{x.get(e.id)===t&&x.delete(e.id)}}function J(e){const t=j.get(e);void 0!==t&&(j.delete(e),t())}function Q(e){const t=w.get(e);return void 0===t||"sending"!==t.status?Promise.resolve():new Promise(t=>j.set(e,t))}function V(e){return("pending"===e.status||"failed"===e.status)&&!("failed"===e.status&&!1===e.lastError?.retryable)&&"manual"!==e.resumePolicy&&(e.attempts<T||O.has(e.id))&&void 0!==M.resolve(e.conversationType)}function W(e,t){let n;for(const o of w.values())!t.has(o.id)&&P(o)===e&&V(o)&&(void 0===n||R(o,n)<0)&&(n=o);return n}function X(e,t=!1){const n=P(e);t&&F.add(n),k.has(n)?q.add(n):Y(n)}async function Y(e){const t=/* @__PURE__ */Symbol("outbox-drain"),n=C;k.set(e,t);try{do{q.delete(e);const t=/* @__PURE__ */new Set;for(;n===C&&m.isAuthenticated();){const o=W(e,t);if(void 0===o)break;if(t.add(o.id),O.delete(o.id),await H(o),n!==C)return;F.has(e)&&await Q(o.id)}}while(n===C&&q.has(e)&&m.isAuthenticated())}finally{k.get(e)===t&&(k.delete(e),F.delete(e))}}function Z(e,t=!1){t&&F.add(e),k.has(e)?q.add(e):Y(e)}function _(e){const t={code:-2,reason:"unknown",displayCode:"sendCanceled",retryable:!1};e.lastError=t,z(e.id,{status:"failed",failure:t}),O.delete(e.id),w.delete(e.id),B(e.id),N(e,"canceled"),J(e.id)}return{allocateMessageIdentity:D,enqueueText(e){const{id:t,createdAt:n}=D(e),o={encrypted:e.encrypted??!1},r=void 0!==M.resolve(e.conversationType),a=r&&m.isAuthenticated(),i={id:t,conversationId:e.conversationId,conversationType:e.conversationType,senderId:m.getSelfId(),contentType:"text",body:{text:e.text},createdAt:n,sortKey:s(n,t),status:"pending",receipt:"unread",encryption:f(o),...void 0!==e.mention?{mention:f(e.mention)}:{},...void 0!==e.refMessageId?{refMessageId:e.refMessageId}:{}},c={id:t,conversationId:e.conversationId,conversationType:e.conversationType,contentType:"text",body:{text:e.text},encryption:f(o),status:"pending",attempts:0,createdAt:n,...void 0!==e.mention?{mention:f(e.mention)}:{},...void 0!==e.refMessageId?{refMessageId:e.refMessageId}:{}};return w.set(t,c),$(c),N(c,"queued"),m.domainState.upsertMessages([i],"live",{newOutgoing:!0}),r?(a&&X(c),v(c)):(G(t,{code:-3,reason:"unsupported",displayCode:"conversationUnsupported",params:{type:e.conversationType},message:d(e.conversationType,"outbox.enqueueText"),retryable:!1}),v(c))},enqueueContact(e){const t=e.contact.targetId.trim(),n=e.contact.displayName.trim();if(""===t)throw new Error("outbox.enqueueContact: targetId 不能为空");if(""===n)throw new Error("outbox.enqueueContact: displayName 不能为空");if("individual"!==e.contact.kind&&"group"!==e.contact.kind&&"channel"!==e.contact.kind&&"robot"!==e.contact.kind)throw new Error(`outbox.enqueueContact: 不支持发送 ${String(e.contact.kind)} 名片`);const{id:o,createdAt:r}=D(e),a={encrypted:e.encrypted??!1},i=void 0!==M.resolve(e.conversationType),c=i&&m.isAuthenticated(),u={contact:{...e.contact,targetId:t,displayName:n}},p={id:o,conversationId:e.conversationId,conversationType:e.conversationType,senderId:m.getSelfId(),contentType:"contact",body:g(u),createdAt:r,sortKey:s(r,o),status:"pending",receipt:"unread",encryption:f(a),...void 0===e.refMessageId?{}:{refMessageId:e.refMessageId}},l={id:o,conversationId:e.conversationId,conversationType:e.conversationType,contentType:"contact",body:g(u),encryption:f(a),status:"pending",attempts:0,createdAt:r,...void 0===e.refMessageId?{}:{refMessageId:e.refMessageId}};return w.set(o,l),$(l),N(l,"queued"),m.domainState.upsertMessages([p],"live",{newOutgoing:!0}),i?(c&&X(l),v(l)):(G(o,{code:-3,reason:"unsupported",displayCode:"conversationUnsupported",params:{type:e.conversationType},message:d(e.conversationType,"outbox.enqueueContact"),retryable:!1}),v(l))},enqueueMedia(e){const{id:n,createdAt:o}=e,r=g(e.body);t(e.contentType,r,"outbox.enqueueMedia"),K(e,o);const a=void 0!==M.resolve(e.conversationType),i=a&&m.isAuthenticated(),c={id:n,conversationId:e.conversationId,conversationType:e.conversationType,senderId:m.getSelfId(),contentType:e.contentType,body:g(r),createdAt:o,sortKey:s(o,n),status:"pending",receipt:"unread",encryption:f(e.encryption),...void 0!==e.mention?{mention:f(e.mention)}:{},...void 0!==e.refMessageId?{refMessageId:e.refMessageId}:{}},u={id:n,conversationId:e.conversationId,conversationType:e.conversationType,contentType:e.contentType,body:r,encryption:f(e.encryption),status:"pending",attempts:0,createdAt:o,...void 0!==e.mention?{mention:f(e.mention)}:{},...void 0!==e.refMessageId?{refMessageId:e.refMessageId}:{}};return w.set(n,u),$(u),N(u,"queued"),m.domainState.upsertMessages([c],"live",{newOutgoing:!0!==e.optimisticAlreadyCreated}),a?(i&&X(u),v(u)):(G(n,{code:-3,reason:"unsupported",displayCode:"conversationUnsupported",params:{type:e.conversationType},message:d(e.conversationType,"outbox.enqueueMedia"),retryable:!1}),X(u,!0),v(u))},onChatAck(e){L(e.id);const t=w.get(e.id);if(void 0!==t)if(u.has(e.errcode))w.delete(e.id),B(e.id),z(e.id,{status:"sent",serverTime:e.serverTime>0?e.serverTime:void 0,...void 0===e.extensions?{}:{extensions:{...m.domainState.getConversationMessage(t,e.id)?.extensions,...e.extensions}}},{entry:t}),N(t,"sent"),J(e.id);else{t.status="failed",t.lastError=e.failure??r(e.errcode);const n="direct"===t.conversationType&&p.has(e.errcode),o="direct"===t.conversationType&&e.errcode===l;n?t.lastError={...t.lastError,displayCode:"contactRequired",retryable:!1}:o&&(t.lastError={...t.lastError,displayCode:"messageRefused",retryable:!1}),z(e.id,{status:"failed",failure:t.lastError},{authoritativeFailure:!0}),n?m.domainState.appendDirectContactRequiredNotice(t.conversationId,t.id):o&&m.domainState.appendDirectMessageRefusedNotice(t.conversationId,t.id),$(t),N(t,"failed"),J(e.id)}else u.has(e.errcode)||function(e){if(!y.has(e.errcode))return;const t=m.getSelfId(),n=m.domainState.getMessagesById(e.id).filter(e=>"direct"===e.conversationType&&e.senderId===t);if(n.length>1)throw new Error(`DIRECT 终态 ACK 消息 ID 存在多会话歧义:${e.id}`);const o=n[0];if(void 0===o)return;const s=p.has(e.errcode),a=e.errcode===l;let i=e.failure??r(e.errcode);s?i={...i,displayCode:"contactRequired",retryable:!1}:a&&(i={...i,displayCode:"messageRefused",retryable:!1}),m.domainState.upsertMessages([{...o,status:"failed",failure:i}],"live",{authoritativeOutboxFailure:!0}),s?m.domainState.appendDirectContactRequiredNotice(o.conversationId,o.id):a&&m.domainState.appendDirectMessageRefusedNotice(o.conversationId,o.id)}(e)},onAuthenticated(){!function(){const e=/* @__PURE__ */new Set;for(const t of w.values())V(t)&&e.add(P(t));for(const t of e)Z(t,!0)}()},retry(e){const t=w.get(e);return void 0===t?{accepted:!1,reason:"not-found"}:"sending"===t.status||x.has(e)?{accepted:!1,reason:"in-flight"}:"failed"===t.status&&!1===t.lastError?.retryable?{accepted:!1,reason:"not-retryable"}:"failed"===t.status||"pending"===t.status&&"manual"===t.resumePolicy?("manual"===t.resumePolicy&&(t.resumePolicy="automatic",$(t)),O.add(t.id),X(t),{accepted:!0}):{accepted:!1,reason:"invalid-state"}},cancel(e){const t=w.get(e);return void 0===t?{accepted:!1,reason:"not-found"}:"sending"===t.status||x.has(e)?{accepted:!1,reason:"in-flight"}:"pending"!==t.status?{accepted:!1,reason:"invalid-state"}:t.attempts>0?{accepted:!1,reason:"delivery-uncertain"}:(_(t),{accepted:!0})},markConversationTerminated(e,t){const n=P(e);E.add(n);for(const e of[...w.values()])P(e)===n&&("pending"!==e.status||0!==e.attempts||x.has(e.id)?G(e.id,{code:-4,reason:"unknown",displayCode:"unknown",message:`会话成员关系已终止:${t}`,retryable:!1}):_(e))},clearConversationTerminated(e){const t=P(e);E.delete(t),Z(t)},markForLogout(e){for(const t of w.values())if("pending"===t.status||"sending"===t.status||"failed"===t.status){if(L(t.id),t.resumePolicy="manual","fail"===e.policy){const e={code:-2,reason:"unknown",displayCode:"sessionLoggedOut",retryable:!0};t.status="failed",t.lastError=e,z(t.id,{status:"failed",failure:e}),N(t,"failed")}$(t),J(t.id)}},getPending:()=>[...w.values()].map(v),reset(e){e?.discardInFlight&&(C+=1,x.clear(),k.clear(),q.clear(),F.clear());for(const e of[...A.keys()])L(e);for(const e of w.values())"sending"===e.status&&(e.status="pending",z(e.id,{status:"pending"}),$(e),N(e,"queued"),J(e.id))},async hydrate(e=()=>!0){if(void 0!==m.storage&&e())try{const o=await m.storage.getOutbox();if(!e())return;for(const r of o){if(!e())return;const o=v(r);if("sent"===o.status){B(o.id);continue}try{t(o.contentType,o.body,`outbox.hydrate(${o.id})`),void 0!==o.body.video&&(o.body={...o.body,video:n(o.body.video)})}catch(t){if(m.onStorageError?.(`Outbox 持久化条目正文损坏,已删除(id=${o.id})`,t),!e())return;if(B(o.id),void 0!==m.domainState.getConversationMessage(o,o.id)){const e={code:-2,reason:"unknown",displayCode:"unknown",message:"发送账本正文损坏,已停止重试",retryable:!1};z(o.id,{status:"failed",failure:e},{authoritativeFailure:!0,entry:o})}continue}const s="sending"===o.status?"pending":o.status,a={...o,status:s};if(w.set(o.id,a),K(a,a.createdAt),N(a,"pending"===s?"queued":s),!e())return;void 0!==m.domainState.getConversationMessage(o,o.id)&&z(o.id,{status:s,failure:void 0===o.lastError?void 0:f(o.lastError)})}}catch(t){e()&&m.onStorageError?.("Outbox 回读失败",t)}}}}export{m as createOutboxManager};
package/dist/sdk.d.ts CHANGED
@@ -2917,9 +2917,9 @@ interface TextMessageOptions {
2917
2917
  mentions?: readonly MentionInput[];
2918
2918
  replyToMessageId?: string;
2919
2919
  }
2920
- /** 首期允许主动分享的名片 DTO;机器人和未知 wire 类别只保留接收兼容。 */
2920
+ /** 可主动分享的名片。未知 wire 类别只保留接收,不能放进发送入参。 */
2921
2921
  type ContactCardInput = Omit<ImContactBody, 'kind' | 'rawKind'> & {
2922
- kind: Extract<ImContactBody['kind'], 'individual' | 'group' | 'channel'>;
2922
+ kind: Extract<ImContactBody['kind'], 'individual' | 'group' | 'channel' | 'robot'>;
2923
2923
  };
2924
2924
  /** 名片发送附加数据;回复关系与其它可发送消息使用同一稳定消息 id。 */
2925
2925
  interface ContactMessageOptions {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "k2-im",
3
3
  "type": "module",
4
- "version": "0.2.2",
4
+ "version": "0.2.3",
5
5
  "private": false,
6
6
  "license": "MIT",
7
7
  "publishConfig": {