@xmtp/browser-sdk 5.3.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 (53) hide show
  1. package/dist/index.d.ts +541 -671
  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 +30 -29
  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 +98 -45
  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/opfs.ts +66 -0
  31. package/src/types/actions/preferences.ts +6 -13
  32. package/src/types/actions/streams.ts +8 -8
  33. package/src/types/actions.ts +11 -9
  34. package/src/types/options.ts +47 -6
  35. package/src/{ClientWorkerClass.ts → utils/WorkerBridge.ts} +35 -45
  36. package/src/utils/contentTypes.ts +77 -0
  37. package/src/utils/conversions.ts +17 -590
  38. package/src/utils/createClient.ts +16 -11
  39. package/src/utils/errors.ts +13 -19
  40. package/src/utils/inboxId.ts +46 -0
  41. package/src/utils/inboxState.ts +23 -0
  42. package/src/utils/installations.ts +95 -0
  43. package/src/utils/metadata.ts +15 -0
  44. package/src/utils/signer.ts +4 -4
  45. package/src/utils/uuid.ts +8 -0
  46. package/src/workers/client.ts +176 -135
  47. package/src/workers/opfs.ts +127 -0
  48. package/dist/workers/utils.js +0 -2
  49. package/dist/workers/utils.js.map +0 -1
  50. package/src/Utils.ts +0 -143
  51. package/src/UtilsWorkerClass.ts +0 -121
  52. package/src/types/actions/utils.ts +0 -69
  53. package/src/workers/utils.ts +0 -155
@@ -1,42 +1,35 @@
1
1
  import type {
2
+ Consent,
2
3
  ConsentEntityType,
3
4
  ConsentState,
4
5
  GroupSyncSummary,
6
+ InboxState,
5
7
  } from "@xmtp/wasm-bindings";
6
- import type { SafeConsent, SafeInboxState } from "@/utils/conversions";
7
8
 
8
9
  export type PreferencesAction =
9
10
  | {
10
11
  action: "preferences.inboxState";
11
12
  id: string;
12
- result: SafeInboxState;
13
+ result: InboxState;
13
14
  data: {
14
15
  refreshFromNetwork: boolean;
15
16
  };
16
17
  }
17
18
  | {
18
- action: "preferences.inboxStateFromInboxIds";
19
+ action: "preferences.getInboxStates";
19
20
  id: string;
20
- result: SafeInboxState[];
21
+ result: InboxState[];
21
22
  data: {
22
23
  inboxIds: string[];
23
24
  refreshFromNetwork: boolean;
24
25
  };
25
26
  }
26
- | {
27
- action: "preferences.getLatestInboxState";
28
- id: string;
29
- result: SafeInboxState;
30
- data: {
31
- inboxId: string;
32
- };
33
- }
34
27
  | {
35
28
  action: "preferences.setConsentStates";
36
29
  id: string;
37
30
  result: undefined;
38
31
  data: {
39
- records: SafeConsent[];
32
+ records: Consent[];
40
33
  };
41
34
  }
42
35
  | {
@@ -1,15 +1,15 @@
1
- import type { UserPreference } from "@xmtp/wasm-bindings";
2
1
  import type {
3
- SafeConsent,
4
- SafeConversation,
5
- SafeMessage,
6
- } from "@/utils/conversions";
2
+ Consent,
3
+ DecodedMessage,
4
+ UserPreferenceUpdate,
5
+ } from "@xmtp/wasm-bindings";
6
+ import type { SafeConversation } from "@/utils/conversions";
7
7
 
8
8
  export type StreamAction =
9
9
  | {
10
10
  action: "stream.message";
11
11
  streamId: string;
12
- result: SafeMessage | undefined;
12
+ result: DecodedMessage | undefined;
13
13
  }
