@xmtp/browser-sdk 5.2.0 → 6.0.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.
Files changed (54) hide show
  1. package/dist/index.d.ts +587 -670
  2. package/dist/index.js +1 -1
  3. package/dist/index.js.map +1 -1
  4. package/dist/workers/client.js +1 -1
  5. package/dist/workers/client.js.map +1 -1
  6. package/dist/workers/opfs.js +2 -0
  7. package/dist/workers/opfs.js.map +1 -0
  8. package/package.json +12 -16
  9. package/src/Client.ts +92 -219
  10. package/src/CodecRegistry.ts +27 -0
  11. package/src/Conversation.ts +275 -104
  12. package/src/Conversations.ts +188 -99
  13. package/src/DebugInformation.ts +10 -27
  14. package/src/DecodedMessage.ts +155 -58
  15. package/src/Dm.ts +25 -9
  16. package/src/Group.ts +69 -26
  17. package/src/Opfs.ts +63 -0
  18. package/src/Preferences.ts +68 -52
  19. package/src/WorkerClient.ts +5 -5
  20. package/src/WorkerConversation.ts +119 -44
  21. package/src/WorkerConversations.ts +35 -74
  22. package/src/WorkerDebugInformation.ts +1 -12
  23. package/src/WorkerPreferences.ts +6 -14
  24. package/src/index.ts +53 -24
  25. package/src/types/actions/client.ts +6 -17
  26. package/src/types/actions/conversation.ts +160 -31
  27. package/src/types/actions/conversations.ts +21 -24
  28. package/src/types/actions/debugInformation.ts +3 -11
  29. package/src/types/actions/dm.ts +1 -1
  30. package/src/types/actions/group.ts +25 -0
  31. package/src/types/actions/opfs.ts +66 -0
  32. package/src/types/actions/preferences.ts +6 -13
  33. package/src/types/actions/streams.ts +8 -8
  34. package/src/types/actions.ts +11 -9
  35. package/src/types/options.ts +47 -6
  36. package/src/{ClientWorkerClass.ts → utils/WorkerBridge.ts} +35 -45
  37. package/src/utils/contentTypes.ts +77 -0
  38. package/src/utils/conversions.ts +18 -588
  39. package/src/utils/createClient.ts +16 -11
  40. package/src/utils/errors.ts +13 -19
  41. package/src/utils/inboxId.ts +46 -0
  42. package/src/utils/inboxState.ts +23 -0
  43. package/src/utils/installations.ts +95 -0
  44. package/src/utils/metadata.ts +15 -0
  45. package/src/utils/signer.ts +4 -4
  46. package/src/utils/uuid.ts +8 -0
  47. package/src/workers/client.ts +191 -132
  48. package/src/workers/opfs.ts +127 -0
  49. package/dist/workers/utils.js +0 -2
  50. package/dist/workers/utils.js.map +0 -1
  51. package/src/Utils.ts +0 -143
  52. package/src/UtilsWorkerClass.ts +0 -121
  53. package/src/types/actions/utils.ts +0 -69
  54. package/src/workers/utils.ts +0 -155
@@ -1,21 +1,31 @@
1
- import type { ContentTypeId } from "@xmtp/content-type-primitives";
2
- import { ContentTypeText } from "@xmtp/content-type-text";
3
- import type { ConsentState } from "@xmtp/wasm-bindings";
4
- import { v4 } from "uuid";
5
- import type { Client } from "@/Client";
1
+ import {
2
+ type Actions,
3
+ type Attachment,
4
+ type ConsentState,
5
+ type EncodedContent,
6
+ type Intent,
7
+ type ListMessagesOptions,
8
+ type MultiRemoteAttachment,
9
+ type Reaction,
10
+ type RemoteAttachment,
11
+ type Reply,
12
+ type SendMessageOpts,
13
+ type TransactionReference,
14
+ type WalletSendCalls,
15
+ type DecodedMessage as XmtpDecodedMessage,
16
+ } from "@xmtp/wasm-bindings";
17
+ import type { CodecRegistry } from "@/CodecRegistry";
6
18
  import { DecodedMessage } from "@/DecodedMessage";
7
- import type {
8
- SafeConversation,
9
- SafeListMessagesOptions,
10
- SafeMessage,
11
- } from "@/utils/conversions";
19
+ import type { ClientWorkerAction } from "@/types/actions";
20
+ import type { SafeConversation } from "@/utils/conversions";
12
21
  import { nsToDate } from "@/utils/date";
