@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,89 +1,186 @@
1
- import type { ContentTypeId } from "@xmtp/content-type-primitives";
2
- import { DeliveryStatus, GroupMessageKind } from "@xmtp/wasm-bindings";
3
- import type { Client } from "@/Client";
4
- import { fromSafeContentTypeId, type SafeMessage } from "@/utils/conversions";
1
+ import {
2
+ contentTypeToString,
3
+ type EncodedContent,
4
+ } from "@xmtp/content-type-primitives";
5
+ import type {
6
+ ContentTypeId,
7
+ DecodedMessageContent,
8
+ DeliveryStatus,
9
+ GroupMessageKind,
10
+ Reaction,
11
+ DecodedMessage as XmtpDecodedMessage,
12
+ } from "@xmtp/wasm-bindings";
13
+ import type { CodecRegistry } from "@/CodecRegistry";
14
+ import { nsToDate } from "@/utils/date";
5
15
 
6
- export type MessageKind = "application" | "membership_change";
7
- export type MessageDeliveryStatus = "unpublished" | "published" | "failed";
16
+ const getContentFromDecodedMessageContent = <T = unknown>(
17
+ content: DecodedMessageContent,
18
+ ): T => {
19
+ switch (content.type) {
20
+ case "text": {
21
+ return content.content as T;
22
+ }
23
+ case "markdown": {
24
+ return content.content as T;
25
+ }
26
+ case "reply": {
27
+ return content.content as T;
28
+ }
29
+ case "reaction": {
30
+ return content.content as T;
31
+ }
32
+ case "attachment": {
33
+ return content.content as T;
34
+ }
35
+ case "remoteAttachment": {
36
+ return content.content as T;
37
+ }
38
+ case "multiRemoteAttachment": {
39
+ return content.content as T;
40
+ }
41
+ case "transactionReference": {
42
+ return content.content as T;
43
+ }
44
+ case "groupUpdated": {
45
+ return content.content as T;
46
+ }
47
+ case "readReceipt": {
48
+ return content.content as T;
49
+ }
50
+ case "leaveRequest": {
51
+ return content.content as T;
52
+ }
53
+ case "walletSendCalls": {
54
+ return content.content as T;
55
+ }
56
+ case "intent": {
57
+ return content.content as T;
58
+ }
59
+ case "actions": {
60
+ return content.content as T;
61
+ }
62
+ case "custom": {
63
+ return content.content as T;
64
+ }
65
+ default:
66
+ content satisfies never;
67
+ return null as T;
68
+ }
69
+ };
8
70
 
9
71
  /**
10
72
  * Represents a decoded XMTP message
11
73
  *
12
- * This class transforms network messages into a structured format with
13
- * content decoding.
14
- *
15
74
  * @class
16
- * @property {any} content - The decoded content of the message
75
+ * @property {unknown} content - The decoded content of the message
17
76
  * @property {ContentTypeId} contentType - The content type of the message content
18
77
  * @property {string} conversationId - Unique identifier for the conversation
19
- * @property {MessageDeliveryStatus} deliveryStatus - Current delivery status of the message ("unpublished" | "published" | "failed")
20
- * @property {string} [fallback] - Optional fallback text for the message
21
- * @property {number} [compression] - Optional compression level applied to the message
78
+ * @property {DeliveryStatus} deliveryStatus - Current delivery status of the message ("unpublished" | "published" | "failed")
79
+ * @property {bigint} expiresAtNs - Timestamp when the message will expire (in nanoseconds)
80
+ * @property {Date} expiresAt - Timestamp when the message will expire
81
+ * @property {string} fallback - Optional fallback text for the message
22
82
  * @property {string} id - Unique identifier for the message
23
- * @property {MessageKind} kind - Type of message ("application" | "membership_change")
24
- * @property {Map<string, string>} parameters - Additional parameters associated with the message
25
- * @property {SafeMessage["content"]} encodedContent - Raw encoded content of the message
83
+ * @property {GroupMessageKind} kind - Type of message ("application" | "membership_change")
84
+ * @property {bigint} numReplies - Number of replies to the message
85
+ * @property {DecodedMessage<Reaction>[]} reactions - Reactions to the message
26
86
  * @property {string} senderInboxId - Identifier for the sender's inbox
87
+ * @property {Date} sentAt - Timestamp when the message was sent
27
88
  * @property {bigint} sentAtNs - Timestamp when the message was sent (in nanoseconds)
28
89
  */
