@pmate/chat-sdk 0.12.0-next.7 → 0.12.0-next.71
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 +170 -81
- package/lib/advanced.d.ts +10 -0
- package/lib/advanced.js +10 -0
- package/lib/chat.d.ts +5 -0
- package/lib/chat.js +11 -0
- package/lib/chatContract.d.ts +148 -0
- package/lib/chatContract.js +1 -0
- package/lib/chatError.d.ts +11 -0
- package/lib/chatError.js +13 -0
- package/lib/chatTypes.d.ts +121 -0
- package/lib/chatTypes.js +1 -0
- package/lib/commands.d.ts +20 -0
- package/lib/commands.js +12 -0
- package/lib/components/ChatInputDesktop.js +5 -4
- package/lib/components/ChatInputMobile.js +5 -4
- package/lib/components/CommandMenu.d.ts +15 -0
- package/lib/components/CommandMenu.js +10 -0
- package/lib/credentials.d.ts +26 -0
- package/lib/credentials.js +110 -0
- package/lib/fileMetadata.d.ts +9 -0
- package/lib/fileMetadata.js +18 -0
- package/lib/framework.d.ts +11 -0
- package/lib/framework.js +12 -0
- package/lib/history.d.ts +4 -0
- package/lib/history.js +8 -0
- package/lib/hooks/useChatSnapshot.d.ts +4 -0
- package/lib/hooks/useChatSnapshot.js +9 -0
- package/lib/hooks/useComposerSuggestions.d.ts +50 -0
- package/lib/hooks/useComposerSuggestions.js +70 -0
- package/lib/index.d.ts +5 -0
- package/lib/index.js +5 -0
- package/lib/internal/chatEngine.d.ts +131 -0
- package/lib/internal/chatEngine.js +280 -0
- package/lib/internal/chatRegistry.d.ts +4 -0
- package/lib/internal/chatRegistry.js +4 -0
- package/lib/internal/createPublicChat.d.ts +4 -0
- package/lib/internal/createPublicChat.js +52 -0
- package/lib/messageView.d.ts +6 -0
- package/lib/messageView.js +103 -0
- package/lib/runtime/batchedReadIndex.d.ts +16 -3
- package/lib/runtime/batchedReadIndex.js +50 -4
- package/lib/runtime/cachedMessage.d.ts +12 -0
- package/lib/runtime/cachedMessage.js +18 -0
- package/lib/runtime/chatClient.d.ts +2 -0
- package/lib/runtime/chatClient.js +8 -3
- package/lib/runtime/chatError.d.ts +3 -0
- package/lib/runtime/chatError.js +15 -0
- package/lib/runtime/chatSession.d.ts +127 -13
- package/lib/runtime/chatSession.js +727 -40
- package/lib/runtime/connectivity.d.ts +4 -0
- package/lib/runtime/connectivity.js +8 -0
- package/lib/runtime/conversationDirectory.d.ts +37 -0
- package/lib/runtime/conversationDirectory.js +116 -0
- package/lib/runtime/conversationRead.d.ts +13 -0
- package/lib/runtime/conversationRead.js +34 -0
- package/lib/runtime/conversationReading.d.ts +28 -0
- package/lib/runtime/conversationReading.js +47 -0
- package/lib/runtime/createChat.d.ts +11 -7
- package/lib/runtime/createChat.js +46 -23
- package/lib/runtime/durableSender.d.ts +97 -14
- package/lib/runtime/durableSender.js +786 -71
- package/lib/runtime/groupClient.d.ts +76 -0
- package/lib/runtime/groupClient.js +325 -0
- package/lib/runtime/index.d.ts +7 -0
- package/lib/runtime/index.js +5 -0
- package/lib/runtime/messageClient.d.ts +35 -8
- package/lib/runtime/messageClient.js +364 -66
- package/lib/runtime/messageControls.d.ts +19 -0
- package/lib/runtime/messageControls.js +44 -0
- package/lib/runtime/messageLaunch.d.ts +44 -0
- package/lib/runtime/messageLaunch.js +137 -0
- package/lib/runtime/messageQuotes.d.ts +3 -0
- package/lib/runtime/messageQuotes.js +17 -0
- package/lib/runtime/messageReaders.d.ts +12 -1
- package/lib/runtime/messageReaders.js +67 -3
- package/lib/runtime/messageStore.d.ts +40 -2
- package/lib/runtime/messageStore.js +227 -11
- package/lib/runtime/messageTimeline.d.ts +50 -0
- package/lib/runtime/messageTimeline.js +120 -0
- package/lib/runtime/outboxSchema.d.ts +5 -0
- package/lib/runtime/outboxSchema.js +59 -0
- package/lib/runtime/outgoingContent.d.ts +89 -0
- package/lib/runtime/outgoingContent.js +113 -0
- package/lib/runtime/readBatchJournal.d.ts +35 -0
- package/lib/runtime/readBatchJournal.js +210 -0
- package/lib/runtime/readCounts.d.ts +15 -1
- package/lib/runtime/readCounts.js +97 -9
- package/lib/runtime/readIndex.d.ts +68 -7
- package/lib/runtime/readIndex.js +460 -22
- package/lib/runtime/readSchema.d.ts +2 -0
- package/lib/runtime/readSchema.js +58 -0
- package/lib/runtime/readSync.d.ts +1 -0
- package/lib/runtime/readSync.js +26 -9
- package/lib/runtime/readingClient.d.ts +19 -0
- package/lib/runtime/readingClient.js +131 -19
- package/lib/runtime/readingContent.d.ts +5 -0
- package/lib/runtime/readingContent.js +23 -0
- package/lib/runtime/responseCache.d.ts +59 -0
- package/lib/runtime/responseCache.js +232 -0
- package/lib/runtime/socialReadClient.d.ts +4 -2
- package/lib/runtime/socialReadClient.js +12 -8
- package/lib/runtime/voiceUpload.d.ts +2 -0
- package/lib/runtime/voiceUpload.js +27 -0
- package/lib/types/chatInput.d.ts +3 -0
- package/package.json +27 -2
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ A dropped socket (including a network change or a suspended laptop) previously r
|
|
|
39
39
|
|
|
40
40
|
`client.watchConnection(handler)` immediately reports the current state and returns an unsubscribe function. States are `connecting`, `reconnecting`, `authenticating`, `connected`, `authentication_required`, and `closed`. Only `connected` means the Hub acknowledged authentication. A rejected authentication stops retries; supply a fresh login session using a new client. `close()` cancels retries, heartbeat timers and browser wake listeners.
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
The low-level `ChatClient.sendChat` rejects unacknowledged sends on disconnect and does not provide a durable outbox. Browser applications should use `createChat().sendMessage` below, which persists the exact wire in IndexedDB and owns delivery state and retries. An initial `connect()` wait times out after 30 seconds, but recovery continues until explicitly closed; retain the client and observe its connection state.
|
|
43
43
|
|
|
44
44
|
## Versioned Markdown and chart content
|
|
45
45
|
|
|
@@ -128,93 +128,182 @@ The handler leaves text-only pastes and events already handled by the host untou
|
|
|
128
128
|
|
|
129
129
|
Only send on explicit submission: block send while `pending`, deliver each ready attachment's `url`, and remove it after delivery succeeds. Keep unsuccessful drafts to allow retry without reuploading. For `ChatInputDesktop`, use `hasPendingContent` and `sendDisabled` with host-owned drafts. Unmount or key the draft owner when switching accounts, Teams or conversations.
|
|
130
130
|
|
|
131
|
-
## Browser
|
|
131
|
+
## Browser messages
|
|
132
132
|
|
|
133
133
|
### Background
|
|
134
134
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
The browser entry point is `createChat` from `@pmate/chat-sdk/runtime`:
|
|
135
|
+
App code should render messages and report actual user actions. IndexedDB,
|
|
136
|
+
trusted recent windows, synchronization, recipients, retries and read rules belong
|
|
137
|
+
to the SDK. The default API presents one semantic message list, including delivery,
|
|
138
|
+
recalls and reactions. It never presents a partial migration as a ready window.
|
|
141
139
|
|
|
142
140
|
```ts
|
|
143
|
-
|
|
141
|
+
import {createChat} from "@pmate/chat-sdk/chat"
|
|
142
|
+
const chat = createChat({appId, accountId, profileId, teamId,
|
|
143
|
+
getToken: ({forceRefresh, signal}) => auth.getAccessToken({forceRefresh, signal}),
|
|
144
|
+
})
|
|
144
145
|
await chat.start()
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
//
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
146
|
+
const threadId = await chat.resolveThreadId({peerId}) // or {groupId}; no remote creation
|
|
147
|
+
const off = chat.subscribeThread(threadId, state => {
|
|
148
|
+
if (state.status === "ready") render(state.messages)
|
|
149
|
+
else if (state.status === "error") showRetry(state.error)
|
|
150
|
+
}) // listens first, opens local state, then owns background synchronization
|
|
151
|
+
await chat.sendMessage(threadId, {text: "Hello"})
|
|
152
|
+
await chat.loadEarlier(threadId)
|
|
153
|
+
off()
|
|
154
|
+
await chat.close()
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`getThread(id)` is the one-shot equivalent; it returns the same stable snapshot
|
|
158
|
+
as `subscribeThread`. Cache hits do not repeat history requests. `loadEarlier(id)`
|
|
159
|
+
extends the SDK window without limits or cursors. `refreshThread(id)` explicitly
|
|
160
|
+
reconciles authority; ordinary reconnect/wake synchronization is automatic.
|
|
161
|
+
For a cold route, keep the usable list visible until `getThread` resolves, or
|
|
162
|
+
until a subscription delivers `status: "ready"`; no intermediate message loader
|
|
163
|
+
is necessary. Subscription initialization has four bounded concurrent lanes.
|
|
164
|
+
Multiple consumers share work; disposing one does not stop another consumer.
|
|
165
|
+
|
|
166
|
+
Thread state is `pending`, `ready`, or `error`. A failed initial read is `error`;
|
|
167
|
+
a failed background refresh retains `ready` messages and includes `error`.
|
|
168
|
+
`ready` is retained as a derived compatibility field; use `status` in new code.
|
|
169
|
+
Connection state is independent. A snapshot also contains `hasEarlier`, `unread`,
|
|
170
|
+
`group`, `readCounts` and scoped target identity. Unknown unread stays undefined.
|
|
171
|
+
`subscribeChat` delivers known conversation summaries and aggregate reading state.
|
|
172
|
+
Subscriptions emit their current snapshot first, then meaningful changes only;
|
|
173
|
+
`fields` filters notifications. Callbacks cannot corrupt a shared cache read.
|
|
174
|
+
Opening or subscribing does not mark messages read.
|
|
175
|
+
|
|
176
|
+
Messages provide kind/text/image/voice/quote, reactions and reading/delivery state.
|
|
177
|
+
No raw wire or control revision is needed. The public models do not derive their
|
|
178
|
+
types from Session, storage or projection internals. Object references stay stable
|
|
179
|
+
when content has not changed. React `useChatSnapshot(chat, threadId)` manages the
|
|
180
|
+
external-store subscription; custom integrations use `getChatStore` from
|
|
181
|
+
`@pmate/chat-sdk/framework`, where synchronous memory snapshots and change-only
|
|
182
|
+
notifications are explicitly separated from ordinary asynchronous reads.
|
|
183
|
+
|
|
184
|
+
`getThread`, `loadEarlier`, `refreshThread`, `getGroup`, `refreshGroup`,
|
|
185
|
+
`resolveThreadId`, and `getVoiceAttachment` accept `{signal}`. Reader queries accept
|
|
186
|
+
`{afterProfileId, revision, signal}`. Aborting rejects that consumer with the signal
|
|
187
|
+
reason (normally AbortError); it does not abort shared work, publish a fake cache
|
|
188
|
+
failure, or undo a durable send. A late abandoned voice lease is released.
|
|
189
|
+
Complete-history search/export is an explicit, potentially large operation:
|
|
190
|
+
`loadThreadHistory(chat, id, {signal})` from `@pmate/chat-sdk/history`. It traverses
|
|
191
|
+
accessible history and rejects incomplete scans. It is not a flag on normal reads.
|
|
192
|
+
|
|
193
|
+
The credential provider is invoked on connection/reconnection and authenticated
|
|
194
|
+
HTTP reads; concurrent requests share acquisition. HTTP 401 gets one coalesced
|
|
195
|
+
forced refresh and at most one replay. HTTP 403 is never retried automatically.
|
|
196
|
+
Providers must preserve account/app/profile identity. A fixed `token` remains
|
|
197
|
+
supported as an alternative; select one source. An ambiguous WebSocket AUTH_FAILED
|
|
198
|
+
requires authentication rather than guessing that a permission failure expired.
|
|
199
|
+
Closing fences late providers and prevents further authenticated network work.
|
|
200
|
+
|
|
201
|
+
Sending accepts text/mentions/quotes, images, files, voice, recalls, reactions and
|
|
202
|
+
invitations through the same typed content union. The SDK resolves the target
|
|
203
|
+
and group roster, expands @all and assigns reaction revisions. The default
|
|
204
|
+
Promise resolves after durable acceptance and returns `{id,timestamp}`; outgoing
|
|
205
|
+
status is part of the same message list. `{waitFor:"delivered"}` waits for the
|
|
206
|
+
server acknowledgement. `retryMessage(id)` retries a failed exact wire.
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
await chat.sendMessage(threadId, {file:{url,name,size,mimeType}}) // uploaded HTTPS metadata
|
|
210
|
+
await chat.sendMessage(threadId, {voice:{blob,durationMs}})
|
|
211
|
+
await chat.sendMessage(threadId, {reaction:{messageId,emojiId,active:true}})
|
|
212
|
+
await chat.sendMessage(threadId, {recall:{messageId}}, {waitFor:"delivered"})
|
|
153
213
|
await chat.observeMessages(threadId, visibleIds, "visible")
|
|
154
|
-
|
|
155
|
-
await chat.
|
|
156
|
-
await chat.sendMessage(threadId, { to, body, recipients })
|
|
214
|
+
await chat.observeMessages(threadId, [voiceId], "played") // actual playing event
|
|
215
|
+
await chat.markRead(threadId) // ordinary text only; unheard voice stays unread
|
|
157
216
|
const readers = await chat.getReaders(threadId, messageId)
|
|
158
|
-
off(); offSummary()
|
|
159
|
-
await chat.close()
|
|
160
217
|
```
|
|
161
218
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
`
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
219
|
+
Voice media is acquired on demand through `getVoiceAttachment(threadId,messageId)`.
|
|
220
|
+
Use its `url` and call `release()` when playback/transcription stops. Acquisition,
|
|
221
|
+
clicking play, a fulfilled play Promise and transcription do not count as playing.
|
|
222
|
+
The SDK persists observations locally, batches writes and synchronization, and
|
|
223
|
+
preserves offline intents. It does not promise all media binaries are offline.
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
const group = await chat.getGroup(threadId)
|
|
227
|
+
const created = await chat.createGroup({title, members}) // optional groupId
|
|
228
|
+
await chat.updateGroup(created.threadId, {title: "New title"})
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Group reads restore IndexedDB metadata before background reconciliation. Mutations
|
|
232
|
+
wait for confirmed delivery and a causal server version. SDK-owned requests share
|
|
233
|
+
bounded queues and time windows; Apps do not poll. `watchReaders(threadId,ids,receiver)`
|
|
234
|
+
selects receipt counts for the current viewport; clear with `watchReaders(undefined,[])`.
|
|
235
|
+
|
|
236
|
+
`ChatError` exposes code, retryable, optional HTTP status and cause. Categories are
|
|
237
|
+
unavailable, authentication-required, access-denied, storage-unavailable,
|
|
238
|
+
invalid-input, conflict, closed and busy. Token expiry requires authentication;
|
|
239
|
+
a 403 withdraws the scoped session and its caches. Account/profile/team changes
|
|
240
|
+
require a new SDK instance. Token renewal alone uses the provider.
|
|
241
|
+
|
|
242
|
+
### Advanced integration and migration
|
|
243
|
+
|
|
244
|
+
The default client has no `advanced`, `getSnapshot`, `getMessages`, `getConversation`
|
|
245
|
+
or generic `subscribe`. Use resolveThreadId, getThread, subscribeThread,
|
|
246
|
+
loadEarlier and refreshThread. Public readers no longer accept a separate fourth
|
|
247
|
+
signal argument. Use `{signal}` together with the query instead.
|
|
248
|
+
|
|
249
|
+
Host-owned calls/presence transports use `createChatWithTransport` from
|
|
250
|
+
`@pmate/chat-sdk/advanced`; it returns the same public client. `getChatRuntime(chat)`
|
|
251
|
+
provides explicit one-time migration/diagnostic access. `handleChatReach` and
|
|
252
|
+
`revokeChatAccess` are advanced integration helpers. A deprecated
|
|
253
|
+
`getLegacyChat(chat)` adapter is available only from this entry during migration.
|
|
254
|
+
The raw factory is named `createChatSession` in `/runtime`; its old `createChat`
|
|
255
|
+
name is deprecated. Raw storage/transport clients remain advanced capabilities.
|
|
256
|
+
They must share identity and respect scope authority. Apps do not maintain a second
|
|
257
|
+
message cache, outbox, recipient roster or control revision generator.
|
|
258
|
+
|
|
259
|
+
### Reading storage recovery
|
|
260
|
+
|
|
261
|
+
Large reading snapshots use a persisted journal. The full plan becomes durable
|
|
262
|
+
before canonical rows change; bounded transactions apply it, and the sync cursor
|
|
263
|
+
commits last. A reader recovers an active plan before returning a snapshot. Failed
|
|
264
|
+
staging leaves the previous snapshot authoritative; failure after activation keeps
|
|
265
|
+
the old cursor until recovery completes. Pending reads retain their operation IDs.
|
|
266
|
+
|
|
267
|
+
Writes use at most 128 records per transaction, including journal/lease records,
|
|
268
|
+
and conservatively budget one MiB per batch. Message order is stored per row so a
|
|
269
|
+
source no longer needs a growing array of every message ID. Ordinary observations
|
|
270
|
+
continue to update only positions and pending operations. Locks serialize each
|
|
271
|
+
owner across documents; a fenced IndexedDB lease supports contexts without Web Locks.
|
|
272
|
+
|
|
273
|
+
The private reading database format is now 60. Existing stores are renamed by
|
|
274
|
+
IndexedDB metadata operations, preserving keys and records. Older SDKs cannot
|
|
275
|
+
write through the former namespace and need a page reload to use the new format.
|
|
276
|
+
The message-body cache is separate and unchanged. The public reading state remains
|
|
277
|
+
schema version 1; applications do not select storage formats or recovery steps.
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
Pending voice snapshots contain `voiceAttachment` metadata, without loading audio bytes or creating Blob URLs at startup. For playback or transcription, acquire media through `await chat.getVoiceAttachment(threadId, messageId)`, use the returned `url`, and call `release()` when the consumer stops. The same method returns a remote voice reference for delivered messages. Attachment reads are charged before allocation, and close/revocation releases local URLs. A pending voice remains unread until the player actually starts; acquiring media does not mark it read.
|
|
281
|
+
|
|
282
|
+
The outbox format upgrade renames the old canonical store without copying binary data in the schema transaction, then atomically separates inline payloads in bounded, resumable transactions. An already-open older application must reload; unsupported senders reject the new format. Original wire/hash, failed status and retry semantics are retained.
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
SDK group metadata uses a 200ms merge window, four concurrent requests, a bounded
|
|
286
|
+
128-request queue, five-second timeout and at most five read-after-write attempts.
|
|
287
|
+
The persistent group cache is capped at 64 groups / 1MiB, with 128KiB records,
|
|
288
|
+
32-record write batches, conditional writes and bounded pruning. Conversation
|
|
289
|
+
identities are stored separately without rosters; repeated registrations share
|
|
290
|
+
one write, concurrent lookups use bulk reads, and a concurrent memory-hit burst
|
|
291
|
+
shares one fresh authority read without retaining authorization across bursts. Scope withdrawal retires both.
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
## Slash command suggestions
|
|
295
|
+
|
|
296
|
+
### Background
|
|
297
|
+
|
|
298
|
+
Host applications know which commands are available in a conversation. The shared desktop/mobile inputs now display that catalog while typing a leading `/`, without coupling the SDK to a particular bot or executing commands from the picker.
|
|
299
|
+
|
|
300
|
+
```tsx
|
|
301
|
+
<ChatInputDesktop commands={{ commands: [
|
|
302
|
+
{ name: "review", description: "Review code", usage: "PR URL or branch" },
|
|
303
|
+
{ name: "qa", description: "Run independent tests", usage: "PR URL" },
|
|
304
|
+
] }} mentions={groupMembers} onSend={sendMessage} />
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
The same prop is supported by `ChatInputMobile`. Names omit the leading slash. Arrow keys navigate, Enter/Tab inserts a command and a space, Escape closes; touch selection edits the draft only. Command arguments and existing member mentions are preserved. IME composition, URLs and slashes in ordinary body text do not trigger command execution. Omit `commands` for unsupported conversations.
|
|
308
|
+
|
|
309
|
+
Host-owned textareas can use `useComposerSuggestions({ value, onChange, mentions, commands, disabled })`, spread `inputProps`, render `MentionMenu` with `menuProps` and `CommandMenu` with `commandMenuProps`, and send `getMentions()` with the untrimmed value.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Chat } from "./chatContract.js";
|
|
2
|
+
import type { CreateChatOptions } from "./runtime/createChat.js";
|
|
3
|
+
/** Host-owned transport integration. The default factory never accepts raw transports. */
|
|
4
|
+
export declare function createChatWithTransport(options: CreateChatOptions): Chat;
|
|
5
|
+
/** Explicit migration/diagnostic access, outside the ordinary client. */
|
|
6
|
+
export declare function getChatRuntime(chat: Chat): import("./runtime/chatSession.js").ChatSession<import("./runtime/cachedMessage.js").CachedChatMessage>;
|
|
7
|
+
export declare function handleChatReach(chat: Chat, event: Parameters<ReturnType<typeof getChatRuntime>["handleReach"]>[0]): Promise<"handled" | undefined>;
|
|
8
|
+
export declare function revokeChatAccess(chat: Chat): Promise<void>;
|
|
9
|
+
/** @deprecated Transitional next.68 adapter. New App code must use the public client. */
|
|
10
|
+
export declare function getLegacyChat(chat: Chat): import("./internal/chatEngine.js").ChatEngine;
|
package/lib/advanced.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createPublicChat } from "./internal/createPublicChat.js";
|
|
2
|
+
import { chatEngine } from "./internal/chatRegistry.js";
|
|
3
|
+
/** Host-owned transport integration. The default factory never accepts raw transports. */
|
|
4
|
+
export function createChatWithTransport(options) { return createPublicChat(options); }
|
|
5
|
+
/** Explicit migration/diagnostic access, outside the ordinary client. */
|
|
6
|
+
export function getChatRuntime(chat) { return chatEngine(chat).advanced; }
|
|
7
|
+
export function handleChatReach(chat, event) { return chatEngine(chat).handleReach(event); }
|
|
8
|
+
export function revokeChatAccess(chat) { return chatEngine(chat).revokeAccess(); }
|
|
9
|
+
/** @deprecated Transitional next.68 adapter. New App code must use the public client. */
|
|
10
|
+
export function getLegacyChat(chat) { return chatEngine(chat); }
|
package/lib/chat.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Chat, CreateChatOptions } from "./chatContract.js";
|
|
2
|
+
export type * from "./chatContract.js";
|
|
3
|
+
export type * from "./chatTypes.js";
|
|
4
|
+
export { ChatError, type ChatErrorCode } from "./chatError.js";
|
|
5
|
+
export declare function createChat(options: CreateChatOptions): Chat;
|
package/lib/chat.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createPublicChat } from "./internal/createPublicChat.js";
|
|
2
|
+
import { ChatError } from "./chatError.js";
|
|
3
|
+
export { ChatError } from "./chatError.js";
|
|
4
|
+
export function createChat(options) {
|
|
5
|
+
const allowed = ["appId", "accountId", "profileId", "teamId", "threadId", "endpoint", "uploadEndpoint", "clientInstanceId", "fetch", "onError", "token", "getToken"];
|
|
6
|
+
if (Object.keys(options).some(key => !allowed.includes(key)))
|
|
7
|
+
throw new ChatError("invalid-input", "Unsupported chat option; transport integration belongs to /advanced");
|
|
8
|
+
if (options.token !== undefined && options.getToken !== undefined)
|
|
9
|
+
throw new ChatError("invalid-input", "Select one credential source");
|
|
10
|
+
return createPublicChat({ ...options, onError: error => options.onError?.(error) });
|
|
11
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import type { ChatFile } from "./fileMetadata.js";
|
|
2
|
+
import type { ChatError } from "./chatError.js";
|
|
3
|
+
import type { ChatMention } from "./mentions/model.js";
|
|
4
|
+
import type { TokenProvider } from "./credentials.js";
|
|
5
|
+
import type { ChatGroup, ChatSnapshot, ChatThreadSnapshot, ReadOptions } from "./chatTypes.js";
|
|
6
|
+
export type { TokenProvider, TokenRequest } from "./credentials.js";
|
|
7
|
+
export type ConversationTarget = {
|
|
8
|
+
peerId: string;
|
|
9
|
+
groupId?: never;
|
|
10
|
+
} | {
|
|
11
|
+
groupId: string;
|
|
12
|
+
peerId?: never;
|
|
13
|
+
};
|
|
14
|
+
export type CreateChatOptions = {
|
|
15
|
+
appId: string;
|
|
16
|
+
accountId: string;
|
|
17
|
+
profileId: string;
|
|
18
|
+
teamId: string;
|
|
19
|
+
threadId?: string;
|
|
20
|
+
endpoint?: string;
|
|
21
|
+
uploadEndpoint?: string;
|
|
22
|
+
clientInstanceId?: string;
|
|
23
|
+
fetch?: typeof fetch;
|
|
24
|
+
onError?: (error: ChatError) => void;
|
|
25
|
+
} & ({
|
|
26
|
+
token: string;
|
|
27
|
+
getToken?: never;
|
|
28
|
+
} | {
|
|
29
|
+
getToken: TokenProvider;
|
|
30
|
+
token?: never;
|
|
31
|
+
});
|
|
32
|
+
export type SendOptions = {
|
|
33
|
+
waitFor?: "accepted" | "delivered";
|
|
34
|
+
};
|
|
35
|
+
export type SentMessage = Readonly<{
|
|
36
|
+
id: string;
|
|
37
|
+
timestamp: number;
|
|
38
|
+
}>;
|
|
39
|
+
export type ReadersQuery = {
|
|
40
|
+
afterProfileId?: string;
|
|
41
|
+
revision?: number;
|
|
42
|
+
};
|
|
43
|
+
export type ReadersPage = Readonly<{
|
|
44
|
+
appId: string;
|
|
45
|
+
threadId: string;
|
|
46
|
+
messageId: string;
|
|
47
|
+
revision: number;
|
|
48
|
+
total: number;
|
|
49
|
+
profileIds: string[];
|
|
50
|
+
nextAfter: string | null;
|
|
51
|
+
resetRequired: boolean;
|
|
52
|
+
}>;
|
|
53
|
+
export type GroupChanges = {
|
|
54
|
+
expectedRevision?: string;
|
|
55
|
+
addMembers?: readonly string[];
|
|
56
|
+
removeMembers?: readonly string[];
|
|
57
|
+
title?: string;
|
|
58
|
+
avatar?: string;
|
|
59
|
+
};
|
|
60
|
+
export type VoiceAttachment = Readonly<{
|
|
61
|
+
url: string;
|
|
62
|
+
release: () => void;
|
|
63
|
+
}>;
|
|
64
|
+
export type SubscribeOptions<T> = {
|
|
65
|
+
fields?: readonly (keyof T)[];
|
|
66
|
+
};
|
|
67
|
+
type ContentChoices = {
|
|
68
|
+
text: {
|
|
69
|
+
text: string;
|
|
70
|
+
mentions?: ChatMention[];
|
|
71
|
+
quote?: {
|
|
72
|
+
messageId?: string;
|
|
73
|
+
senderName: string;
|
|
74
|
+
content: string;
|
|
75
|
+
};
|
|
76
|
+
};
|
|
77
|
+
file: {
|
|
78
|
+
file: ChatFile;
|
|
79
|
+
};
|
|
80
|
+
image: {
|
|
81
|
+
image: {
|
|
82
|
+
url: string;
|
|
83
|
+
name?: string;
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
voice: {
|
|
87
|
+
voice: ({
|
|
88
|
+
blob: Blob;
|
|
89
|
+
url?: never;
|
|
90
|
+
} | {
|
|
91
|
+
url: string;
|
|
92
|
+
blob?: never;
|
|
93
|
+
}) & {
|
|
94
|
+
durationMs: number;
|
|
95
|
+
transcript?: string;
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
recall: {
|
|
99
|
+
recall: {
|
|
100
|
+
messageId: string;
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
reaction: {
|
|
104
|
+
reaction: {
|
|
105
|
+
messageId: string;
|
|
106
|
+
emojiId: string;
|
|
107
|
+
active: boolean;
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
invitation: {
|
|
111
|
+
invitation: {
|
|
112
|
+
message?: string;
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
export type OutgoingContent = {
|
|
117
|
+
[K in keyof ContentChoices]: ContentChoices[K] & {
|
|
118
|
+
[P in Exclude<keyof ContentChoices, K>]?: never;
|
|
119
|
+
};
|
|
120
|
+
}[keyof ContentChoices];
|
|
121
|
+
/** Default public client. Protocol and synchronous store internals are separate entry points. */
|
|
122
|
+
export interface Chat {
|
|
123
|
+
start(): Promise<void>;
|
|
124
|
+
close(): Promise<void>;
|
|
125
|
+
resolveThreadId(target: ConversationTarget, options?: ReadOptions): Promise<string>;
|
|
126
|
+
getThread(threadId: string, options?: ReadOptions): Promise<ChatThreadSnapshot>;
|
|
127
|
+
loadEarlier(threadId: string, options?: ReadOptions): Promise<ChatThreadSnapshot>;
|
|
128
|
+
refreshThread(threadId: string, options?: ReadOptions): Promise<ChatThreadSnapshot>;
|
|
129
|
+
subscribeThread(threadId: string, listener: (snapshot: ChatThreadSnapshot) => void, options?: SubscribeOptions<ChatThreadSnapshot>): () => void;
|
|
130
|
+
subscribeChat(listener: (snapshot: ChatSnapshot) => void, options?: SubscribeOptions<ChatSnapshot>): () => void;
|
|
131
|
+
sendMessage(threadId: string, content: OutgoingContent, options?: SendOptions): Promise<SentMessage>;
|
|
132
|
+
retryMessage(messageId: string): Promise<SentMessage>;
|
|
133
|
+
observeMessages(threadId: string, ids: string[], event: "visible" | "played"): Promise<void>;
|
|
134
|
+
markRead(threadId: string): Promise<void>;
|
|
135
|
+
getReaders(threadId: string, messageId: string, options?: ReadersQuery & ReadOptions): Promise<ReadersPage>;
|
|
136
|
+
watchReaders(threadId: string | undefined, ids: string[], receiver?: string): Promise<void>;
|
|
137
|
+
getVoiceAttachment(threadId: string, messageId: string, options?: ReadOptions): Promise<VoiceAttachment>;
|
|
138
|
+
getGroup(threadId: string, options?: ReadOptions): Promise<ChatGroup>;
|
|
139
|
+
refreshGroup(threadId: string, options?: ReadOptions): Promise<ChatGroup>;
|
|
140
|
+
createGroup(input: {
|
|
141
|
+
groupId?: string;
|
|
142
|
+
title: string;
|
|
143
|
+
members: readonly string[];
|
|
144
|
+
}): Promise<ChatGroup & {
|
|
145
|
+
threadId: string;
|
|
146
|
+
}>;
|
|
147
|
+
updateGroup(threadId: string, changes: GroupChanges): Promise<ChatGroup>;
|
|
148
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type ChatErrorCode = "unavailable" | "access-denied" | "authentication-required" | "storage-unavailable" | "invalid-input" | "conflict" | "closed" | "busy";
|
|
2
|
+
/** Stable recovery categories. The original failure remains available as cause. */
|
|
3
|
+
export declare class ChatError extends Error {
|
|
4
|
+
readonly code: ChatErrorCode;
|
|
5
|
+
readonly retryable: boolean;
|
|
6
|
+
readonly status?: number;
|
|
7
|
+
constructor(code: ChatErrorCode, message: string, options?: {
|
|
8
|
+
cause?: unknown;
|
|
9
|
+
status?: number;
|
|
10
|
+
});
|
|
11
|
+
}
|
package/lib/chatError.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Stable recovery categories. The original failure remains available as cause. */
|
|
2
|
+
export class ChatError extends Error {
|
|
3
|
+
code;
|
|
4
|
+
retryable;
|
|
5
|
+
status;
|
|
6
|
+
constructor(code, message, options = {}) {
|
|
7
|
+
super(message, { cause: options.cause });
|
|
8
|
+
this.code = code;
|
|
9
|
+
this.name = "ChatError";
|
|
10
|
+
this.status = options.status;
|
|
11
|
+
this.retryable = ["unavailable", "storage-unavailable", "busy"].includes(code);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { ChatFile } from "./fileMetadata.js";
|
|
2
|
+
export type { ChatFile } from "./fileMetadata.js";
|
|
3
|
+
import type { ChatError } from "./chatError.js";
|
|
4
|
+
import type { ChatMention } from "./mentions/model.js";
|
|
5
|
+
/** Stable product model. No storage, transport, Session or projection types. */
|
|
6
|
+
export type ChatMessage = Readonly<{
|
|
7
|
+
id: string;
|
|
8
|
+
threadId: string;
|
|
9
|
+
timestamp: number;
|
|
10
|
+
from?: string;
|
|
11
|
+
mine: boolean;
|
|
12
|
+
status: "sending" | "sent" | "failed" | "read";
|
|
13
|
+
recalled: boolean;
|
|
14
|
+
unread?: boolean;
|
|
15
|
+
attentionEligible?: boolean;
|
|
16
|
+
reading?: Readonly<{
|
|
17
|
+
seq?: number;
|
|
18
|
+
read: boolean;
|
|
19
|
+
readPolicy: "visible" | "explicit";
|
|
20
|
+
}>;
|
|
21
|
+
reactions: readonly Readonly<{
|
|
22
|
+
emojiId: string;
|
|
23
|
+
userIds: string[];
|
|
24
|
+
mine: boolean;
|
|
25
|
+
}>[];
|
|
26
|
+
kind: "text" | "image" | "voice" | "emoji" | "quote" | "invitation" | "system" | "attachment";
|
|
27
|
+
text: string;
|
|
28
|
+
senderName?: string;
|
|
29
|
+
senderAvatar?: string;
|
|
30
|
+
timeLabel?: string;
|
|
31
|
+
file?: ChatFile;
|
|
32
|
+
image?: Readonly<{
|
|
33
|
+
url: string;
|
|
34
|
+
name?: string;
|
|
35
|
+
}>;
|
|
36
|
+
voice?: Readonly<{
|
|
37
|
+
url: string;
|
|
38
|
+
durationMs: number;
|
|
39
|
+
transcript?: string;
|
|
40
|
+
}>;
|
|
41
|
+
quote?: Readonly<{
|
|
42
|
+
messageId?: string;
|
|
43
|
+
senderName: string;
|
|
44
|
+
content: string;
|
|
45
|
+
recalled?: boolean;
|
|
46
|
+
}>;
|
|
47
|
+
attachment?: Readonly<{
|
|
48
|
+
name?: string;
|
|
49
|
+
size?: string;
|
|
50
|
+
}>;
|
|
51
|
+
richContent?: unknown;
|
|
52
|
+
mentions?: readonly ChatMention[];
|
|
53
|
+
recipientCount?: number;
|
|
54
|
+
system?: Readonly<{
|
|
55
|
+
type: "group-created" | "group-updated" | "contact-updated";
|
|
56
|
+
title?: string;
|
|
57
|
+
}>;
|
|
58
|
+
}>;
|
|
59
|
+
export type ChatGroup = Readonly<{
|
|
60
|
+
groupId: string;
|
|
61
|
+
teamId: string;
|
|
62
|
+
ownerId: string;
|
|
63
|
+
title: string;
|
|
64
|
+
avatar?: string;
|
|
65
|
+
memberIds: readonly string[];
|
|
66
|
+
revision: string;
|
|
67
|
+
}>;
|
|
68
|
+
export type ConversationInfo = Readonly<{
|
|
69
|
+
kind: "dm" | "group";
|
|
70
|
+
targetId: string;
|
|
71
|
+
}>;
|
|
72
|
+
export type UnreadState = Readonly<{
|
|
73
|
+
unreadMessageCount: number;
|
|
74
|
+
unreadMentionCount: number;
|
|
75
|
+
badgeCount: number;
|
|
76
|
+
}>;
|
|
77
|
+
export type ReadCounts = Readonly<{
|
|
78
|
+
appId: string;
|
|
79
|
+
threadId: string;
|
|
80
|
+
revision: number;
|
|
81
|
+
counts: Record<string, number>;
|
|
82
|
+
}>;
|
|
83
|
+
type ThreadFields = Readonly<{
|
|
84
|
+
threadId: string;
|
|
85
|
+
messages: readonly ChatMessage[];
|
|
86
|
+
hasEarlier: boolean;
|
|
87
|
+
conversation?: ConversationInfo;
|
|
88
|
+
unread: UnreadState | undefined;
|
|
89
|
+
group: ChatGroup | undefined;
|
|
90
|
+
readCounts: ReadCounts | undefined;
|
|
91
|
+
}>;
|
|
92
|
+
/** A refresh failure retains ready data; only an initial failure has status error. */
|
|
93
|
+
export type ChatThreadSnapshot = ThreadFields & (Readonly<{
|
|
94
|
+
status: "pending";
|
|
95
|
+
ready: false;
|
|
96
|
+
error?: undefined;
|
|
97
|
+
}> | Readonly<{
|
|
98
|
+
status: "ready";
|
|
99
|
+
ready: true;
|
|
100
|
+
error?: ChatError;
|
|
101
|
+
}> | Readonly<{
|
|
102
|
+
status: "error";
|
|
103
|
+
ready: false;
|
|
104
|
+
error: ChatError;
|
|
105
|
+
}>);
|
|
106
|
+
export type ChatConnectionState = "connecting" | "reconnecting" | "authenticating" | "connected" | "authentication_required" | "closed";
|
|
107
|
+
export type ChatSnapshot = Readonly<{
|
|
108
|
+
threads: readonly {
|
|
109
|
+
threadId: string;
|
|
110
|
+
lastMessage?: ChatMessage;
|
|
111
|
+
group: ChatGroup | undefined;
|
|
112
|
+
unread: UnreadState | undefined;
|
|
113
|
+
outgoingCount: number;
|
|
114
|
+
}[];
|
|
115
|
+
unreadMessageCount: number | undefined;
|
|
116
|
+
badgeCount: number | undefined;
|
|
117
|
+
connection: ChatConnectionState;
|
|
118
|
+
}>;
|
|
119
|
+
export type ReadOptions = {
|
|
120
|
+
signal?: AbortSignal;
|
|
121
|
+
};
|
package/lib/chatTypes.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|