13
- import { MissingContentTypeError } from "@/utils/errors";
14
22
  import {
15
23
  createStream,
16
24
  type StreamCallback,
17
25
  type StreamOptions,
18
26
  } from "@/utils/streams";
27
+ import { uuid } from "@/utils/uuid";
28
+ import type { WorkerBridge } from "@/utils/WorkerBridge";
19
29
 
20
30
  /**
21
31
  * Represents a conversation
@@ -24,25 +34,28 @@ import {
24
34
  */
25
35
  export class Conversation<ContentTypes = unknown> {
26
36
  #addedByInboxId?: SafeConversation["addedByInboxId"];
27
- #client: Client<ContentTypes>;
37
+ #codecRegistry: CodecRegistry;
28
38
  #createdAtNs?: SafeConversation["createdAtNs"];
29
39
  #id: string;
30
40
  #metadata?: SafeConversation["metadata"];
31
- #isCommitLogForked?: SafeConversation["isCommitLogForked"];
41
+ #worker: WorkerBridge<ClientWorkerAction>;
32
42
 
33
43
  /**
34
44
  * Creates a new conversation instance
35
45
  *
36
- * @param client - The client instance managing the conversation
46
+ * @param worker - The worker bridge instance for client communication
47
+ * @param codecRegistry - The codec registry instance
37
48
  * @param id - The unique identifier for this conversation
38
49
  * @param data - Optional conversation data to initialize with
39
50
  */
40
51
  constructor(
41
- client: Client<ContentTypes>,
52
+ worker: WorkerBridge<ClientWorkerAction>,
53
+ codecRegistry: CodecRegistry,
42
54
  id: string,
43
55
  data?: SafeConversation,
44
56
  ) {
45
- this.#client = client;
57
+ this.#worker = worker;
58
+ this.#codecRegistry = codecRegistry;
46
59
  this.#id = id;
47
60
  this.#syncData(data);
48
61
  }
@@ -51,17 +64,12 @@ export class Conversation<ContentTypes = unknown> {
51
64
  this.#addedByInboxId = data?.addedByInboxId;
52
65
  this.#metadata = data?.metadata;
53
66
  this.#createdAtNs = data?.createdAtNs;
54
- this.#isCommitLogForked = data?.isCommitLogForked;
55
67
  }
56
68
 
57
69
  get id() {
58
70
  return this.#id;
59
71
  }
60
72
 
61
- get isCommitLogForked() {
62
- return this.#isCommitLogForked;
63
- }
64
-
65
73
  get addedByInboxId() {
66
74
  return this.#addedByInboxId;
67
75
  }
@@ -79,19 +87,16 @@ export class Conversation<ContentTypes = unknown> {
79
87
  }
80
88
 
81
89
  async lastMessage() {
82
- const lastMessage = await this.#client.sendMessage(
83
- "conversation.lastMessage",
84
- {
85
- id: this.#id,
86
- },
87
- );
90
+ const lastMessage = await this.#worker.action("conversation.lastMessage", {
91
+ id: this.#id,
92
+ });
88
93
  return lastMessage
89
- ? new DecodedMessage(this.#client, lastMessage)
94
+ ? new DecodedMessage<ContentTypes>(this.#codecRegistry, lastMessage)
90
95
  : undefined;
91
96
  }
92
97
 
93
98
  async isActive() {
94
- return this.#client.sendMessage("conversation.isActive", {
99
+ return this.#worker.action("conversation.isActive", {
95
100
  id: this.#id,
96
101
  });
97
102
  }
@@ -102,7 +107,7 @@ export class Conversation<ContentTypes = unknown> {
102
107
  * @returns Promise that resolves with the conversation members
103
108
  */
104
109
  async members() {
105
- return this.#client.sendMessage("conversation.members", {
110
+ return this.#worker.action("conversation.members", {
106
111
  id: this.#id,
107
112
  });
108
113
  }
@@ -113,7 +118,7 @@ export class Conversation<ContentTypes = unknown> {
113
118
  * @returns Promise that resolves with the updated conversation data
114
119
  */
115
120
  async sync() {
116
- const data = await this.#client.sendMessage("conversation.sync", {
121
+ const data = await this.#worker.action("conversation.sync", {
117
122
  id: this.#id,
118
123
  });