29
90
  export class DecodedMessage<ContentTypes = unknown> {
30
- #client: Client<ContentTypes>;
31
91
  content: ContentTypes | undefined;
32
92
  contentType: ContentTypeId;
33
93
  conversationId: string;
34
- deliveryStatus: MessageDeliveryStatus;
94
+ deliveryStatus: DeliveryStatus;
95
+ expiresAtNs?: bigint;
96
+ expiresAt?: Date;
35
97
  fallback?: string;
36
- compression?: number;
37
98
  id: string;
38
- kind: MessageKind;
39
- parameters: Map<string, string>;
40
- encodedContent: SafeMessage["content"];
99
+ kind: GroupMessageKind;
100
+ numReplies: bigint;
101
+ reactions: DecodedMessage<Reaction>[];
41
102
  senderInboxId: string;
103
+ sentAt: Date;
42
104
  sentAtNs: bigint;
43
105
 
44
- constructor(client: Client<ContentTypes>, message: SafeMessage) {
45
- this.#client = client;
106
+ constructor(codecRegistry: CodecRegistry, message: XmtpDecodedMessage) {
46
107
  this.id = message.id;
108
+ this.expiresAtNs = message.expiresAtNs;
109
+ this.expiresAt = message.expiresAtNs
110
+ ? nsToDate(message.expiresAtNs)
111
+ : undefined;
47
112
  this.sentAtNs = message.sentAtNs;
48
- this.conversationId = message.convoId;
113
+ this.sentAt = nsToDate(message.sentAtNs);
114
+ this.conversationId = message.conversationId;
49
115
  this.senderInboxId = message.senderInboxId;
50
- this.encodedContent = message.content;
116
+ this.contentType = message.contentType;
117
+ this.fallback = message.fallback ?? undefined;
51
118
 
52
- switch (message.kind) {
53
- case GroupMessageKind.Application:
54
- this.kind = "application";
55
- break;
56
- case GroupMessageKind.MembershipChange:
57
- this.kind = "membership_change";
58
- break;
59
- // no default
60
- }
119
+ this.kind = message.kind;
120
+ this.deliveryStatus = message.deliveryStatus;
61
121
 
62
- switch (message.deliveryStatus) {
63
- case DeliveryStatus.Unpublished:
64
- this.deliveryStatus = "unpublished";
65
- break;
66
- case DeliveryStatus.Published:
67
- this.deliveryStatus = "published";
68
- break;
69
- case DeliveryStatus.Failed:
70
- this.deliveryStatus = "failed";
71
- break;
72
- // no default
73
- }
122
+ this.numReplies = message.numReplies;
123
+ this.reactions = message.reactions.map(
124
+ (reaction) => new DecodedMessage<Reaction>(codecRegistry, reaction),
125
+ );
74
126
 
75
- this.contentType = fromSafeContentTypeId(message.content.type);
76
- this.parameters = new Map(Object.entries(message.content.parameters));
77
- this.fallback = message.content.fallback;
78
- this.compression = message.content.compression;
127
+ this.content =
128
+ getContentFromDecodedMessageContent<ContentTypes>(message.content) ??
129
+ undefined;
79
130
 
80
- try {
81
- this.content = this.#client.decodeContent<ContentTypes>(
82
- message,
83
- this.contentType,
84
- );
85
- } catch {
86
- this.content = undefined;
131
+ switch (message.content.type) {
132
+ case "reply": {
133
+ const reply = message.content.content;
134
+ let replyContent = getContentFromDecodedMessageContent<ContentTypes>(
135
+ reply.content,
136
+ );
137
+ if (reply.content.type === "custom") {
138
+ const codec = codecRegistry.getCodec<ContentTypes>(
139
+ reply.content.content.type as ContentTypeId,
140
+ );
141
+ if (codec) {
142
+ try {
143
+ replyContent = codec.decode(replyContent as EncodedContent);
144
+ } catch (error) {
145
+ if (error instanceof Error) {
146
+ console.warn(`Error decoding custom content: ${error.message}`);
147
+ } else {
148
+ console.warn(`Error decoding custom content`);
149
+ }
150
+ }
151
+ }
152
+ }
153
+ this.content = {
154
+ referenceId: reply.referenceId,
155
+ content: replyContent,
156
+ inReplyTo: reply.inReplyTo
157
+ ? new DecodedMessage<ContentTypes>(codecRegistry, reply.inReplyTo)
158
+ : null,
159
+ } as ContentTypes;
160
+ break;
161
+ }
162
+ case "custom": {
163
+ const encodedContent = message.content.content;
164
+ const codec = codecRegistry.getCodec<ContentTypes>(this.contentType);
165
+ if (codec) {
166
+ try {
167
+ this.content = codec.decode(encodedContent);
168
+ } catch (error) {
169
+ if (error instanceof Error) {
170
+ console.warn(`Error decoding custom content: ${error.message}`);
171
+ } else {
172
+ console.warn(`Error decoding custom content`);
173
+ }
174
+ this.content = undefined;
175
+ }
176
+ } else {
177
+ console.warn(
178
+ `No codec found for content type "${contentTypeToString(this.contentType)}"`,
179
+ );
180
+ this.content = undefined;
181
+ }
182
+ break;
183
+ }
87
184
  }
88
185
  }
89
186
  }
package/src/Dm.ts CHANGED
@@ -1,6 +1,8 @@
1
- import type { Client } from "@/Client";
1
+ import type { CodecRegistry } from "@/CodecRegistry";
2
2
  import { Conversation } from "@/Conversation";
3
+ import type { ClientWorkerAction } from "@/types/actions";
3
4
  import type { SafeConversation } from "@/utils/conversions";
5
+ import type { WorkerBridge } from "@/utils/WorkerBridge";
4
6
 
5
7
  /**
6
8
  * Represents a direct message conversation between two inboxes
@@ -8,23 +10,27 @@ import type { SafeConversation } from "@/utils/conversions";
8
10
  * This class is not intended to be initialized directly.
9
11
  */
10
12
  export class Dm<ContentTypes = unknown> extends Conversation<ContentTypes> {
11
- #client: Client<ContentTypes>;
13
+ #codecRegistry: CodecRegistry;
14
+ #worker: WorkerBridge<ClientWorkerAction>;
12
15
  #id: string;
13
16
 
14
17
  /**
15
18
  * Creates a new direct message conversation instance
16
19
  *
17
- * @param client - The client instance managing this direct message conversation
20
+ * @param worker - The worker bridge instance for client communication
21
+ * @param codecRegistry - The codec registry instance
18
22
  * @param id - Identifier for the direct message conversation
19
23
  * @param data - Optional conversation data to initialize with
20
24
  */
21
25
  constructor(
22
- client: Client<ContentTypes>,
26
+ worker: WorkerBridge<ClientWorkerAction>,
27
+ codecRegistry: CodecRegistry,
23
28
  id: string,
24
29
  data?: SafeConversation,
25
30
  ) {
26
- super(client, id, data);
27
- this.#client = client;
31
+ super(worker, codecRegistry, id, data);
32
+ this.#worker = worker;
33
+ this.#codecRegistry = codecRegistry;
28
34
  this.#id = id;
29
35
  }
30
36
 
@@ -34,14 +40,24 @@ export class Dm<ContentTypes = unknown> extends Conversation<ContentTypes> {
34
40
  * @returns Promise that resolves with the peer's inbox ID
35
41
  */
36
42
  async peerInboxId() {
37
- return this.#client.sendMessage("dm.peerInboxId", {
43
+ return this.#worker.action("dm.peerInboxId", {
38
44
  id: this.#id,
39
45
  });
40
46
  }
41
47
 
42
- async getDuplicateDms() {
43
- return this.#client.sendMessage("dm.getDuplicateDms", {
48
+ async duplicateDms() {
49
+ const conversations = await this.#worker.action("dm.duplicateDms", {
44
50
  id: this.#id,
45
51
  });
52
+
53
+ return conversations.map(
54
+ (conversation) =>
55
+ new Dm<ContentTypes>(
56
+ this.#worker,
57
+ this.#codecRegistry,
58
+ conversation.id,
59
+ conversation,
60
+ ),
61
+ );
46
62
  }
