@dtelecom/secure-chat-client 0.1.0
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/LICENSE +202 -0
- package/README.md +134 -0
- package/dist/index.cjs +2189 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +435 -0
- package/dist/index.d.ts +435 -0
- package/dist/index.js +2162 -0
- package/dist/index.js.map +1 -0
- package/package.json +82 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,435 @@
|
|
|
1
|
+
interface MintTokenResponse {
|
|
2
|
+
chatToken: string;
|
|
3
|
+
expiresAt: number;
|
|
4
|
+
/**
|
|
5
|
+
* Closest dtelecom node WebSocket URL for this client's IP, computed
|
|
6
|
+
* server-side via the existing @dtelecom/server-sdk-js node discovery
|
|
7
|
+
* (Solana registry → /relevants ranking). The SDK uses this for /chat/ws.
|
|
8
|
+
*/
|
|
9
|
+
chatNodeWsUrl: string;
|
|
10
|
+
}
|
|
11
|
+
interface OneTimeKey {
|
|
12
|
+
id: string;
|
|
13
|
+
public: string;
|
|
14
|
+
}
|
|
15
|
+
interface ClaimedDevice {
|
|
16
|
+
deviceId: string;
|
|
17
|
+
identityKeyCurve: string;
|
|
18
|
+
identityKeyEd: string;
|
|
19
|
+
signedPrekey: string;
|
|
20
|
+
signedPrekeySig: string;
|
|
21
|
+
oneTimeKey: OneTimeKey | null;
|
|
22
|
+
fallbackPrekey: string;
|
|
23
|
+
fallbackPrekeySig: string;
|
|
24
|
+
fingerprint: string;
|
|
25
|
+
lastActiveAt: number;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Public material the SDK uploads via POST /api/chat/keys/upload. */
|
|
29
|
+
interface UploadBundle {
|
|
30
|
+
identityKeyCurve: string;
|
|
31
|
+
identityKeyEd: string;
|
|
32
|
+
signedPrekey: string;
|
|
33
|
+
signedPrekeySig: string;
|
|
34
|
+
fallbackPrekey: string;
|
|
35
|
+
fallbackPrekeySig: string;
|
|
36
|
+
fingerprint: string;
|
|
37
|
+
oneTimeKeys: {
|
|
38
|
+
id: string;
|
|
39
|
+
public: string;
|
|
40
|
+
}[];
|
|
41
|
+
}
|
|
42
|
+
/** Output of CryptoAdapter.encryptForPeer. */
|
|
43
|
+
interface OutboundEnvelope {
|
|
44
|
+
/** base64-encoded Olm ciphertext. */
|
|
45
|
+
ciphertext: string;
|
|
46
|
+
/**
|
|
47
|
+
* "prekey" if this is the first message in a fresh outbound session
|
|
48
|
+
* (consumes one of the recipient's one-time-keys); "normal" once the
|
|
49
|
+
* session has ratcheted at least once.
|
|
50
|
+
*/
|
|
51
|
+
msgType: "prekey" | "normal";
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Olm primitives surface. Methods are async because the underlying WASM
|
|
55
|
+
* library is async-loaded and persistence is async (IndexedDB).
|
|
56
|
+
*
|
|
57
|
+
* The adapter owns Olm account + per-(peerUser, peerDevice) session state
|
|
58
|
+
* and persists across restarts. Callers (sessions.ts, key_bundle.ts) are
|
|
59
|
+
* stateless wrappers that orchestrate via this interface.
|
|
60
|
+
*/
|
|
61
|
+
interface CryptoAdapter {
|
|
62
|
+
/** Initialize underlying WASM library. Idempotent. */
|
|
63
|
+
init(): Promise<void>;
|
|
64
|
+
/** True if this adapter has a persisted Olm account. */
|
|
65
|
+
hasAccount(): Promise<boolean>;
|
|
66
|
+
/**
|
|
67
|
+
* Generate a new Olm account and return the public bundle to upload.
|
|
68
|
+
* Persists private state. Throws if an account already exists — call
|
|
69
|
+
* hasAccount() first.
|
|
70
|
+
*/
|
|
71
|
+
generateAccount(otkCount: number): Promise<UploadBundle>;
|
|
72
|
+
/**
|
|
73
|
+
* Re-emit the current bundle without generating new keys. Used when
|
|
74
|
+
* the device id is already known but the server doesn't yet have the
|
|
75
|
+
* bundle (e.g. backend was wiped during testing).
|
|
76
|
+
*/
|
|
77
|
+
getCurrentBundle(): Promise<UploadBundle>;
|
|
78
|
+
/** Generate N more one-time keys for top-up. Returns public material. */
|
|
79
|
+
generateOneTimeKeys(n: number): Promise<{
|
|
80
|
+
id: string;
|
|
81
|
+
public: string;
|
|
82
|
+
}[]>;
|
|
83
|
+
/**
|
|
84
|
+
* Locally tracked count of unconsumed one-time keys. The server's count
|
|
85
|
+
* is authoritative; this is the SDK's view (decrements on every
|
|
86
|
+
* outbound prekey-message it knows about, but the server may consume
|
|
87
|
+
* OTKs without notifying the SDK).
|
|
88
|
+
*/
|
|
89
|
+
unusedOneTimeKeyCount(): Promise<number>;
|
|
90
|
+
/**
|
|
91
|
+
* Encrypt for a peer device. Creates an outbound Olm session lazily
|
|
92
|
+
* from peerBundle if no session exists with (peerUserId, peerDeviceId);
|
|
93
|
+
* subsequent calls reuse the established session.
|
|
94
|
+
*/
|
|
95
|
+
encryptForPeer(peerUserId: string, peerDeviceId: string, peerBundle: ClaimedDevice, plaintext: string): Promise<OutboundEnvelope>;
|
|
96
|
+
/**
|
|
97
|
+
* Decrypt an inbound ciphertext. If `msgType === "prekey"` and no
|
|
98
|
+
* session exists yet for (peerUserId, peerDeviceId), creates an
|
|
99
|
+
* inbound session from the prekey message.
|
|
100
|
+
*/
|
|
101
|
+
decryptFromPeer(peerUserId: string, peerDeviceId: string, ciphertext: string, msgType: "prekey" | "normal"): Promise<string>;
|
|
102
|
+
/** Drop the session with a peer device. Used for explicit reset. */
|
|
103
|
+
forgetSession(peerUserId: string, peerDeviceId: string): Promise<void>;
|
|
104
|
+
/** Whether a session exists with this peer device. */
|
|
105
|
+
hasSession(peerUserId: string, peerDeviceId: string): Promise<boolean>;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
interface KVStore {
|
|
109
|
+
getString(key: string): Promise<string | null>;
|
|
110
|
+
setString(key: string, value: string): Promise<void>;
|
|
111
|
+
getBytes(key: string): Promise<Uint8Array | null>;
|
|
112
|
+
setBytes(key: string, value: Uint8Array): Promise<void>;
|
|
113
|
+
delete(key: string): Promise<void>;
|
|
114
|
+
/** List keys with the given prefix. Used to enumerate per-peer-device sessions. */
|
|
115
|
+
listKeys(prefix: string): Promise<string[]>;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Persisted shape of a message. `senderUserId` is the truth — the SDK
|
|
120
|
+
* sets it to either "self" (when the local user sent it) or to the
|
|
121
|
+
* peer's user id (on inbound). Edit/delete authorization compares this
|
|
122
|
+
* field on the stored row to the sender of the inbound mutation event.
|
|
123
|
+
*/
|
|
124
|
+
interface StoredMessage {
|
|
125
|
+
id: string;
|
|
126
|
+
peerUserId: string;
|
|
127
|
+
senderUserId: string;
|
|
128
|
+
text: string;
|
|
129
|
+
/** Original clientSentAt of the text event; never mutated by edits. */
|
|
130
|
+
sentAt: number;
|
|
131
|
+
/** Set on edit; null otherwise. */
|
|
132
|
+
editedAt: number | null;
|
|
133
|
+
/** Set on delete; the message becomes a tombstone. */
|
|
134
|
+
deletedAt: number | null;
|
|
135
|
+
replyTo?: string;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
type MessageStatus = "pending" | "sent" | "delivered" | "deliveredAll" | "read";
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Consumer-supplied callback that mints a chat token via the tenant backend.
|
|
142
|
+
* Must return the full `POST /api/chat/token` response so the SDK can use the
|
|
143
|
+
* server-discovered node URL (chatNodeWsUrl) — no Solana code on the client.
|
|
144
|
+
*/
|
|
145
|
+
type FetchChatToken = (deviceId: string) => Promise<MintTokenResponse>;
|
|
146
|
+
|
|
147
|
+
interface OlmAdapterOptions {
|
|
148
|
+
store: KVStore;
|
|
149
|
+
}
|
|
150
|
+
declare class OlmCryptoAdapter implements CryptoAdapter {
|
|
151
|
+
private opts;
|
|
152
|
+
private account;
|
|
153
|
+
private sessions;
|
|
154
|
+
constructor(opts: OlmAdapterOptions);
|
|
155
|
+
init(): Promise<void>;
|
|
156
|
+
hasAccount(): Promise<boolean>;
|
|
157
|
+
generateAccount(otkCount: number): Promise<UploadBundle>;
|
|
158
|
+
getCurrentBundle(): Promise<UploadBundle>;
|
|
159
|
+
generateOneTimeKeys(n: number): Promise<{
|
|
160
|
+
id: string;
|
|
161
|
+
public: string;
|
|
162
|
+
}[]>;
|
|
163
|
+
unusedOneTimeKeyCount(): Promise<number>;
|
|
164
|
+
encryptForPeer(peerUserId: string, peerDeviceId: string, peerBundle: ClaimedDevice, plaintext: string): Promise<OutboundEnvelope>;
|
|
165
|
+
decryptFromPeer(peerUserId: string, peerDeviceId: string, ciphertext: string, msgType: "prekey" | "normal"): Promise<string>;
|
|
166
|
+
forgetSession(peerUserId: string, peerDeviceId: string): Promise<void>;
|
|
167
|
+
hasSession(peerUserId: string, peerDeviceId: string): Promise<boolean>;
|
|
168
|
+
private requireAccount;
|
|
169
|
+
private buildBundle;
|
|
170
|
+
private persistAccount;
|
|
171
|
+
private loadSession;
|
|
172
|
+
private persistSession;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
declare class FakeCryptoAdapter implements CryptoAdapter {
|
|
176
|
+
private account;
|
|
177
|
+
private sessions;
|
|
178
|
+
private otkCounter;
|
|
179
|
+
init(): Promise<void>;
|
|
180
|
+
hasAccount(): Promise<boolean>;
|
|
181
|
+
generateAccount(otkCount: number): Promise<UploadBundle>;
|
|
182
|
+
getCurrentBundle(): Promise<UploadBundle>;
|
|
183
|
+
generateOneTimeKeys(n: number): Promise<{
|
|
184
|
+
id: string;
|
|
185
|
+
public: string;
|
|
186
|
+
}[]>;
|
|
187
|
+
unusedOneTimeKeyCount(): Promise<number>;
|
|
188
|
+
encryptForPeer(peerUserId: string, peerDeviceId: string, _peerBundle: ClaimedDevice, plaintext: string): Promise<OutboundEnvelope>;
|
|
189
|
+
decryptFromPeer(peerUserId: string, peerDeviceId: string, ciphertext: string, msgType: "prekey" | "normal"): Promise<string>;
|
|
190
|
+
forgetSession(peerUserId: string, peerDeviceId: string): Promise<void>;
|
|
191
|
+
hasSession(peerUserId: string, peerDeviceId: string): Promise<boolean>;
|
|
192
|
+
private snapshot;
|
|
193
|
+
private makeOtks;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
declare class MemoryKVStore implements KVStore {
|
|
197
|
+
private map;
|
|
198
|
+
getString(key: string): Promise<string | null>;
|
|
199
|
+
setString(key: string, value: string): Promise<void>;
|
|
200
|
+
getBytes(key: string): Promise<Uint8Array | null>;
|
|
201
|
+
setBytes(key: string, value: Uint8Array): Promise<void>;
|
|
202
|
+
delete(key: string): Promise<void>;
|
|
203
|
+
listKeys(prefix: string): Promise<string[]>;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
declare class WebKVStore implements KVStore {
|
|
207
|
+
getString(key: string): Promise<string | null>;
|
|
208
|
+
setString(key: string, value: string): Promise<void>;
|
|
209
|
+
getBytes(key: string): Promise<Uint8Array | null>;
|
|
210
|
+
setBytes(key: string, value: Uint8Array): Promise<void>;
|
|
211
|
+
delete(key: string): Promise<void>;
|
|
212
|
+
listKeys(prefix: string): Promise<string[]>;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
declare const VERSION = "0.0.0";
|
|
216
|
+
declare const CONTENT_PROTOCOL_VERSION = 1;
|
|
217
|
+
interface ConnectOptions {
|
|
218
|
+
/** dmeet-backend (or mock) base URL — e.g. https://dmeet.example.com */
|
|
219
|
+
apiBaseURL: string;
|
|
220
|
+
/** Function that mints a chat token. The SDK calls this whenever a fresh
|
|
221
|
+
* token is needed; the consumer's implementation handles their own
|
|
222
|
+
* outer auth (Privy, an internal cookie, etc.). */
|
|
223
|
+
fetchChatToken: FetchChatToken;
|
|
224
|
+
/** Optional. Defaults to WebKVStore (IndexedDB) in browsers. Tests pass
|
|
225
|
+
* MemoryKVStore to avoid the IDB dependency. */
|
|
226
|
+
store?: KVStore;
|
|
227
|
+
/** Optional. Defaults to OlmCryptoAdapter wrapping vodozemac. Tests
|
|
228
|
+
* pass FakeCryptoAdapter to avoid bundling WASM in the test runner. */
|
|
229
|
+
crypto?: CryptoAdapter;
|
|
230
|
+
/** Optional fetch implementation. Defaults to globalThis.fetch. Tests
|
|
231
|
+
* pass a mocked fetch to avoid real network calls. */
|
|
232
|
+
fetchImpl?: typeof fetch;
|
|
233
|
+
}
|
|
234
|
+
interface MessageReceived {
|
|
235
|
+
peerUserId: string;
|
|
236
|
+
peerDeviceId: string;
|
|
237
|
+
/** The user that authored this message. Equals selfUserId when the
|
|
238
|
+
* message arrived via self-echo from another own device. */
|
|
239
|
+
senderUserId: string;
|
|
240
|
+
message: {
|
|
241
|
+
id: string;
|
|
242
|
+
text: string;
|
|
243
|
+
replyTo?: string;
|
|
244
|
+
sentAt: number;
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
interface MessageEdited {
|
|
248
|
+
peerUserId: string;
|
|
249
|
+
/** Author of the edit. Equals selfUserId for self-echoed edits. */
|
|
250
|
+
editorUserId: string;
|
|
251
|
+
targetId: string;
|
|
252
|
+
newText: string;
|
|
253
|
+
editedAt: number;
|
|
254
|
+
}
|
|
255
|
+
interface MessageDeleted {
|
|
256
|
+
peerUserId: string;
|
|
257
|
+
/** Author of the delete. Equals selfUserId for self-echoed deletes. */
|
|
258
|
+
deleterUserId: string;
|
|
259
|
+
targetId: string;
|
|
260
|
+
deletedAt: number;
|
|
261
|
+
}
|
|
262
|
+
interface ReadReceiptEvent {
|
|
263
|
+
peerUserId: string;
|
|
264
|
+
peerDeviceId: string;
|
|
265
|
+
upToId: string;
|
|
266
|
+
}
|
|
267
|
+
interface TypingEvt {
|
|
268
|
+
peerUserId: string;
|
|
269
|
+
peerDeviceId: string;
|
|
270
|
+
state: "started" | "stopped";
|
|
271
|
+
}
|
|
272
|
+
interface StatusChangeEvt {
|
|
273
|
+
peerUserId: string;
|
|
274
|
+
messageId: string;
|
|
275
|
+
status: MessageStatus;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Emitted the first time the SDK observes a previously-unknown peer device,
|
|
279
|
+
* either via an inbound prekey-message from it OR via a refreshed device
|
|
280
|
+
* list. Apps render this as the "Bob is using a new device — verify?"
|
|
281
|
+
* banner per plan §17.
|
|
282
|
+
*/
|
|
283
|
+
interface PeerNewDeviceEvt {
|
|
284
|
+
peerUserId: string;
|
|
285
|
+
peerDeviceId: string;
|
|
286
|
+
fingerprint: string;
|
|
287
|
+
}
|
|
288
|
+
interface EventMap {
|
|
289
|
+
message: MessageReceived;
|
|
290
|
+
messageEdited: MessageEdited;
|
|
291
|
+
messageDeleted: MessageDeleted;
|
|
292
|
+
readReceipt: ReadReceiptEvent;
|
|
293
|
+
typing: TypingEvt;
|
|
294
|
+
statusChange: StatusChangeEvt;
|
|
295
|
+
peerNewDevice: PeerNewDeviceEvt;
|
|
296
|
+
}
|
|
297
|
+
type EventName = keyof EventMap;
|
|
298
|
+
type Listener<T extends EventName> = (event: EventMap[T]) => void;
|
|
299
|
+
/** Public shape returned by `getKnownPeerDevices()`. */
|
|
300
|
+
interface KnownPeerDevice {
|
|
301
|
+
deviceId: string;
|
|
302
|
+
fingerprint: string;
|
|
303
|
+
lastActiveAt: number;
|
|
304
|
+
/** True if the local user has explicitly verified this device. */
|
|
305
|
+
verified: boolean;
|
|
306
|
+
}
|
|
307
|
+
declare class DTelecomSecureChat {
|
|
308
|
+
private deviceId;
|
|
309
|
+
private http;
|
|
310
|
+
private ws;
|
|
311
|
+
private crypto;
|
|
312
|
+
private store;
|
|
313
|
+
private keyBundle;
|
|
314
|
+
private sessions;
|
|
315
|
+
private peerDevices;
|
|
316
|
+
private messages;
|
|
317
|
+
private status;
|
|
318
|
+
private outbox;
|
|
319
|
+
private typingMgr;
|
|
320
|
+
private listeners;
|
|
321
|
+
/** Per-peer-device queue of received-event ids awaiting batch send. */
|
|
322
|
+
private pendingReceived;
|
|
323
|
+
private receivedFlushTimer;
|
|
324
|
+
/** Self user id derived from chat-token claims after first mint. */
|
|
325
|
+
private selfUserId;
|
|
326
|
+
/** Devices we've already emitted `peerNewDevice` for, to avoid duplicates. */
|
|
327
|
+
private announcedNewDevices;
|
|
328
|
+
/** True after we've reuploaded the bundle once for this connection — set
|
|
329
|
+
* after a "peer has zero devices" outcome that suggests the backend
|
|
330
|
+
* forgot us (registry mismatch / wipe). Don't loop. */
|
|
331
|
+
private bundleReuploadAttempted;
|
|
332
|
+
/** Cache of the read-receipts preference (loaded lazily). */
|
|
333
|
+
private readReceiptsCache;
|
|
334
|
+
/**
|
|
335
|
+
* Connect to the dtelecom mesh. Generates an Olm account on first run,
|
|
336
|
+
* uploads the bundle, opens /chat/ws to the closest discovered node,
|
|
337
|
+
* and pulls any pending offline envelopes.
|
|
338
|
+
*/
|
|
339
|
+
static connect(opts: ConnectOptions): Promise<DTelecomSecureChat>;
|
|
340
|
+
private constructor();
|
|
341
|
+
/** Stable per-install device id. Useful for app diagnostics. */
|
|
342
|
+
get currentDeviceId(): string;
|
|
343
|
+
sendText(peerUserId: string, text: string, opts?: {
|
|
344
|
+
replyTo?: string;
|
|
345
|
+
}): Promise<string>;
|
|
346
|
+
editMessage(peerUserId: string, targetId: string, newText: string): Promise<string>;
|
|
347
|
+
deleteMessage(peerUserId: string, targetId: string): Promise<string>;
|
|
348
|
+
/**
|
|
349
|
+
* Send a read-watermark to `peerUserId`. No-op when read receipts are
|
|
350
|
+
* disabled by `setReadReceiptsEnabled(false)` — the local user remains
|
|
351
|
+
* invisible to senders, but inbound `read` events from peers are still
|
|
352
|
+
* consumed (the sender's preference is their own call).
|
|
353
|
+
*/
|
|
354
|
+
markRead(peerUserId: string, upToMessageId: string): Promise<void>;
|
|
355
|
+
setTyping(peerUserId: string, isTyping: boolean): void;
|
|
356
|
+
/** Enable/disable outbound read receipts. Persisted in the local KV store. */
|
|
357
|
+
setReadReceiptsEnabled(enabled: boolean): Promise<void>;
|
|
358
|
+
/** Read the current preference. Default true. */
|
|
359
|
+
areReadReceiptsEnabled(): Promise<boolean>;
|
|
360
|
+
/**
|
|
361
|
+
* Returns the cached peer-device list for `peerUserId`. Refreshes via
|
|
362
|
+
* `list_devices` if the local cache is empty or stale. Used to render
|
|
363
|
+
* the "Known Devices" settings panel. Doesn't consume OTKs.
|
|
364
|
+
*/
|
|
365
|
+
getKnownPeerDevices(peerUserId: string): Promise<KnownPeerDevice[]>;
|
|
366
|
+
/** Single-device fingerprint accessor. Returns null if unknown. */
|
|
367
|
+
getPeerDeviceFingerprint(peerUserId: string, peerDeviceId: string): Promise<string | null>;
|
|
368
|
+
/**
|
|
369
|
+
* Mark a peer device as verified (or unverified). Local-only — doesn't
|
|
370
|
+
* change the protocol's behavior, just exposes a flag the UI can render.
|
|
371
|
+
*/
|
|
372
|
+
markPeerDeviceVerified(peerUserId: string, peerDeviceId: string, verified: boolean): Promise<void>;
|
|
373
|
+
isPeerDeviceVerified(peerUserId: string, peerDeviceId: string): Promise<boolean>;
|
|
374
|
+
/**
|
|
375
|
+
* Read persisted message history with `peerUserId`, oldest→newest within
|
|
376
|
+
* the page. Use `beforeSentAt` + `limit` to paginate older messages.
|
|
377
|
+
* Returns include local-sent messages (sender = self), inbound messages
|
|
378
|
+
* (sender = peer), and tombstoned/edited rows reflecting the latest state.
|
|
379
|
+
*/
|
|
380
|
+
getHistory(peerUserId: string, opts?: {
|
|
381
|
+
limit?: number;
|
|
382
|
+
beforeSentAt?: number;
|
|
383
|
+
}): Promise<StoredMessage[]>;
|
|
384
|
+
blockUser(peerUserId: string): Promise<{
|
|
385
|
+
ok: true;
|
|
386
|
+
}>;
|
|
387
|
+
unblockUser(peerUserId: string): Promise<{
|
|
388
|
+
ok: true;
|
|
389
|
+
}>;
|
|
390
|
+
getBlockedUsers(): Promise<string[]>;
|
|
391
|
+
on<T extends EventName>(event: T, fn: Listener<T>): () => void;
|
|
392
|
+
disconnect(): Promise<void>;
|
|
393
|
+
private bootstrap;
|
|
394
|
+
/**
|
|
395
|
+
* State-listener for the underlying WsClient. On every transition to
|
|
396
|
+
* "open" (initial connect AND auto-reconnect), drain any queued
|
|
397
|
+
* outbound sends and re-pull pending offline envelopes — closes the
|
|
398
|
+
* gap during disconnect.
|
|
399
|
+
*/
|
|
400
|
+
private onWsState;
|
|
401
|
+
/** Set true while drainPending is running to avoid overlapping calls
|
|
402
|
+
* (would otherwise hand the same envelope to two concurrent decrypt
|
|
403
|
+
* invocations — Olm rejects the second). */
|
|
404
|
+
private drainingPending;
|
|
405
|
+
private drainPending;
|
|
406
|
+
private onFrame;
|
|
407
|
+
private handleInboundCiphertext;
|
|
408
|
+
/**
|
|
409
|
+
* If `peerDeviceId` is not in our local cache for `peerUserId`, refresh
|
|
410
|
+
* the peer's device list and emit `peerNewDevice` exactly once. Idempotent
|
|
411
|
+
* across repeated calls — second message from the same new device is a
|
|
412
|
+
* cheap cache hit. Failures (HTTP error fetching the device list) are
|
|
413
|
+
* swallowed; we'll re-attempt on the next inbound from this device.
|
|
414
|
+
*/
|
|
415
|
+
private maybeAnnouncePeerDevice;
|
|
416
|
+
private dispatchInboundEvent;
|
|
417
|
+
/**
|
|
418
|
+
* Multi-device self-echo. Wraps the original event in a `selfEcho`
|
|
419
|
+
* envelope and ships it to our own user (mesh fanout filters our
|
|
420
|
+
* own device). Other devices belonging to the same user receive,
|
|
421
|
+
* unwrap, and persist the event so their local history mirrors this
|
|
422
|
+
* device's. No-op when:
|
|
423
|
+
* - we don't yet know our own user id
|
|
424
|
+
* - the original was addressed to ourselves (avoids loops)
|
|
425
|
+
* - we have no other devices registered (encryptForPeer returns [])
|
|
426
|
+
* Best-effort: failures here don't surface to the caller.
|
|
427
|
+
*/
|
|
428
|
+
private selfEcho;
|
|
429
|
+
private sendContent;
|
|
430
|
+
private queueReceivedAck;
|
|
431
|
+
private flushReceivedBatch;
|
|
432
|
+
private dispatch;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
export { CONTENT_PROTOCOL_VERSION, type ConnectOptions, type CryptoAdapter, DTelecomSecureChat, FakeCryptoAdapter, type KVStore, type KnownPeerDevice, MemoryKVStore, type MessageDeleted, type MessageEdited, type MessageReceived, type MessageStatus, OlmCryptoAdapter, type PeerNewDeviceEvt, type ReadReceiptEvent, type StatusChangeEvt, type StoredMessage, type TypingEvt, VERSION, WebKVStore };
|