119
124
  this.#syncData(data);
@@ -126,60 +131,217 @@ export class Conversation<ContentTypes = unknown> {
126
131
  * @returns Promise that resolves when publishing is complete
127
132
  */
128
133
  async publishMessages() {
129
- return this.#client.sendMessage("conversation.publishMessages", {
134
+ return this.#worker.action("conversation.publishMessages", {
135
+ id: this.#id,
136
+ });
137
+ }
138
+
139
+ /**
140
+ * Sends a message
141
+ *
142
+ * @param content - The encoded content to send
143
+ * @param options - Optional send options
144
+ * @param options.shouldPush - Indicates whether this message should be
145
+ * included in push notifications
146
+ * @param options.isOptimistic - Indicates whether this message should be
147
+ * sent optimistically and published later via `publishMessages`
148
+ * @returns Promise that resolves with the message ID after it has been sent
149
+ */
150
+ async send(content: EncodedContent, options?: SendMessageOpts) {
151
+ return this.#worker.action("conversation.send", {
152
+ id: this.#id,
153
+ content,
154
+ options,
155
+ });
156
+ }
157
+
158
+ /**
159
+ * Sends a text message
160
+ *
161
+ * @param text - The text to send
162
+ * @param isOptimistic - Whether to send the message optimistically
163
+ * @returns Promise that resolves with the message ID after it has been sent
164
+ */
165
+ async sendText(text: string, isOptimistic?: boolean) {
166
+ return this.#worker.action("conversation.sendText", {
167
+ id: this.#id,
168
+ text,
169
+ isOptimistic,
170
+ });
171
+ }
172
+
173
+ /**
174
+ * Sends a markdown message
175
+ *
176
+ * @param markdown - The markdown to send
177
+ * @param isOptimistic - Whether to send the message optimistically
178
+ * @returns Promise that resolves with the message ID after it has been sent
179
+ */
180
+ async sendMarkdown(markdown: string, isOptimistic?: boolean) {
181
+ return this.#worker.action("conversation.sendMarkdown", {
182
+ id: this.#id,
183
+ markdown,
184
+ isOptimistic,
185
+ });
186
+ }
187
+
188
+ /**
189
+ * Sends a reaction message
190
+ *
191
+ * @param reaction - The reaction to send
192
+ * @param isOptimistic - Whether to send the message optimistically
193
+ * @returns Promise that resolves with the message ID after it has been sent
194
+ */
195
+ async sendReaction(reaction: Reaction, isOptimistic?: boolean) {
196
+ return this.#worker.action("conversation.sendReaction", {
130
197
  id: this.#id,
198
+ reaction,
199
+ isOptimistic,
131
200
  });
132
201
  }
133
202
 
134
203
  /**
135
- * Prepares a message to be published
204
+ * Sends a read receipt message
136
205
  *
137
- * @param content - The content to send
138
- * @param contentType - Optional content type of the message content
139
- * @returns Promise that resolves with the message ID
140
- * @throws {MissingContentTypeError} if content type is required but not provided
206
+ * @param isOptimistic - Whether to send the message optimistically
207
+ * @returns Promise that resolves with the message ID after it has been sent
141
208
  */
142
- async sendOptimistic(content: ContentTypes, contentType?: ContentTypeId) {
143
- if (typeof content !== "string" && !contentType) {
144
- throw new MissingContentTypeError();
145
- }
146
-
147
- const { encodedContent: safeEncodedContent, sendOptions } =
148
- typeof content === "string"
149
- ? this.#client.prepareForSend(content, contentType ?? ContentTypeText)
150
- : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
151
- this.#client.prepareForSend(content, contentType!);
152
-
153
- return this.#client.sendMessage("conversation.sendOptimistic", {
209
+ async sendReadReceipt(isOptimistic?: boolean) {
210
+ return this.#worker.action("conversation.sendReadReceipt", {
154
211
  id: this.#id,
155
- content: safeEncodedContent,
156
- sendOptions,
212
+ isOptimistic,
157
213
  });
158
214
  }
159
215
 