47
63
  }
package/src/Group.ts CHANGED
@@ -4,9 +4,11 @@ import type {
4
4
  PermissionPolicy,
5
5
  PermissionUpdateType,
6
6
  } from "@xmtp/wasm-bindings";
7
- import type { Client } from "@/Client";
7
+ import type { CodecRegistry } from "@/CodecRegistry";
8
8
  import { Conversation } from "@/Conversation";
9
+ import type { ClientWorkerAction } from "@/types/actions";
9
10
  import type { SafeConversation } from "@/utils/conversions";
11
+ import type { WorkerBridge } from "@/utils/WorkerBridge";
10
12
 
11
13
  /**
12
14
  * Represents a group conversation between multiple inboxes
@@ -15,7 +17,8 @@ import type { SafeConversation } from "@/utils/conversions";
15
17
  */
16
18
  export class Group<ContentTypes = unknown> extends Conversation<ContentTypes> {
17
19
  #admins: SafeConversation["admins"] = [];
18
- #client: Client<ContentTypes>;
20
+ #appData?: SafeConversation["appData"];
21
+ #worker: WorkerBridge<ClientWorkerAction>;
19
22
  #description?: SafeConversation["description"];
20
23
  #id: string;
21
24
  #imageUrl?: SafeConversation["imageUrl"];
@@ -26,6 +29,7 @@ export class Group<ContentTypes = unknown> extends Conversation<ContentTypes> {
26
29
  this.#name = data?.name ?? "";
27
30
  this.#imageUrl = data?.imageUrl ?? "";
28
31
  this.#description = data?.description ?? "";
32
+ this.#appData = data?.appData ?? "";
29
33
  this.#admins = data?.admins ?? [];
30
34
  this.#superAdmins = data?.superAdmins ?? [];
31
35
  }
@@ -33,17 +37,19 @@ export class Group<ContentTypes = unknown> extends Conversation<ContentTypes> {
33
37
  /**
34
38
  * Creates a new group conversation instance
35
39
  *
36
- * @param client - The client instance managing this group conversation
40
+ * @param worker - The worker bridge instance for client communication
41
+ * @param codecRegistry - The codec registry instance
37
42
  * @param id - Identifier for the group conversation
38
43
  * @param data - Optional conversation data to initialize with
39
44
  */
40
45
  constructor(
41
- client: Client<ContentTypes>,
46
+ worker: WorkerBridge<ClientWorkerAction>,
47
+ codecRegistry: CodecRegistry,
42
48
  id: string,
43
49
  data?: SafeConversation,
44
50
  ) {
45
- super(client, id, data);
46
- this.#client = client;
51
+ super(worker, codecRegistry, id, data);
52
+ this.#worker = worker;
47
53
  this.#id = id;
48
54
  this.#syncData(data);
49
55
  }
@@ -72,7 +78,7 @@ export class Group<ContentTypes = unknown> extends Conversation<ContentTypes> {
72
78
  * @param name The new name for the group
73
79
  */
74
80
  async updateName(name: string) {
75
- await this.#client.sendMessage("group.updateName", {
81
+ await this.#worker.action("group.updateName", {
76
82
  id: this.#id,
77
83
  name,
78
84
  });
@@ -92,7 +98,7 @@ export class Group<ContentTypes = unknown> extends Conversation<ContentTypes> {
92
98
  * @param imageUrl The new image URL for the group
93
99
  */
94
100
  async updateImageUrl(imageUrl: string) {
95
- await this.#client.sendMessage("group.updateImageUrl", {
101
+ await this.#worker.action("group.updateImageUrl", {
96
102
  id: this.#id,
97
103
  imageUrl,
98
104
  });
@@ -112,13 +118,33 @@ export class Group<ContentTypes = unknown> extends Conversation<ContentTypes> {
112
118
  * @param description The new description for the group
113
119
  */
114
120
  async updateDescription(description: string) {
115
- await this.#client.sendMessage("group.updateDescription", {
121
+ await this.#worker.action("group.updateDescription", {
116
122
  id: this.#id,
117
123
  description,
118
124
  });
119
125
  this.#description = description;
120
126
  }
121
127
 
128
+ /**
129
+ * The app data of the group
130
+ */
131
+ get appData() {
132
+ return this.#appData;
133
+ }
134
+
135
+ /**
136
+ * Updates the group's app data (max 8192 bytes)
137
+ *
138
+ * @param appData The new app data for the group
139
+ */
140
+ async updateAppData(appData: string) {
141
+ await this.#worker.action("group.updateAppData", {
142
+ id: this.#id,
143
+ appData,
144
+ });
145
+ this.#appData = appData;
146
+ }
147
+
122
148
  /**
123
149
  * The list of admins of the group by inbox ID
124
150
  */
