feat: define MessagingAdapter contract

This commit is contained in:
2026-07-18 00:45:03 +05:30
parent 0c9b27684b
commit 3e9951b3a8
+56
View File
@@ -0,0 +1,56 @@
import type {
Attachment,
Conversation,
Membership,
Message,
MessageEvent,
SendOpts,
Unsubscribe,
} from './types';
/**
* The one seam of this SDK. Hosts implement this; the SDK renders it.
*
* Lifted from lynkeduppro-crm's MessengerData/ThreadData, which already survived
* two implementations (live data-door + mock) — the minimum real evidence that a
* seam is genuine rather than imagined.
*
* Optional methods degrade gracefully: the UI hides the reaction picker when
* `react` is absent, and the attach button when `upload` is absent. That is how one
* component set serves both a full CRM messenger and a stripped-down widget with no
* `mode` prop.
*/
export interface MessagingAdapter {
listConversations(): Promise<Conversation[]>;
openThread(p: { participantIds: string[]; membership?: Membership; subject?: string }): Promise<{ threadId: string }>;
history(threadId: string): Promise<Message[]>;
send(threadId: string, content: string, opts?: SendOpts): Promise<Message>;
/** Returns an unsubscribe fn. Implementations MUST be idempotent on repeat unsubscribe. */
subscribe(threadId: string, cb: (e: MessageEvent) => void): Unsubscribe;
sendTyping(threadId: string): void;
markRead(threadId: string, messageId: string): Promise<void>;
/**
* The current user's actor id, or null if not yet known.
*
* MUST NOT be inferred from message history. The CRM's bug was exactly that:
* scanning for a sent message meant every message read as not-yours until you
* had spoken. Adapters derive this from auth/session.
*/
currentActorId(): string | null;
/** Absent => the UI hides reactions entirely. */
react?(threadId: string, messageId: string, emoji: string): Promise<void>;
/** Absent => the UI hides attachments. Storage/auth/limits are the host's concern. */
upload?(file: File): Promise<Attachment>;
/** Absent => treated as always connected (e.g. a pure-REST adapter). */
isConnected?(): boolean;
}