160
216
  /**
161
- * Publishes a new message
217
+ * Sends a reply message
162
218
  *
163
- * @param content - The content to send
164
- * @param contentType - Optional content type of the message content
219
+ * @param reply - The reply to send
220
+ * @param isOptimistic - Whether to send the message optimistically
165
221
  * @returns Promise that resolves with the message ID after it has been sent
166
- * @throws {MissingContentTypeError} if content type is required but not provided
167
222
  */
168
- async send(content: ContentTypes, contentType?: ContentTypeId) {
169
- if (typeof content !== "string" && !contentType) {
170
- throw new MissingContentTypeError();
171
- }
172
-
173
- const { encodedContent: safeEncodedContent, sendOptions } =
174
- typeof content === "string"
175
- ? this.#client.prepareForSend(content, contentType ?? ContentTypeText)
176
- : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
177
- this.#client.prepareForSend(content, contentType!);
178
-
179
- return this.#client.sendMessage("conversation.send", {
223
+ async sendReply(reply: Reply, isOptimistic?: boolean) {
224
+ return this.#worker.action("conversation.sendReply", {
180
225
  id: this.#id,
181
- content: safeEncodedContent,
182
- sendOptions,
226
+ reply,
227
+ isOptimistic,
228
+ });
229
+ }
230
+
231
+ /**
232
+ * Sends a transaction reference message
233
+ *
234
+ * @param transactionReference - The transaction reference to send
235
+ * @param isOptimistic - Whether to send the message optimistically
236
+ * @returns Promise that resolves with the message ID after it has been sent
237
+ */
238
+ async sendTransactionReference(
239
+ transactionReference: TransactionReference,
240
+ isOptimistic?: boolean,
241
+ ) {
242
+ return this.#worker.action("conversation.sendTransactionReference", {
243
+ id: this.#id,
244
+ transactionReference,
245
+ isOptimistic,
246
+ });
247
+ }
248
+
249
+ /**
250
+ * Sends a wallet send calls message
251
+ *
252
+ * @param walletSendCalls - The wallet send calls to send
253
+ * @param isOptimistic - Whether to send the message optimistically
254
+ * @returns Promise that resolves with the message ID after it has been sent
255
+ */
256
+ async sendWalletSendCalls(
257
+ walletSendCalls: WalletSendCalls,
258
+ isOptimistic?: boolean,
259
+ ) {
260
+ return this.#worker.action("conversation.sendWalletSendCalls", {
261
+ id: this.#id,
262
+ walletSendCalls,
263
+ isOptimistic,
264
+ });
265
+ }
266
+
267
+ /**
268
+ * Sends an actions message
269
+ *
270
+ * @param actions - The actions to send
271
+ * @param isOptimistic - Whether to send the message optimistically
272
+ * @returns Promise that resolves with the message ID after it has been sent
273
+ */
274
+ async sendActions(actions: Actions, isOptimistic?: boolean) {
275
+ return this.#worker.action("conversation.sendActions", {
276
+ id: this.#id,
277
+ actions,
278
+ isOptimistic,
279
+ });
280
+ }
281
+
282
+ /**
283
+ * Sends an intent message
284
+ *
285
+ * @param intent - The intent to send
286
+ * @param isOptimistic - Whether to send the message optimistically
287
+ * @returns Promise that resolves with the message ID after it has been sent
288
+ */
289
+ async sendIntent(intent: Intent, isOptimistic?: boolean) {
290
+ return this.#worker.action("conversation.sendIntent", {
291
+ id: this.#id,
292
+ intent,
293
+ isOptimistic,
294
+ });
295
+ }
296
+
297
+ /**
298
+ * Sends an attachment message
299
+ *
300
+ * @param attachment - The attachment to send
301
+ * @param isOptimistic - Whether to send the message optimistically
302
+ * @returns Promise that resolves with the message ID after it has been sent
303
+ */
304
+ async sendAttachment(attachment: Attachment, isOptimistic?: boolean) {
305
+ return this.#worker.action("conversation.sendAttachment", {
306
+ id: this.#id,
307
+ attachment,
308
+ isOptimistic,
309
+ });
310
+ }
311
+
312
+ /**
313
+ * Sends a multi remote attachment message
314
+ *
315
+ * @param multiRemoteAttachment - The multi remote attachment to send
316
+ * @param isOptimistic - Whether to send the message optimistically
317
+ * @returns Promise that resolves with the message ID after it has been sent
318
+ */
319
+ async sendMultiRemoteAttachment(
320
+ multiRemoteAttachment: MultiRemoteAttachment,
321
+ isOptimistic?: boolean,
322
+ ) {
323
+ return this.#worker.action("conversation.sendMultiRemoteAttachment", {
324
+ id: this.#id,
325
+ multiRemoteAttachment,
326
+ isOptimistic,
327
+ });
328
+ }
329
+
330
+ /**
331
+ * Sends a remote attachment message
332
+ *
333
+ * @param remoteAttachment - The remote attachment to send
334
+ * @param isOptimistic - Whether to send the message optimistically
335
+ * @returns Promise that resolves with the message ID after it has been sent
336
+ */
337
+ async sendRemoteAttachment(
338
+ remoteAttachment: RemoteAttachment,
339
+ isOptimistic?: boolean,
340
+ ) {
341
+ return this.#worker.action("conversation.sendRemoteAttachment", {
342
+ id: this.#id,
343
+ remoteAttachment,
344
+ isOptimistic,
183
345
  });