@@ -139,7 +165,7 @@ export class Group<ContentTypes = unknown> extends Conversation<ContentTypes> {
139
165
  * @returns Array of admin inbox IDs
140
166
  */
141
167
  async listAdmins() {
142
- const admins = await this.#client.sendMessage("group.listAdmins", {
168
+ const admins = await this.#worker.action("group.listAdmins", {
143
169
  id: this.#id,
144
170
  });
145
171
  this.#admins = admins;
@@ -152,12 +178,9 @@ export class Group<ContentTypes = unknown> extends Conversation<ContentTypes> {
152
178
  * @returns Array of super admin inbox IDs
153
179
  */
154
180
  async listSuperAdmins() {
155
- const superAdmins = await this.#client.sendMessage(
156
- "group.listSuperAdmins",
157
- {
158
- id: this.#id,
159
- },
160
- );
181
+ const superAdmins = await this.#worker.action("group.listSuperAdmins", {
182
+ id: this.#id,
183
+ });
161
184
  this.#superAdmins = superAdmins;
162
185
  return superAdmins;
163
186
  }
@@ -168,7 +191,7 @@ export class Group<ContentTypes = unknown> extends Conversation<ContentTypes> {
168
191
  * @returns The group's permissions
169
192
  */
170
193
  async permissions() {
171
- return this.#client.sendMessage("group.permissions", {
194
+ return this.#worker.action("group.permissions", {
172
195
  id: this.#id,
173
196
  });
