@pmate/chat-sdk 0.12.0-next.8 → 0.12.0-next.80

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 (111) hide show
  1. package/README.md +170 -83
  2. package/lib/advanced.d.ts +10 -0
  3. package/lib/advanced.js +10 -0
  4. package/lib/chat.d.ts +5 -0
  5. package/lib/chat.js +11 -0
  6. package/lib/chatContract.d.ts +151 -0
  7. package/lib/chatContract.js +1 -0
  8. package/lib/chatError.d.ts +14 -0
  9. package/lib/chatError.js +16 -0
  10. package/lib/chatTypes.d.ts +127 -0
  11. package/lib/chatTypes.js +1 -0
  12. package/lib/commands.d.ts +20 -0
  13. package/lib/commands.js +12 -0
  14. package/lib/components/ChatInputDesktop.js +5 -4
  15. package/lib/components/ChatInputMobile.js +5 -4
  16. package/lib/components/CommandMenu.d.ts +15 -0
  17. package/lib/components/CommandMenu.js +10 -0
  18. package/lib/credentials.d.ts +26 -0
  19. package/lib/credentials.js +110 -0
  20. package/lib/fileMetadata.d.ts +9 -0
  21. package/lib/fileMetadata.js +18 -0
  22. package/lib/framework.d.ts +11 -0
  23. package/lib/framework.js +12 -0
  24. package/lib/history.d.ts +4 -0
  25. package/lib/history.js +8 -0
  26. package/lib/hooks/useChatSnapshot.d.ts +4 -0
  27. package/lib/hooks/useChatSnapshot.js +9 -0
  28. package/lib/hooks/useComposerSuggestions.d.ts +50 -0
  29. package/lib/hooks/useComposerSuggestions.js +70 -0
  30. package/lib/index.d.ts +5 -0
  31. package/lib/index.js +5 -0
  32. package/lib/internal/chatEngine.d.ts +130 -0
  33. package/lib/internal/chatEngine.js +284 -0
  34. package/lib/internal/chatRegistry.d.ts +4 -0
  35. package/lib/internal/chatRegistry.js +4 -0
  36. package/lib/internal/createPublicChat.d.ts +4 -0
  37. package/lib/internal/createPublicChat.js +52 -0
  38. package/lib/messageView.d.ts +6 -0
  39. package/lib/messageView.js +103 -0
  40. package/lib/runtime/batchedReadIndex.d.ts +16 -3
  41. package/lib/runtime/batchedReadIndex.js +50 -4
  42. package/lib/runtime/cachedMessage.d.ts +12 -0
  43. package/lib/runtime/cachedMessage.js +18 -0
  44. package/lib/runtime/chatClient.d.ts +7 -0
  45. package/lib/runtime/chatClient.js +14 -3
  46. package/lib/runtime/chatError.d.ts +5 -0
  47. package/lib/runtime/chatError.js +20 -0
  48. package/lib/runtime/chatSession.d.ts +128 -13
  49. package/lib/runtime/chatSession.js +759 -48
  50. package/lib/runtime/connectivity.d.ts +4 -0
  51. package/lib/runtime/connectivity.js +8 -0
  52. package/lib/runtime/conversationDirectory.d.ts +37 -0
  53. package/lib/runtime/conversationDirectory.js +116 -0
  54. package/lib/runtime/conversationRead.d.ts +13 -0
  55. package/lib/runtime/conversationRead.js +34 -0
  56. package/lib/runtime/conversationReading.d.ts +28 -0
  57. package/lib/runtime/conversationReading.js +47 -0
  58. package/lib/runtime/createChat.d.ts +11 -7
  59. package/lib/runtime/createChat.js +46 -23
  60. package/lib/runtime/durableSender.d.ts +97 -14
  61. package/lib/runtime/durableSender.js +786 -71
  62. package/lib/runtime/groupClient.d.ts +91 -0
  63. package/lib/runtime/groupClient.js +380 -0
  64. package/lib/runtime/groupDenial.d.ts +2 -0
  65. package/lib/runtime/groupDenial.js +4 -0
  66. package/lib/runtime/index.d.ts +7 -0
  67. package/lib/runtime/index.js +5 -0
  68. package/lib/runtime/indexedDbRecovery.d.ts +4 -0
  69. package/lib/runtime/indexedDbRecovery.js +27 -0
  70. package/lib/runtime/messageClient.d.ts +35 -8
  71. package/lib/runtime/messageClient.js +364 -66
  72. package/lib/runtime/messageControls.d.ts +19 -0
  73. package/lib/runtime/messageControls.js +44 -0
  74. package/lib/runtime/messageLaunch.d.ts +44 -0
  75. package/lib/runtime/messageLaunch.js +137 -0
  76. package/lib/runtime/messageQuotes.d.ts +3 -0
  77. package/lib/runtime/messageQuotes.js +17 -0
  78. package/lib/runtime/messageReaders.d.ts +5 -1
  79. package/lib/runtime/messageReaders.js +16 -5
  80. package/lib/runtime/messageStore.d.ts +43 -2
  81. package/lib/runtime/messageStore.js +248 -14
  82. package/lib/runtime/messageTimeline.d.ts +50 -0
  83. package/lib/runtime/messageTimeline.js +120 -0
  84. package/lib/runtime/outboxSchema.d.ts +5 -0
  85. package/lib/runtime/outboxSchema.js +59 -0
  86. package/lib/runtime/outgoingContent.d.ts +94 -0
  87. package/lib/runtime/outgoingContent.js +118 -0
  88. package/lib/runtime/readBatchJournal.d.ts +35 -0
  89. package/lib/runtime/readBatchJournal.js +210 -0
  90. package/lib/runtime/readCounts.d.ts +15 -1
  91. package/lib/runtime/readCounts.js +97 -9
  92. package/lib/runtime/readIndex.d.ts +68 -7
  93. package/lib/runtime/readIndex.js +460 -22
  94. package/lib/runtime/readSchema.d.ts +2 -0
  95. package/lib/runtime/readSchema.js +58 -0
  96. package/lib/runtime/readSync.d.ts +1 -0
  97. package/lib/runtime/readSync.js +26 -9
  98. package/lib/runtime/readingClient.d.ts +19 -0
  99. package/lib/runtime/readingClient.js +131 -19
  100. package/lib/runtime/readingContent.d.ts +5 -0
  101. package/lib/runtime/readingContent.js +23 -0
  102. package/lib/runtime/responseCache.d.ts +40 -5
  103. package/lib/runtime/responseCache.js +182 -44
  104. package/lib/runtime/socialReadClient.d.ts +9 -4
  105. package/lib/runtime/socialReadClient.js +12 -8
  106. package/lib/runtime/voiceUpload.d.ts +2 -0
  107. package/lib/runtime/voiceUpload.js +27 -0
  108. package/lib/snapshot.d.ts +36 -0
  109. package/lib/snapshot.js +50 -0
  110. package/lib/types/chatInput.d.ts +3 -0
  111. package/package.json +32 -2