184
346
  }
185
347
 
@@ -189,13 +351,16 @@ export class Conversation<ContentTypes = unknown> {
189
351
  * @param options - Optional filtering and pagination options
190
352
  * @returns Promise that resolves with an array of decoded messages
191
353
  */
192
- async messages(options?: SafeListMessagesOptions) {
193
- const messages = await this.#client.sendMessage("conversation.messages", {
354
+ async messages(options?: ListMessagesOptions) {
355
+ const messages = await this.#worker.action("conversation.messages", {
194
356
  id: this.#id,
195
357
  options,
196
358
  });
197
359
 
198
- return messages.map((message) => new DecodedMessage(this.#client, message));
360
+ return messages.map(
361
+ (message) =>
362
+ new DecodedMessage<ContentTypes>(this.#codecRegistry, message),
363
+ );
199
364
  }
200
365
 
201
366
  /**
@@ -205,9 +370,9 @@ export class Conversation<ContentTypes = unknown> {
205
370
  * @returns Promise that resolves with the count of messages
206
371
  */
207
372
  async countMessages(
208
- options?: Omit<SafeListMessagesOptions, "limit" | "direction">,
373
+ options?: Omit<ListMessagesOptions, "limit" | "direction">,
209
374
  ) {
210
- const count = await this.#client.sendMessage("conversation.countMessages", {
375
+ const count = await this.#worker.action("conversation.countMessages", {
211
376
  id: this.#id,
212
377
  options,
213
378
  });
@@ -219,8 +384,8 @@ export class Conversation<ContentTypes = unknown> {
219
384
  *
220
385
  * @returns Promise that resolves with the current consent state
221
386
  */
222
- async consentState() {
223
- return this.#client.sendMessage("conversation.consentState", {
387
+ async consentState(): Promise<ConsentState> {
388
+ return this.#worker.action("conversation.consentState", {
224
389
  id: this.#id,
225
390
  });
226
391
  }
@@ -232,7 +397,7 @@ export class Conversation<ContentTypes = unknown> {
232
397
  * @returns Promise that resolves when the update is complete
233
398
  */
234
399
  async updateConsentState(state: ConsentState) {
235
- return this.#client.sendMessage("conversation.updateConsentState", {
400
+ return this.#worker.action("conversation.updateConsentState", {
236
401
  id: this.#id,
237
402
  state,
238
403
  });
@@ -244,12 +409,9 @@ export class Conversation<ContentTypes = unknown> {
244
409
  * @returns Promise that resolves with the current message disappearing settings
245
410
  */
246
411
  async messageDisappearingSettings() {
247
- return this.#client.sendMessage(
248
- "conversation.messageDisappearingSettings",
249
- {
250
- id: this.#id,
251
- },
252
- );
412
+ return this.#worker.action("conversation.messageDisappearingSettings", {
413
+ id: this.#id,
414
+ });
253
415
  }
254
416
 
255
417
  /**
@@ -260,7 +422,7 @@ export class Conversation<ContentTypes = unknown> {
260
422
  * @returns Promise that resolves when the update is complete
261
423
  */