174
197
  }
@@ -185,7 +208,7 @@ export class Group<ContentTypes = unknown> extends Conversation<ContentTypes> {
185
208
  policy: PermissionPolicy,
186
209
  metadataField?: MetadataField,
187
210
  ) {
188
- return this.#client.sendMessage("group.updatePermission", {
211
+ return this.#worker.action("group.updatePermission", {
189
212
  id: this.#id,
190
213
  permissionType,
191
214
  policy,
@@ -221,7 +244,7 @@ export class Group<ContentTypes = unknown> extends Conversation<ContentTypes> {
221
244
  * @param identifiers Array of member identifiers to add
222
245
  */
223
246
  async addMembersByIdentifiers(identifiers: Identifier[]) {
224
- return this.#client.sendMessage("group.addMembersByIdentifiers", {
247
+ return this.#worker.action("group.addMembersByIdentifiers", {
225
248
  id: this.#id,
226
249
  identifiers,
227
250
  });
@@ -233,7 +256,7 @@ export class Group<ContentTypes = unknown> extends Conversation<ContentTypes> {
233
256
  * @param inboxIds Array of inbox IDs to add
234
257
  */
235
258
  async addMembers(inboxIds: string[]) {
236
- return this.#client.sendMessage("group.addMembers", {
259
+ return this.#worker.action("group.addMembers", {
237
260
  id: this.#id,
238
261
  inboxIds,
239
262
  });
@@ -245,7 +268,7 @@ export class Group<ContentTypes = unknown> extends Conversation<ContentTypes> {
245
268
  * @param identifiers Array of member identifiers to remove
246
269
  */
247
270
  async removeMembersByIdentifiers(identifiers: Identifier[]) {
248
- return this.#client.sendMessage("group.removeMembersByIdentifiers", {
271
+ return this.#worker.action("group.removeMembersByIdentifiers", {
249
272
  id: this.#id,
250
273
  identifiers,
251
274
  });
@@ -257,7 +280,7 @@ export class Group<ContentTypes = unknown> extends Conversation<ContentTypes> {
257
280
  * @param inboxIds Array of inbox IDs to remove
258
281
  */
259
282
  async removeMembers(inboxIds: string[]) {
260
- return this.#client.sendMessage("group.removeMembers", {
283
+ return this.#worker.action("group.removeMembers", {
261
284
  id: this.#id,
262
285
  inboxIds,
263
286
  });
@@ -269,7 +292,7 @@ export class Group<ContentTypes = unknown> extends Conversation<ContentTypes> {
269
292
  * @param inboxId The inbox ID of the member to promote
270
293
  */
271
294
  async addAdmin(inboxId: string) {
272
- return this.#client.sendMessage("group.addAdmin", {
295
+ return this.#worker.action("group.addAdmin", {
273
296
  id: this.#id,
274
297
  inboxId,
275
298
  });
@@ -281,7 +304,7 @@ export class Group<ContentTypes = unknown> extends Conversation<ContentTypes> {
281
304
  * @param inboxId The inbox ID of the admin to demote
282
305
  */
283
306
  async removeAdmin(inboxId: string) {
284
- return this.#client.sendMessage("group.removeAdmin", {
307
+ return this.#worker.action("group.removeAdmin", {
285
308
  id: this.#id,
286
309
  inboxId,
287
310
  });
@@ -293,7 +316,7 @@ export class Group<ContentTypes = unknown> extends Conversation<ContentTypes> {
293
316
  * @param inboxId The inbox ID of the member to promote
294
317
  */
295
318
  async addSuperAdmin(inboxId: string) {
296
- return this.#client.sendMessage("group.addSuperAdmin", {
319
+ return this.#worker.action("group.addSuperAdmin", {
297
320
  id: this.#id,
298
321
  inboxId,
299
322
  });
@@ -305,9 +328,29 @@ export class Group<ContentTypes = unknown> extends Conversation<ContentTypes> {
305
328
  * @param inboxId The inbox ID of the super admin to demote
306
329
  */
307
330
  async removeSuperAdmin(inboxId: string) {
308
- return this.#client.sendMessage("group.removeSuperAdmin", {
331
+ return this.#worker.action("group.removeSuperAdmin", {
309
332
  id: this.#id,
310
333
  inboxId,
311
334
  });
312
335
  }