@@ -123,7 +123,7 @@ export class ChatClient {
123
123
  /** Non-secret identity used to validate a shared browser transport. */
124
124
  identityScope() {
125
125
  const identity = this.session?.identity ?? this.session?.session?.identity;
126
- return { appId: this.options.appId, profileId: this.profileIdValue ?? this.options.profileId, accountId: identity?.accountId, teamId: this.options.teamId };
126
+ return { appId: this.options.appId, profileId: this.profileIdValue ?? this.options.profileId, accountId: identity?.accountId, teamId: this.options.teamId, clientInstanceId: this.instanceId };
127
127
  }
128
128
  watchConnection(handler) {
129
129
  this.connectionHandlers.add(handler);
@@ -161,7 +161,7 @@ export class ChatClient {
161
161
  catch (error) {
162
162
  if (generation !== this.generation)
163
163
  return;
164
- if (error instanceof ChatAckError) {
164
+ if (error instanceof ChatAckError || ["authentication-required", "access-denied"].includes(error?.code ?? "")) {
165
165
  this.close();
166
166
  this.setConnection("authentication_required");
167
167
  }
@@ -252,6 +252,12 @@ export class ChatClient {
252
252
  addMembers: input.addMembers ?? [], removeMembers: input.removeMembers ?? [], ...(input.title !== undefined ? { title: input.title } : {}), ...(input.avatar !== undefined ? { avatar: input.avatar } : {}) },
253
253
  });
254
254
  }
255
+ async dissolveGroup(input) {
256
+ return this.sendChat(input.groupId, {
257
+ kind: "group", threadKind: "group", action: "groupDissolve",
258
+ payload: { appId: this.options.appId, groupId: input.groupId, ...(input.teamId !== undefined ? { teamId: input.teamId } : {}), expectedRevision: input.expectedRevision },
259
+ });
260
+ }
255
261
  async sendGroup(input) {
256
262
  return this.sendChat(input.groupId, {
257
263
  kind: "message",
@@ -295,7 +301,12 @@ export class ChatClient {
295
301
  });
296
302
  }
297
303
  async sendAuth() {
298
- const token = this.session?.token;
304
+ const generation = this.generation;
305
+ const token = this.options.getToken ? await this.options.getToken() : this.session?.token;
306
+ if (generation !== this.generation)
307
+ throw new Error("ChatClient closed");
308
+ if (this.session && token)
309
+ this.session = { ...this.session, token };
299
310
  if (!token) {
300
311
  throw new Error("Missing PMate session token");
301
312
  }
@@ -0,0 +1,5 @@
1
+ import { ChatError } from "../chatError.js";
2
+ export { ChatError, type ChatErrorCode } from "../chatError.js";
3
+ export declare function asChatError(error: unknown): ChatError;
4
+ /** Keep a conversation denial distinct from loss of Team authority. */
5
+ export declare function threadChatError(error: unknown, threadId: string): ChatError;
@@ -0,0 +1,20 @@
1
+ import { ChatError } from "../chatError.js";
2
+ export { ChatError } from "../chatError.js";
3
+ export function asChatError(error) {
4
+ if (error instanceof ChatError)
5
+ return error;
6
+ const value = error;
7
+ const status = value?.status, message = value?.message ?? String(error);
8
+ const code = status === 401 ? "authentication-required" : status === 403 || /ACCESS_REVOKED|ACCESS_DENIED|SCOPE_CHANGED/.test(message) ? "access-denied"
9
+ : /CLOSED/.test(message) ? "closed"
10
+ : status === 409 || /REVISION_CONFLICT|PROJECTION_CHANGED/.test(message) ? "conflict"
11
+ : /BACKPRESSURE|TOO_LARGE/.test(message) ? "busy"
12
+ : status === 400 || /INVALID_|CONVERSATION_MISMATCH|CONVERSATION_UNKNOWN|NOT_OWNED/.test(message) ? "invalid-input"
13
+ : /QuotaExceeded|DatabaseClosed|UnknownError|AbortError/.test(value?.name ?? "") || /STORAGE_|DATABASE_|CACHE_WRITE/.test(message) ? "storage-unavailable" : "unavailable";
14
+ return new ChatError(code, message, { cause: error, status, threadId: value?.threadId });
15
+ }
16
+ /** Keep a conversation denial distinct from loss of Team authority. */
17
+ export function threadChatError(error, threadId) {
18
+ const failure = asChatError(error);
19
+ return failure.code === "access-denied" ? new ChatError(failure.code, failure.message, { cause: failure, status: failure.status, threadId }) : failure;
20
+ }
@@ -1,61 +1,124 @@
1
+ import { type ConversationTarget } from "./conversationDirectory.js";
2
+ import { type ChatGroup } from "./groupClient.js";
3
+ import type { SocialGroup } from "./socialReadClient.js";
4
+ import { type ChatConversation, type OutgoingContent } from "./outgoingContent.js";
5
+ import { type TimelineItem } from "./messageTimeline.js";
6
+ import { type ReadingCandidate, type MessageReadingState } from "./conversationReading.js";
7
+ import type { ConversationReadMessage } from "./conversationRead.js";
8
+ import { type ReadCountsResult } from "./readCounts.js";
9
+ import type { OnlineReachEvent } from "./reach.js";
1
10
  import { type MessageReadersQuery } from "./messageReaders.js";
2
11
  import { type MessageClientOptions, type MessageSnapshot } from "./messageClient.js";
3
- import { DurableChatSender } from "./durableSender.js";
4
- import { type ReadingClientOptions } from "./readingClient.js";
12
+ import { DurableChatSender, type SendMessageInput, type DurableSenderOptions, type VoiceAttachmentLease } from "./durableSender.js";
13
+ import { ReadingClient, type ReadingClientOptions } from "./readingClient.js";
5
14
  import { ChatClient, type ChatClientOptions, type ChatConnectionState } from "./chatClient.js";
6
15
  import { unreadInConversation } from "./readIndex.js";
7
16
  import type { StoredMessage } from "./messageStore.js";
8
- import type { ChatBody, ChatRuntimeMessage } from "./protocol.js";
17
+ import type { ChatRuntimeMessage } from "./protocol.js";
9
18
  export type ChatSessionOptions<T extends StoredMessage> = {
19
+ onDispose?: () => void;
10
20
  messages: MessageClientOptions<T>;
11
21
  connection: ChatClientOptions;
12
22
  sharedConnection?: ChatClient;
23
+ activeThreadId?: string;
24
+ uploadVoice?: DurableSenderOptions["uploadVoice"];
25
+ reconcileOutgoing?: DurableSenderOptions["reconcile"];
26
+ loadGroup?: (groupId: string, signal: AbortSignal) => Promise<SocialGroup>;
13
27
  reading: ReadingClientOptions;
14
28
  decode: (message: ChatRuntimeMessage) => T | undefined;
29
+ readingCandidate?: (message: T) => ReadingCandidate | undefined;
15
30
  onError: (error: unknown) => void;
16
31
  };
17
32
  export type ChatThreadSnapshot<T> = {
18
33
  messages: readonly T[];
34
+ timeline: readonly TimelineItem<T>[];
19
35
  outgoing: ReturnType<DurableChatSender["snapshot"]>;
20
36
  hasEarlier: boolean;
21
37
  resetVersion: number;
22
38
  complete: boolean;
23
- unread: ReturnType<typeof unreadInConversation>;
39
+ group?: ChatGroup;
40
+ readCounts?: ReadCountsResult;
41
+ unread: ReturnType<typeof unreadInConversation> | undefined;
42
+ messageReading: Readonly<Record<string, MessageReadingState>>;
24
43
  };
25
44
  export type ChatSummarySnapshot<T> = {
26
45
  threads: readonly {
27
46
  threadId: string;
28
47
  lastMessage?: T;
48
+ lastItem?: TimelineItem<T>;
49
+ group?: ChatGroup;
29
50
  unread: ChatThreadSnapshot<T>["unread"];
30
51
  outgoingCount: number;
31
52
  }[];
32
- unreadMessageCount: number;
33
- badgeCount: number;
53
+ unreadMessageCount: number | undefined;
54
+ badgeCount: number | undefined;
34
55
  connection: ChatConnectionState;
35
56
  };
36
57
  /** Owns all browser message clients. Hosts subscribe to stable thread snapshots. */
37
58
  export declare class ChatSession<T extends StoredMessage> {
38
59
  private options;
60
+ private conversations;
39
61
  private messages;
40
62
  private connection;
41
63
  private reading;
42
64
  private readers;
65
+ private groups?;
66
+ private counts?;
67
+ private countsReceiver?;
68
+ private countsThread?;
69
+ private countSnapshots;
70
+ private countClosings;
71
+ private countTasks;
72
+ private controlTails;
73
+ private acceptedControls;
74
+ private controlCount;
75
+ private controlBytes;
43
76
  private sender;
44
77
  private listeners;
45
78
  private snapshots;
79
+ private timelineDecoded;
80
+ private decodeTimeline;
46
81
  private historyEdges;
47
82
  private earlierReads;
48
83
  private off;
49
84
  private globalListeners;
50
85
  private globalSnapshot?;
86
+ private closedThread;
87
+ private closedSummary;
51
88
  private connectionState;
52
89
  private catchUp;
90
+ private readingAllowed;
91
+ private readingListeners;
92
+ private acceptedReading;
93
+ private readingAccess?;
53
94
  private readingStarted;
95
+ private readingStarting?;
96
+ private withdrawn;
97
+ private revoking?;
54
98
  private closed;
55
99
  private starting?;
100
+ private initializing?;
56
101
  private closing?;
57
102
  constructor(options: ChatSessionOptions<T>);
103
+ private scheduleCatchUp;
58
104
  start(): Promise<void>;
105
+ /** Initializes local state and background reading sync without opening a
106
+ * second transport when the host already owns calls/presence. */
107
+ initialize(): Promise<void>;
108
+ private startReading;
109
+ /** One-time migration only: never establishes trusted history coverage. */
110
+ importLegacyMessages(threadId: string, messages: T[]): Promise<void>;
111
+ getReadingSnapshot(): import("./readIndex.js").ReadIndexState | undefined;
112
+ subscribeReading(listener: () => void): () => void;
113
+ private setReadingAllowed;
114
+ private readingAuthority;
115
+ private authorizeReading;
116
+ private readingOperation;
117
+ refreshReading(): void;
118
+ synchronizeReading(cursor?: string, profileId?: string): Promise<import("./readIndex.js").ReadIndexState>;
119
+ /** Explicit text-history acknowledgement; unheard audio stays unread. */
120
+ markRead(threadId: string, messages?: readonly ConversationReadMessage[]): Promise<import("./readIndex.js").ReadIndexState>;
121
+ updateReading(transform: Parameters<ReadingClient["update"]>[0]): Promise<import("./readIndex.js").ReadIndexState>;
59
122
  private assertOpen;
60
123
  getMessages(threadId: string, options?: {
61
124
  limit?: number;
@@ -67,24 +130,76 @@ export declare class ChatSession<T extends StoredMessage> {
67
130
  all?: boolean;
68
131
  refresh?: boolean;
69
132
  }): Promise<MessageSnapshot<T>>;
133
+ private confirmMessages;
70
134
  private rememberHistoryEdge;
71
135
  private readEarlier;
72
136
  private readAll;
73
- sendMessage(threadId: string, input: {
74
- to: string;
75
- body: ChatBody;
76
- recipients?: string[];
137
+ /** Reads trusted local group metadata immediately; SDK refreshes it in the background. */
138
+ getGroup(thread: ChatConversation | string, options?: {
139
+ refresh?: boolean;
140
+ }): Promise<ChatGroup>;
141
+ createGroup(thread: ChatConversation, content: {
142
+ title: string;
143
+ members: readonly string[];
144
+ }): Promise<ChatGroup>;
145
+ updateGroup(target: ChatConversation | string, changes: {
146
+ expectedRevision?: string;
147
+ addMembers?: readonly string[];
148
+ removeMembers?: readonly string[];
149
+ title?: string;
150
+ avatar?: string;
151
+ }): Promise<ChatGroup>;
152
+ dissolveGroup(target: ChatConversation | string, options?: {
153
+ expectedRevision?: string;
154
+ }): Promise<ChatGroup>;
155
+ sendMessage(threadId: string, input: OutgoingContent, options?: {
156
+ waitFor?: "accepted" | "delivered";
157
+ }): Promise<ChatRuntimeMessage>;
158
+ sendMessage(thread: ChatConversation, input: OutgoingContent, options?: {
159
+ waitFor?: "accepted" | "delivered";
160
+ }): Promise<ChatRuntimeMessage>;
161
+ sendMessage(threadId: string, input: SendMessageInput, options?: {
162
+ waitFor?: "accepted" | "delivered";
77
163
  }): Promise<ChatRuntimeMessage>;
164
+ private sendWire;
165
+ getConversation(target: ConversationTarget): Promise<string>;
166
+ private sendContent;
167
+ private resolveConversation;
168
+ /** Validated target identity survives content redaction; no roster is exposed. */
169
+ getConversationTarget(threadId: string): ChatConversation | undefined;
170
+ private sendControl;
78
171
  getReaders(threadId: string, messageId: string, page?: Omit<MessageReadersQuery, "threadId" | "messageId">, signal?: AbortSignal): Promise<import("./messageReaders.js").MessageReadersPage>;
172
+ /** Resolve voice media uniformly; pending local bytes are loaded only on demand. */
173
+ getVoiceAttachment(threadId: string, messageId: string): Promise<VoiceAttachmentLease>;
79
174
  retryMessage(messageId: string): Promise<ChatRuntimeMessage>;
80
- observeMessages(threadId: string, ids: string[], event: "visible" | "played"): Promise<import("./readIndex.js").ReadIndexState>;
175
+ observeMessages(threadId: string, ids: string[], event: "visible" | "played"): ReturnType<ReadingClient["recordVisible"]>;
176
+ observeMessages(threadId: string | undefined, ids: string[], event: "receipts", clientInstanceId?: string): Promise<void>;
177
+ /** Bridge transports can delegate social events without maintaining clients. */
178
+ handleReach(event: OnlineReachEvent): Promise<"handled" | undefined>;
179
+ private wakeCounts;
180
+ private publishCountSnapshot;
181
+ private publishCounts;
182
+ private watchCounts;
81
183
  getSnapshot(): ChatSummarySnapshot<T>;
82
184
  getSnapshot(threadId: string): ChatThreadSnapshot<T>;
83
- subscribe(listener: () => void): () => void;
84
- subscribe(threadId: string, listener: () => void): () => void;
185
+ private buildThreadSnapshot;
186
+ /** Delivers the current stable snapshot immediately, then only meaningful changes. */
187
+ subscribe(listener: (snapshot: ChatSummarySnapshot<T>) => void): () => void;
188
+ subscribe(threadId: string, listener: (snapshot: ChatThreadSnapshot<T>) => void, fields?: readonly (keyof ChatThreadSnapshot<T>)[]): () => void;
189
+ /** Change-only bridge for useSyncExternalStore and other framework adapters. */
190
+ subscribeChanges(listener: () => void): () => void;
191
+ subscribeChanges(threadId: string, listener: () => void, fields?: readonly (keyof ChatThreadSnapshot<T>)[]): () => void;
85
192
  private publishAll;
86
193
  private buildGlobal;
87
194
  private publishGlobal;
88
195
  private publish;
196
+ conversationFailure(error: unknown, threadId: string): Promise<import("./chatError.js").ChatError>;
197
+ /** Withdraw one conversation without retiring sibling conversations. */
198
+ revokeThread(threadId: string): Promise<void>;
199
+ /** Permanently retire this identity/team session. UI withdrawal is immediate;
200
+ * completion means all durable stores have crossed the same authority epoch. */
201
+ revokeAccess(): Promise<void>;
202
+ get active(): boolean;
89
203
  close(): Promise<void>;
204
+ private dispose;
90
205
  }