262
424
  async updateMessageDisappearingSettings(fromNs: bigint, inNs: bigint) {
263
- return this.#client.sendMessage(
425
+ return this.#worker.action(
264
426
  "conversation.updateMessageDisappearingSettings",
265
427
  {
266
428
  id: this.#id,
@@ -276,7 +438,7 @@ export class Conversation<ContentTypes = unknown> {
276
438
  * @returns Promise that resolves when the settings are removed
277
439
  */
278
440
  async removeMessageDisappearingSettings() {
279
- return this.#client.sendMessage(
441
+ return this.#worker.action(
280
442
  "conversation.removeMessageDisappearingSettings",
281
443
  {
282
444
  id: this.#id,
@@ -290,12 +452,9 @@ export class Conversation<ContentTypes = unknown> {
290
452
  * @returns Promise that resolves with whether message disappearing is enabled
291
453
  */
292
454
  async isMessageDisappearingEnabled() {
293
- return this.#client.sendMessage(
294
- "conversation.isMessageDisappearingEnabled",
295
- {
296
- id: this.#id,
297
- },
298
- );
455
+ return this.#worker.action("conversation.isMessageDisappearingEnabled", {
456
+ id: this.#id,
457
+ });
299
458
  }
300
459
 
301
460
  /**
@@ -305,51 +464,51 @@ export class Conversation<ContentTypes = unknown> {
305
464
  * @returns Stream instance for new messages
306
465
  */
307
466
  async stream(
308
- options?: StreamOptions<SafeMessage, DecodedMessage<ContentTypes>>,
467
+ options?: StreamOptions<XmtpDecodedMessage, DecodedMessage<ContentTypes>>,
309
468
  ) {
310
469
  const stream = async (
311
- callback: StreamCallback<SafeMessage>,
470
+ callback: StreamCallback<XmtpDecodedMessage>,
312
471
  onFail: () => void,
313
472
  ) => {
314
- const streamId = v4();
473
+ const streamId = uuid();
315
474
  if (!options?.disableSync) {
316
475
  // sync the conversation
317
476
  await this.sync();
318
477
  }
319
478
  // start the stream
320
- await this.#client.sendMessage("conversation.stream", {
479
+ await this.#worker.action("conversation.stream", {
321
480
  groupId: this.#id,
322
481
  streamId,
323
482
  });
324
483
  // handle stream messages
325
- return this.#client.handleStreamMessage<
326
- SafeMessage,
484
+ return this.#worker.handleStreamMessage<
485
+ XmtpDecodedMessage,
327
486
  DecodedMessage<ContentTypes>
328
487
  >(streamId, callback, {
329
488
  ...options,
330
489
  onFail,
331
490
  });
332
491
  };
333
- const convertMessage = (value: SafeMessage) => {
334
- return new DecodedMessage(this.#client, value);
492
+ const convertMessage = (value: XmtpDecodedMessage) => {
493
+ return new DecodedMessage<ContentTypes>(this.#codecRegistry, value);
335
494
  };
336
495
 
337
496
  return createStream(stream, convertMessage, options);
338
497
  }
339
498
 
340
499
  async pausedForVersion() {
341
- return this.#client.sendMessage("conversation.pausedForVersion", {
500
+ return this.#worker.action("conversation.pausedForVersion", {
342
501
  id: this.#id,
343
502
  });
344
503
  }
345
504
 
346
505
  /**
347
- * Retrieves HMAC keys for this conversation
506
+ * Gets HMAC keys for this conversation
348
507
  *
349
508
  * @returns Promise that resolves with the HMAC keys
350
509
  */
351
- async getHmacKeys() {
352
- return this.#client.sendMessage("conversation.getHmacKeys", {
510
+ async hmacKeys() {
511
+ return this.#worker.action("conversation.hmacKeys", {
353
512
  id: this.#id,
354
513
  });
355
514
  }
@@ -360,7 +519,19 @@ export class Conversation<ContentTypes = unknown> {
360
519
  * @returns The debug information for this conversation
361
520
  */
362
521
  async debugInfo() {
363
- return this.#client.sendMessage("conversation.debugInfo", {
522
+ return this.#worker.action("conversation.debugInfo", {
523
+ id: this.#id,
524
+ });
525
+ }
526
+
527
+ /**
528
+ * Retrieves the last read times for this conversation
529
+ *
530
+ * @returns A map keyed by inbox ID with the last read timestamp
531
+ * (nanoseconds since epoch)
532
+ */
533
+ async lastReadTimes() {
534
+ return this.#worker.action("conversation.lastReadTimes", {
364
535
  id: this.#id,
365
536
  });
366
537
  }