336
+
337
+ /**
338
+ * Request to leave the group
339
+ */
340
+ async requestRemoval() {
341
+ return this.#worker.action("group.requestRemoval", {
342
+ id: this.#id,
343
+ });
344
+ }
345
+
346
+ /**
347
+ * Checks if the current user has requested to leave the group
348
+ *
349
+ * @returns Boolean
350
+ */
351
+ async isPendingRemoval() {
352
+ return this.#worker.action("group.isPendingRemoval", {
353
+ id: this.#id,
354
+ });
355
+ }
313
356
  }
package/src/Opfs.ts ADDED
@@ -0,0 +1,63 @@
1
+ import type { OpfsAction } from "@/types/actions/opfs";
2
+ import { WorkerBridge } from "@/utils/WorkerBridge";
3
+
4
+ export class Opfs {
5
+ #worker: WorkerBridge<OpfsAction>;
6
+ #enableLogging: boolean;
7
+
8
+ constructor(enableLogging?: boolean) {
9
+ const worker = new Worker(new URL("./workers/opfs", import.meta.url), {
10
+ type: "module",
11
+ });
12
+ this.#worker = new WorkerBridge<OpfsAction>(worker, enableLogging);
13
+ this.#enableLogging = enableLogging ?? false;
14
+ }
15
+
16
+ async init() {
17
+ await this.#worker.action("opfs.init", {
18
+ enableLogging: this.#enableLogging,
19
+ });
20
+ }
21
+
22
+ close() {
23
+ this.#worker.close();
24
+ }
25
+
26
+ static async create(enableLogging?: boolean) {
27
+ const opfs = new Opfs(enableLogging);
28
+ await opfs.init();
29
+ return opfs;
30
+ }
31
+
32
+ async listFiles() {
33
+ return this.#worker.action("opfs.listFiles");
34
+ }
35
+
36
+ async fileCount() {
37
+ return this.#worker.action("opfs.fileCount");
38
+ }
39
+
40
+ async poolCapacity() {
41
+ return this.#worker.action("opfs.poolCapacity");
42
+ }
43
+
44
+ async fileExists(path: string) {
45
+ return this.#worker.action("opfs.fileExists", { path });
46
+ }
47
+
48
+ async deleteFile(path: string) {
49
+ return this.#worker.action("opfs.deleteFile", { path });
50
+ }
51
+
52
+ async exportDb(path: string) {
53
+ return this.#worker.action("opfs.exportDb", { path });
54
+ }
55
+
56
+ async importDb(path: string, data: Uint8Array) {
57
+ return this.#worker.action("opfs.importDb", { path, data });
58
+ }
59
+
60
+ async clearAll() {
61
+ return this.#worker.action("opfs.clearAll");
62
+ }
63
+ }