14
14
  | {
15
15
  action: "stream.conversation";
@@ -19,12 +19,12 @@ export type StreamAction =
19
19
  | {
20
20
  action: "stream.consent";
21
21
  streamId: string;
22
- result: SafeConsent[] | undefined;
22
+ result: Consent[] | undefined;
23
23
  }
24
24
  | {
25
25
  action: "stream.preferences";
26
26
  streamId: string;
27
- result: UserPreference[] | undefined;
27
+ result: UserPreferenceUpdate[] | undefined;
28
28
  }
29
29
  | {
30
30
  action: "stream.messageDeleted";
@@ -6,22 +6,24 @@ import type { DmAction } from "@/types/actions/dm";
6
6
  import type { GroupAction } from "@/types/actions/group";
7
7
  import type { PreferencesAction } from "@/types/actions/preferences";
8
8
 
9
- type UnknownAction = {
9
+ export type UnknownAction = {
10
10
  action: string;
11
11
  id: string;
12
12
  result: unknown;
13
13
  data: unknown;
14
14
  };
15
15
 
16
+ export type EndStreamAction = {
17
+ action: "endStream";
18
+ id: string;
19
+ result: undefined;
20
+ data: {
21
+ streamId: string;
22
+ };
23
+ };
24
+
16
25
  export type ClientWorkerAction =
17
- | {
18
- action: "endStream";
19
- id: string;
20
- result: undefined;
21
- data: {
22
- streamId: string;
23
- };
24
- }
26
+ | EndStreamAction
25
27
  | ClientAction
26
28
  | ConversationAction
27
29
  | ConversationsAction
@@ -1,5 +1,21 @@
1
1
  import type { ContentCodec } from "@xmtp/content-type-primitives";
2
+ import type {
3
+ Actions,
4
+ Attachment,
5
+ EnrichedReply,
6
+ GroupUpdated,
7
+ Intent,
8
+ LeaveRequest,
9
+ LogLevel,
10
+ MultiRemoteAttachment,
11
+ Reaction,
12
+ ReadReceipt,
13
+ RemoteAttachment,
14
+ TransactionReference,
15
+ WalletSendCalls,
16
+ } from "@xmtp/wasm-bindings";
2
17
  import type { ApiUrls } from "@/constants";
18
+ import type { DecodedMessage } from "@/DecodedMessage";
3
19
 
4
20
  export type XmtpEnv = keyof typeof ApiUrls;
5
21
 
@@ -74,7 +90,7 @@ export type OtherOptions = {
74
90
  /**
75
91
  * Logging level
76
92
  */
77
- loggingLevel?: "off" | "error" | "warn" | "info" | "debug" | "trace";
93
+ loggingLevel?: LogLevel;
78
94
  /**
79
95
  * Disable automatic registration when creating a client
80
96
  */
@@ -87,14 +103,39 @@ export type OtherOptions = {
87
103
  * Custom app version
88
104
  */
89
105
  appVersion?: string;
90
- /**
91
- * Should debug events be tracked
92
- * (default: false)
93
- */
94
- debugEventsEnabled?: boolean;
95
106
  };
96
107
 
97
108
  export type ClientOptions = NetworkOptions &
98
109
  ContentOptions &
99
110
  StorageOptions &
100
111
  OtherOptions;
112
+
113
+ export type Reply<T = unknown, U = unknown> = {
114
+ referenceId: EnrichedReply["referenceId"];
115
+ content: T;
116
+ inReplyTo: DecodedMessage<U> | null;
117
+ };
118
+
119
+ export type BuiltInContentTypes =
120
+ | string // text, markdown
121
+ | LeaveRequest
122
+ | Reaction
123
+ | ReadReceipt
124
+ | Attachment
125
+ | RemoteAttachment
126
+ | TransactionReference
127
+ | WalletSendCalls
128
+ | Actions
129
+ | Intent
130
+ | MultiRemoteAttachment
131
+ | GroupUpdated;
132
+
133
+ export type ExtractCodecContentTypes<C extends ContentCodec[] = []> =
134
+ C extends readonly []
135
+ ? BuiltInContentTypes
136
+ : [...C][number] extends ContentCodec<infer T>
137
+ ?
138
+ | T
139
+ | BuiltInContentTypes
140
+ | Reply<T | BuiltInContentTypes, T | BuiltInContentTypes>
141
+ : BuiltInContentTypes;
@@ -1,37 +1,35 @@
1
- import { v4 } from "uuid";
2
1
  import type {
3
2
  ActionErrorData,
4
3
  ActionName,
5
4
  ActionWithoutData,
6
- ClientWorkerAction,
5
+ EndStreamAction,
7
6
  ExtractActionData,
8
7
  ExtractActionResult,
8
+ UnknownAction,
9
9
  } from "@/types/actions";
10
10
  import type {
11
11
  StreamAction,
12
12
  StreamActionErrorData,
13
13
  } from "@/types/actions/streams";
14
14
  import type { StreamOptions } from "@/utils/streams";
15
+ import { uuid } from "@/utils/uuid";
15
16
 
16
17
  const handleError = (event: ErrorEvent) => {
17
- console.error(event.message);
18
+ console.error(`[worker] error: ${event.message}`);
18
19
  };
19
20
 
20
21
  /**
21
- * Class that sets up a worker and provides communications for client functions
22
+ * Class that sets up a bridge for worker communications
22
23
  *
23
- * This class is not meant to be used directly, it is extended by the Client class
24
- * to provide an interface to the worker.
24
+ * This class is not meant to be used directly.
25
25
  *
26
- * @param worker - The worker to use for the client class
26
+ * @param worker - The worker to use for communications
27
27
  * @param enableLogging - Whether to enable logging in the worker
28
- * @returns A new ClientWorkerClass instance
28
+ * @returns A new WorkerBridge instance
29
29
  */
30
- export class ClientWorkerClass {
30
+ export class WorkerBridge<T extends UnknownAction> {
31
31
  #worker: Worker;
32
-
33
32
  #enableLogging: boolean;
34
-
35
33
  #promises = new Map<
36
34
  string,
37
35
  {
@@ -40,31 +38,30 @@ export class ClientWorkerClass {
40
38
  }
41
39
  >();
42
40
 
43
- constructor(worker: Worker, enableLogging: boolean) {
41
+ constructor(worker: Worker, enableLogging?: boolean) {
44
42
  this.#worker = worker;
45
43
  this.#worker.addEventListener("message", this.handleMessage);
46
- if (enableLogging) {
47
- this.#worker.addEventListener("error", handleError);
48
- }
49
- this.#enableLogging = enableLogging;
44
+ this.#worker.addEventListener("error", handleError);
45
+ this.#enableLogging = enableLogging ?? false;
50
46
  }
51
47
 
52
48
  /**
53
- * Sends an action message to the client worker
49
+ * Sends an action message to the worker
54
50
  *
55
51
  * @param action - The action to send to the worker
56
52
  * @param data - The data to send to the worker
57
53
  * @returns A promise that resolves when the action is completed
58
54
  */
59
- sendMessage<A extends ActionName<ClientWorkerAction>>(
60
- action: A,
61
- data: ExtractActionData<ClientWorkerAction, A>,
62
- ) {
63
- const promiseId = v4();
55
+ action<
56
+ A extends ActionName<T>,
57
+ D = ExtractActionData<T, A>,
58
+ R = ExtractActionResult<T, A>,
59
+ >(action: A, ...args: D extends undefined ? [] : [data: D]) {
60
+ const promiseId = uuid();
64
61
  this.#worker.postMessage({
65
62
  action,
66
63
  id: promiseId,
67
- data,
64
+ data: args[0],
68
65
  });
69
66
  const promise = new Promise((resolve, reject) => {
70
67
  this.#promises.set(promiseId, {
@@ -72,27 +69,20 @@ export class ClientWorkerClass {
72
69
  reject,
73
70
  });
74
71
  });
75
- return promise as [ExtractActionResult<ClientWorkerAction, A>] extends [
76
- undefined,
77
- ]
78
- ? Promise<void>
79
- : Promise<ExtractActionResult<ClientWorkerAction, A>>;
72
+ return promise as [R] extends [undefined] ? Promise<void> : Promise<R>;
80
73
  }
81
74
 
82
75
  /**
83
- * Handles a message from the client worker
76
+ * Handles a message from the worker
84
77
  *
85
78
  * @param event - The event to handle
86
79
  */
87
80
  handleMessage = (
88
- event: MessageEvent<
89
- | ActionWithoutData<ClientWorkerAction>
90
- | ActionErrorData<ClientWorkerAction>
91
- >,
81
+ event: MessageEvent<ActionWithoutData<T> | ActionErrorData<T>>,
92
82
  ) => {
93
83
  const eventData = event.data;
94
84
  if (this.#enableLogging) {
95
- console.log("client received event data", eventData);
85
+ console.log("[worker] client received event data", eventData);
96
86
  }
97
87
  const promise = this.#promises.get(eventData.id);
98
88
  if (promise) {
@@ -106,16 +96,16 @@ export class ClientWorkerClass {
106
96
  };
107
97
 
108
98
  /**
109
- * Handles a stream message from the client worker
99
+ * Handles a stream message from the worker
110
100
  *
111
101
  * @param streamId - The ID of the stream to handle
112
102
  * @param callback - The callback to handle the stream message
113
103
  * @returns A function to remove the stream handler
114
104
  */
115
- handleStreamMessage = <T extends StreamAction["result"], V = T>(
105
+ handleStreamMessage = <R extends StreamAction["result"], V = R>(
116
106
  streamId: string,
117
- callback: (error: Error | null, value: T | undefined) => void,
118
- options?: StreamOptions<T, V>,
107
+ callback: (error: Error | null, value: R | undefined) => void,
108
+ options?: StreamOptions<R, V>,
119
109
  ) => {
120
110
  const streamHandler = (
121
111
  event: MessageEvent<StreamAction | StreamActionErrorData>,
@@ -131,16 +121,18 @@ export class ClientWorkerClass {
131
121
  if ("error" in eventData) {
132
122
  callback(eventData.error, undefined);
133
123
  } else {
134
- callback(null, eventData.result as T);
124
+ callback(null, eventData.result as R);
135
125
  }
136
126
  }
137
127
  };
138
128
  this.#worker.addEventListener("message", streamHandler);
139
129
 
140
130
  return async () => {
141
- await this.sendMessage("endStream", {
142
- streamId,
143
- });
131
+ await this.action<
132
+ "endStream",
133
+ EndStreamAction["data"],
134
+ EndStreamAction["result"]
135
+ >("endStream", { streamId });
144
136
  this.#worker.removeEventListener("message", streamHandler);
145
137
  };
146
138
  };
@@ -150,9 +142,7 @@ export class ClientWorkerClass {
150
142
  */
151
143
  close() {
152
144
  this.#worker.removeEventListener("message", this.handleMessage);
153
- if (this.#enableLogging) {
154
- this.#worker.removeEventListener("error", handleError);
155
- }
145
+ this.#worker.removeEventListener("error", handleError);
156
146
  this.#worker.terminate();
157
147
  }
158
148
  }
@@ -0,0 +1,77 @@
1
+ import init, {
2
+ contentTypeActions as wasmContentTypeActions,
3
+ contentTypeAttachment as wasmContentTypeAttachment,
4
+ contentTypeGroupUpdated as wasmContentTypeGroupUpdated,
5
+ contentTypeIntent as wasmContentTypeIntent,
6
+ contentTypeLeaveRequest as wasmContentTypeLeaveRequest,
7
+ contentTypeMarkdown as wasmContentTypeMarkdown,
8
+ contentTypeMultiRemoteAttachment as wasmContentTypeMultiRemoteAttachment,
9
+ contentTypeReaction as wasmContentTypeReaction,
10
+ contentTypeReadReceipt as wasmContentTypeReadReceipt,
11
+ contentTypeRemoteAttachment as wasmContentTypeRemoteAttachment,
12
+ contentTypeReply as wasmContentTypeReply,
13
+ contentTypeText as wasmContentTypeText,
14
+ contentTypeTransactionReference as wasmContentTypeTransactionReference,
15
+ contentTypeWalletSendCalls as wasmContentTypeWalletSendCalls,
16
+ decryptAttachment as wasmDecryptAttachment,
17
+ encodeActions as wasmEncodeActions,
18
+ encodeAttachment as wasmEncodeAttachment,
19
+ encodeIntent as wasmEncodeIntent,
20
+ encodeMarkdown as wasmEncodeMarkdown,
21
+ encodeMultiRemoteAttachment as wasmEncodeMultiRemoteAttachment,
22
+ encodeReaction as wasmEncodeReaction,
23
+ encodeReadReceipt as wasmEncodeReadReceipt,
24
+ encodeRemoteAttachment as wasmEncodeRemoteAttachment,
25
+ encodeText as wasmEncodeText,
26
+ encodeTransactionReference as wasmEncodeTransactionReference,
27
+ encodeWalletSendCalls as wasmEncodeWalletSendCalls,
28
+ encryptAttachment as wasmEncryptAttachment,
29
+ } from "@xmtp/wasm-bindings";
30
+
31
+ const wrap =
32
+ <T extends (...args: never[]) => unknown>(fn: T) =>
33
+ async (...args: Parameters<T>): Promise<ReturnType<T>> => {
34
+ await init();
35
+ return fn(...args) as ReturnType<T>;
36
+ };
37
+
38
+ // encoders
39
+ export const encodeActions = wrap(wasmEncodeActions);
40
+ export const encodeAttachment = wrap(wasmEncodeAttachment);
41
+ export const encodeIntent = wrap(wasmEncodeIntent);
42
+ export const encodeMarkdown = wrap(wasmEncodeMarkdown);
43
+ export const encodeMultiRemoteAttachment = wrap(
44
+ wasmEncodeMultiRemoteAttachment,
45
+ );
46
+ export const encodeReaction = wrap(wasmEncodeReaction);
47
+ export const encodeReadReceipt = wrap(wasmEncodeReadReceipt);
48
+ export const encodeRemoteAttachment = wrap(wasmEncodeRemoteAttachment);
49
+ export const encodeText = wrap(wasmEncodeText);
50
+ export const encodeTransactionReference = wrap(wasmEncodeTransactionReference);
51
+ export const encodeWalletSendCalls = wrap(wasmEncodeWalletSendCalls);
52
+
53
+ // content types
54
+ export const contentTypeActions = wrap(wasmContentTypeActions);
55
+ export const contentTypeAttachment = wrap(wasmContentTypeAttachment);
56
+ export const contentTypeGroupUpdated = wrap(wasmContentTypeGroupUpdated);
57
+ export const contentTypeIntent = wrap(wasmContentTypeIntent);
58
+ export const contentTypeLeaveRequest = wrap(wasmContentTypeLeaveRequest);
59
+ export const contentTypeMarkdown = wrap(wasmContentTypeMarkdown);
60
+ export const contentTypeMultiRemoteAttachment = wrap(
61
+ wasmContentTypeMultiRemoteAttachment,
62
+ );
63
+ export const contentTypeReaction = wrap(wasmContentTypeReaction);
64
+ export const contentTypeReadReceipt = wrap(wasmContentTypeReadReceipt);
65
+ export const contentTypeRemoteAttachment = wrap(
66
+ wasmContentTypeRemoteAttachment,
67
+ );
68
+ export const contentTypeReply = wrap(wasmContentTypeReply);
69
+ export const contentTypeText = wrap(wasmContentTypeText);
70
+ export const contentTypeTransactionReference = wrap(
71
+ wasmContentTypeTransactionReference,
72
+ );
73
+ export const contentTypeWalletSendCalls = wrap(wasmContentTypeWalletSendCalls);
74
+
75
+ // remote attachment encryption
76
+ export const encryptAttachment = wrap(wasmEncryptAttachment);
77
+ export const decryptAttachment = wrap(wasmDecryptAttachment);