@xmtp/browser-sdk 6.4.1 → 7.0.0-dev.b5cdc06

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/src/Client.ts CHANGED
@@ -1,12 +1,18 @@
1
1
  import { type ContentCodec } from "@xmtp/content-type-primitives";
2
2
  import {
3
3
  Backend,
4
+ BackupElementSelectionOption,
5
+ IdentifierKind,
4
6
  LogLevel,
7
+ type ArchiveMetadata,
5
8
  type ArchiveOptions,
9
+ type AvailableArchiveInfo,
10
+ type GroupSyncSummary,
6
11
  type Identifier,
7
12
  type InboxState,
8
13
  } from "@xmtp/wasm-bindings";
9
14
  import { CodecRegistry } from "@/CodecRegistry";
15
+ import { HistorySyncUrls } from "@/constants";
10
16
  import { Conversations } from "@/Conversations";
11
17
  import { DebugInformation } from "@/DebugInformation";
12
18
  import { Preferences } from "@/Preferences";
@@ -17,6 +23,7 @@ import type {
17
23
  XmtpEnv,
18
24
  } from "@/types/options";
19
25
  import { createBackend } from "@/utils/createBackend";
26
+ import { createClient as createLowLevelClient } from "@/utils/createClient";
20
27
  import {
21
28
  AccountAlreadyAssociatedError,
22
29
  InboxReassignError,
@@ -46,6 +53,28 @@ const resolveBackend = async (
46
53
  return createBackend({ env: envOrBackend, gatewayHost });
47
54
  };
48
55
 
56
+ const createEphemeralIdentifier = (): Identifier => {
57
+ const bytes = new Uint8Array(20);
58
+ globalThis.crypto.getRandomValues(bytes);
59
+
60
+ return {
61
+ identifier: `0x${Array.from(bytes, (byte) =>
62
+ byte.toString(16).padStart(2, "0"),
63
+ ).join("")}`,
64
+ identifierKind: IdentifierKind.Ethereum,
65
+ };
66
+ };
67
+
68
+ const toInboxUpdatesCountMap = (
69
+ value: Map<string, number> | Record<string, number>,
70
+ ) => {
71
+ if (value instanceof Map) {
72
+ return value;
73
+ }
74
+
75
+ return new Map(Object.entries(value));
76
+ };
77
+
49
78
  /**
50
79
  * Client for interacting with the XMTP network
51
80
  */
@@ -454,6 +483,7 @@ export class Client<ContentTypes = ExtractCodecContentTypes> {
454
483
  return this.#worker.action("client.registerIdentity", {
455
484
  signer,
456
485
  signatureRequestId,
486
+ waitForRegistrationVisible: this.#options?.waitForRegistrationVisible,
457
487
  });
458
488
  }
459
489
 
@@ -713,6 +743,32 @@ export class Client<ContentTypes = ExtractCodecContentTypes> {
713
743
  return this.#worker.action("client.canMessage", { identifiers });
714
744
  }
715
745
 
746
+ /**
747
+ * Fetches the latest inbox updates count for the specified inbox IDs
748
+ *
749
+ * @param inboxIds - The inbox IDs to check
750
+ * @returns Map of inbox IDs to their updates count
751
+ */
752
+ async fetchLatestInboxUpdatesCount(inboxIds: string[]) {
753
+ const result = await this.#worker.action(
754
+ "client.fetchLatestInboxUpdatesCount",
755
+ {
756
+ inboxIds,
757
+ },
758
+ );
759
+
760
+ return toInboxUpdatesCountMap(result);
761
+ }
762
+
763
+ /**
764
+ * Fetches the latest inbox updates count for the client's inbox
765
+ *
766
+ * @returns The latest inbox updates count
767
+ */
768
+ async fetchOwnInboxUpdatesCount() {
769
+ return this.#worker.action("client.fetchOwnInboxUpdatesCount", {});
770
+ }
771
+
716
772
  /**
717
773
  * Checks if the specified identifiers can be messaged
718
774
  *
@@ -755,6 +811,60 @@ export class Client<ContentTypes = ExtractCodecContentTypes> {
755
811
  return canMessageMap;
756
812
  }
757
813
 
814
+ /**
815
+ * Fetches the latest inbox updates count for the specified inbox IDs
816
+ * without a client
817
+ *
818
+ * @param inboxIds - The inbox IDs to check
819
+ * @param backend - Optional `Backend` instance created with `createBackend()`
820
+ * @returns Map of inbox IDs to their updates count
821
+ */
822
+ static async fetchLatestInboxUpdatesCount(
823
+ inboxIds: string[],
824
+ backendOrEnv?: Backend | XmtpEnv,
825
+ ): Promise<Map<string, number>>;
826
+ /**
827
+ * Fetches the latest inbox updates count for the specified inbox IDs
828
+ * without a client
829
+ *
830
+ * @param inboxIds - The inbox IDs to check
831
+ * @param env - Optional XMTP environment
832
+ * @param gatewayHost - Optional gateway host
833
+ * @returns Map of inbox IDs to their updates count
834
+ * @deprecated Pass a `Backend` instance created with `createBackend()` instead
835
+ * of `XmtpEnv` and `gatewayHost`.
836
+ */
837
+ static async fetchLatestInboxUpdatesCount(
838
+ inboxIds: string[],
839
+ env?: XmtpEnv,
840
+ gatewayHost?: string,
841
+ ): Promise<Map<string, number>>;
842
+ static async fetchLatestInboxUpdatesCount(
843
+ inboxIds: string[],
844
+ envOrBackend?: XmtpEnv | Backend,
845
+ gatewayHost?: string,
846
+ ) {
847
+ const backend = await resolveBackend(envOrBackend, gatewayHost);
848
+ const { client } = await createLowLevelClient(createEphemeralIdentifier(), {
849
+ backend,
850
+ dbPath: null,
851
+ disableDeviceSync: true,
852
+ });
853
+ // The wasm-bindings Client holds WASM-linear-memory allocations that are
854
+ // not reclaimed by the JS GC. Free the ephemeral client in finally so the
855
+ // allocation is released even if the fetch rejects.
856
+ try {
857
+ const result = (await client.fetchLatestInboxUpdatesCount(
858
+ true,
859
+ inboxIds,
860
+ )) as Record<string, number> | Map<string, number>;
861
+
862
+ return toInboxUpdatesCountMap(result);
863
+ } finally {
864
+ client.free();
865
+ }
866
+ }
867
+
758
868
  /**
759
869
  * Fetches the inbox ID for a given identifier from the local database
760
870
  * If not found, fetches from the network
@@ -828,17 +938,152 @@ export class Client<ContentTypes = ExtractCodecContentTypes> {
828
938
  });
829
939
  }
830
940
 
941
+ /**
942
+ * Get the default archive options (consent and messages)
943
+ */
944
+ #getDefaultArchiveOptions(): ArchiveOptions {
945
+ return {
946
+ elements: [
947
+ BackupElementSelectionOption.Consent,
948
+ BackupElementSelectionOption.Messages,
949
+ ],
950
+ excludeDisappearingMessages: false,
951
+ };
952
+ }
953
+
954
+ /**
955
+ * Get the default server URL based on the environment
956
+ */
957
+ #getDefaultServerUrl(): string {
958
+ const env = this.#env ?? "dev";
959
+ return HistorySyncUrls[env];
960
+ }
961
+
831
962
  /**
832
963
  * Send a sync request to other devices on the network
833
964
  *
834
- * @param options - Archive options specifying what to sync
835
- * @param serverUrl - The server URL for the sync request
965
+ * @param options - Archive options specifying what to sync (defaults to consent and messages)
966
+ * @param serverUrl - The server URL for the sync request (defaults to environment-specific URL)
836
967
  * @returns Promise that resolves when the sync request is sent
837
968
  */
838
- async sendSyncRequest(options: ArchiveOptions, serverUrl: string) {
969
+ async sendSyncRequest(options?: ArchiveOptions, serverUrl?: string) {
970
+ const resolvedOptions = options ?? this.#getDefaultArchiveOptions();
971
+ const resolvedServerUrl = serverUrl ?? this.#getDefaultServerUrl();
972
+
839
973
  return this.#worker.action("client.sendSyncRequest", {
840
- options,
841
- serverUrl,
974
+ options: resolvedOptions,
975
+ serverUrl: resolvedServerUrl,
976
+ });
977
+ }
978
+
979
+ /**
980
+ * Send a sync archive to the sync group
981
+ *
982
+ * @param pin - The pin used for reference when importing
983
+ * @param options - Archive options specifying what to sync (defaults to consent and messages)
984
+ * @param serverUrl - The server URL for the sync archive (defaults to environment-specific URL)
985
+ * @returns Promise that resolves when the sync archive is sent
986
+ */
987
+ async sendSyncArchive(
988
+ pin: string,
989
+ options?: ArchiveOptions,
990
+ serverUrl?: string,
991
+ ) {
992
+ const resolvedOptions = options ?? this.#getDefaultArchiveOptions();
993
+ const resolvedServerUrl = serverUrl ?? this.#getDefaultServerUrl();
994
+
995
+ return this.#worker.action("client.sendSyncArchive", {
996
+ options: resolvedOptions,
997
+ serverUrl: resolvedServerUrl,
998
+ pin,
842
999
  });
843
1000
  }
1001
+
1002
+ /**
1003
+ * Process a sync archive that matches the pin given
1004
+ *
1005
+ * @param archivePin - Optional pin to match. If not provided, processes the last archive sent
1006
+ * @returns Promise that resolves when the archive is processed
1007
+ */
1008
+ async processSyncArchive(archivePin?: string | null) {
1009
+ return this.#worker.action("client.processSyncArchive", {
1010
+ archivePin,
1011
+ });
1012
+ }
1013
+
1014
+ /**
1015
+ * List the archives available for import in the sync group
1016
+ *
1017
+ * You may need to manually sync the sync group before calling
1018
+ * this function to see recently uploaded archives.
1019
+ *
1020
+ * @param daysCutoff - Number of days to look back for archives
1021
+ * @returns Promise that resolves with array of available archive information
1022
+ */
1023
+ async listAvailableArchives(
1024
+ daysCutoff: number,
1025
+ ): Promise<AvailableArchiveInfo[]> {
1026
+ return this.#worker.action("client.listAvailableArchives", {
1027
+ daysCutoff,
1028
+ });
1029
+ }
1030
+
1031
+ /**
1032
+ * Export archive data to bytes for later restoration
1033
+ *
1034
+ * @param key - Encryption key for the archive
1035
+ * @param opts - Archive options specifying what to include (defaults to consent and messages)
1036
+ * @returns Promise that resolves with the archive data as bytes
1037
+ */
1038
+ async createArchive(
1039
+ key: Uint8Array,
1040
+ opts?: ArchiveOptions,
1041
+ ): Promise<Uint8Array> {
1042
+ const resolvedOpts = opts ?? this.#getDefaultArchiveOptions();
1043
+
1044
+ return this.#worker.action("client.createArchive", {
1045
+ opts: resolvedOpts,
1046
+ key,
1047
+ });
1048
+ }
1049
+
1050
+ /**
1051
+ * Import an archive from bytes
1052
+ *
1053
+ * @param data - The archive data as bytes
1054
+ * @param key - Encryption key for the archive
1055
+ * @returns Promise that resolves when the archive is imported
1056
+ */
1057
+ async importArchive(data: Uint8Array, key: Uint8Array) {
1058
+ return this.#worker.action("client.importArchive", {
1059
+ data,
1060
+ key,
1061
+ });
1062
+ }
1063
+
1064
+ /**
1065
+ * Load the metadata for an archive to see what it contains
1066
+ *
1067
+ * @param data - The archive data as bytes
1068
+ * @param key - Encryption key for the archive
1069
+ * @returns Promise that resolves with the archive metadata
1070
+ */
1071
+ async archiveMetadata(
1072
+ data: Uint8Array,
1073
+ key: Uint8Array,
1074
+ ): Promise<ArchiveMetadata> {
1075
+ return this.#worker.action("client.archiveMetadata", {
1076
+ data,
1077
+ key,
1078
+ });
1079
+ }
1080
+
1081
+ /**
1082
+ * Manually sync all device sync groups
1083
+ *
1084
+ * @returns Promise that resolves with a summary of the sync operation
1085
+ */
1086
+ async syncAllDeviceSyncGroups(): Promise<GroupSyncSummary> {
1087
+ return this.#worker.action("client.syncAllDeviceSyncGroups");
1088
+ }
844
1089
  }
@@ -140,6 +140,19 @@ export class Conversation<ContentTypes = unknown> {
140
140
  });
141
141
  }
142
142
 
143
+ /**
144
+ * Decodes, decrypts, and persists a raw envelope from a group message stream.
145
+ *
146
+ * @param envelopeBytes - Raw protobuf-encoded envelope bytes from the stream
147
+ * @returns The processed and stored messages
148
+ */
149
+ async processStreamedMessage(envelopeBytes: Uint8Array) {
150
+ return this.#worker.action("conversation.processStreamedMessage", {
151
+ id: this.#id,
152
+ envelopeBytes,
153
+ });
154
+ }
155
+
143
156
  /**
144
157
  * Sends a message
145
158
  *
@@ -1,12 +1,19 @@
1
1
  import {
2
2
  verifySignedWithPublicKey,
3
+ type ArchiveMetadata,
3
4
  type ArchiveOptions,
5
+ type AvailableArchiveInfo,
4
6
  type Client,
7
+ type GroupSyncSummary,
5
8
  type Identifier,
6
9
  type KeyPackageStatus,
7
10
  type SignatureRequestHandle,
8
11
  } from "@xmtp/wasm-bindings";
9
- import type { ClientOptions, XmtpEnv } from "@/types/options";
12
+ import type {
13
+ ClientOptions,
14
+ VisibilityConfirmationOptions,
15
+ XmtpEnv,
16
+ } from "@/types/options";
10
17
  import { createClient } from "@/utils/createClient";
11
18
  import type { SafeSigner } from "@/utils/signer";
12
19
  import { WorkerConversations } from "@/WorkerConversations";
@@ -87,6 +94,19 @@ export class WorkerClient {
87
94
  >;
88
95
  }
89
96
 
97
+ async fetchLatestInboxUpdatesCount(inboxIds: string[]) {
98
+ const result = (await this.#client.fetchLatestInboxUpdatesCount(
99
+ true,
100
+ inboxIds,
101
+ )) as Map<string, number> | Record<string, number>;
102
+
103
+ return result instanceof Map ? Object.fromEntries(result) : result;
104
+ }
105
+
106
+ async fetchOwnInboxUpdatesCount() {
107
+ return this.#client.fetchOwnInboxUpdatesCount(true);
108
+ }
109
+
90
110
  async addSignature(
91
111
  signatureRequest: SignatureRequestHandle,
92
112
  signer: SafeSigner,
@@ -145,9 +165,13 @@ export class WorkerClient {
145
165
  async registerIdentity(
146
166
  signer: SafeSigner,
147
167
  signatureRequest: SignatureRequestHandle,
168
+ visibilityConfirmationOptions?: VisibilityConfirmationOptions,
148
169
  ) {
149
170
  await this.addSignature(signatureRequest, signer);
150
- await this.#client.registerIdentity(signatureRequest);
171
+ await this.#client.registerIdentity(
172
+ signatureRequest,
173
+ visibilityConfirmationOptions,
174
+ );
151
175
  }
152
176
 
153
177
  async getInboxIdByIdentifier(identifier: Identifier) {
@@ -195,4 +219,42 @@ export class WorkerClient {
195
219
  async sendSyncRequest(options: ArchiveOptions, serverUrl: string) {
196
220
  return this.#client.device_sync().sendSyncRequest(options, serverUrl);
197
221
  }
222
+
223
+ async sendSyncArchive(
224
+ options: ArchiveOptions,
225
+ serverUrl: string,
226
+ pin: string,
227
+ ) {
228
+ return this.#client.device_sync().sendSyncArchive(options, serverUrl, pin);
229
+ }
230
+
231
+ async processSyncArchive(archivePin?: string | null) {
232
+ return this.#client.device_sync().processSyncArchive(archivePin);
233
+ }
234
+
235
+ listAvailableArchives(daysCutoff: number): AvailableArchiveInfo[] {
236
+ return this.#client.device_sync().listAvailableArchives(BigInt(daysCutoff));
237
+ }
238
+
239
+ async createArchive(
240
+ opts: ArchiveOptions,
241
+ key: Uint8Array,
242
+ ): Promise<Uint8Array> {
243
+ return this.#client.device_sync().createArchive(opts, key);
244
+ }
245
+
246
+ async importArchive(data: Uint8Array, key: Uint8Array) {
247
+ return this.#client.device_sync().importArchive(data, key);
248
+ }
249
+
250
+ async archiveMetadata(
251
+ data: Uint8Array,
252
+ key: Uint8Array,
253
+ ): Promise<ArchiveMetadata> {
254
+ return this.#client.device_sync().archiveMetadata(data, key);
255
+ }
256
+
257
+ async syncAllDeviceSyncGroups(): Promise<GroupSyncSummary> {
258
+ return this.#client.device_sync().syncAllDeviceSyncGroups();
259
+ }
198
260
  }
@@ -188,6 +188,10 @@ export class WorkerConversation {
188
188
  return this.#group.publishMessages();
189
189
  }
190
190
 
191
+ async processStreamedMessage(envelopeBytes: Uint8Array) {
192
+ return this.#group.processStreamedGroupMessage(envelopeBytes);
193
+ }
194
+
191
195
  async send(encodedContent: EncodedContent, opts?: SendMessageOpts) {
192
196
  return this.#group.send(encodedContent, opts ?? { shouldPush: true });
193
197
  }
package/src/index.ts CHANGED
@@ -17,8 +17,10 @@ export type {
17
17
  Action,
18
18
  Actions,
19
19
  ApiStats,
20
+ ArchiveMetadata,
20
21
  ArchiveOptions,
21
22
  Attachment,
23
+ AvailableArchiveInfo,
22
24
  Backend,
23
25
  BackendBuilder,
24
26
  Consent,
@@ -62,6 +64,8 @@ export type {
62
64
  UserPreferenceUpdate,
63
65
  WalletCall,
64
66
  WalletSendCalls,
67
+ WorkerConfigOptions,
68
+ WorkerIntervalOverride,
65
69
  } from "@xmtp/wasm-bindings";
66
70
  export {
67
71
  ActionStyle,
@@ -85,6 +89,7 @@ export {
85
89
  ReactionAction,
86
90
  ReactionSchema,
87
91
  SortDirection,
92
+ WorkerKind,
88
93
  } from "@xmtp/wasm-bindings";
89
94
  export * from "./utils/signer";
90
95
  export * from "./utils/errors";
@@ -1,9 +1,15 @@
1
1
  import type {
2
+ ArchiveMetadata,
2
3
  ArchiveOptions,
4
+ AvailableArchiveInfo,
5
+ GroupSyncSummary,
3
6
  Identifier,
4
7
  KeyPackageStatus,
5
8
  } from "@xmtp/wasm-bindings";
6
- import type { ClientOptions } from "@/types/options";
9
+ import type {
10
+ ClientOptions,
11
+ VisibilityConfirmationOptions,
12
+ } from "@/types/options";
7
13
  import type { SafeSigner } from "@/utils/signer";
8
14
 
9
15
  export type ClientAction =
@@ -109,6 +115,7 @@ export type ClientAction =
109
115
  data: {
110
116
  signer: SafeSigner;
111
117
  signatureRequestId: string;
118
+ waitForRegistrationVisible?: VisibilityConfirmationOptions;
112
119
  };
113
120
  }
114
121
  | {
@@ -174,6 +181,20 @@ export type ClientAction =
174
181
  identifiers: Identifier[];
175
182
  };
176
183
  }
184
+ | {
185
+ action: "client.fetchLatestInboxUpdatesCount";
186
+ id: string;
187
+ result: Record<string, number>;
188
+ data: {
189
+ inboxIds: string[];
190
+ };
191
+ }
192
+ | {
193
+ action: "client.fetchOwnInboxUpdatesCount";
194
+ id: string;
195
+ result: number;
196
+ data: Record<string, never>;
197
+ }
177
198
  | {
178
199
  action: "client.getInboxIdByIdentifier";
179
200
  id: string;
@@ -225,4 +246,63 @@ export type ClientAction =
225
246
  options: ArchiveOptions;
226
247
  serverUrl: string;
227
248
  };
249
+ }
250
+ | {
251
+ action: "client.sendSyncArchive";
252
+ id: string;
253
+ result: undefined;
254
+ data: {
255
+ options: ArchiveOptions;
256
+ serverUrl: string;
257
+ pin: string;
258
+ };
259
+ }
260
+ | {
261
+ action: "client.processSyncArchive";
262
+ id: string;
263
+ result: undefined;
264
+ data: {
265
+ archivePin?: string | null;
266
+ };
267
+ }
268
+ | {
269
+ action: "client.listAvailableArchives";
270
+ id: string;
271
+ result: AvailableArchiveInfo[];
272
+ data: {
273
+ daysCutoff: number;
274
+ };
275
+ }
276
+ | {
277
+ action: "client.createArchive";
278
+ id: string;
279
+ result: Uint8Array;
280
+ data: {
281
+ opts: ArchiveOptions;
282
+ key: Uint8Array;
283
+ };
284
+ }
285
+ | {
286
+ action: "client.importArchive";
287
+ id: string;
288
+ result: undefined;
289
+ data: {
290
+ data: Uint8Array;
291
+ key: Uint8Array;
292
+ };
293
+ }
294
+ | {
295
+ action: "client.archiveMetadata";
296
+ id: string;
297
+ result: ArchiveMetadata;
298
+ data: {
299
+ data: Uint8Array;
300
+ key: Uint8Array;
301
+ };
302
+ }
303
+ | {
304
+ action: "client.syncAllDeviceSyncGroups";
305
+ id: string;
306
+ result: GroupSyncSummary;
307
+ data: undefined;
228
308
  };
@@ -8,6 +8,7 @@ import type {
8
8
  GroupMember,
9
9
  Intent,
10
10
  ListMessagesOptions,
11
+ Message,
11
12
  MessageDisappearingSettings,
12
13
  MultiRemoteAttachment,
13
14
  Reaction,
@@ -50,6 +51,15 @@ export type ConversationAction =
50
51
  id: string;
51
52
  };
52
53
  }
54
+ | {
55
+ action: "conversation.processStreamedMessage";
56
+ id: string;
57
+ result: Message[];
58
+ data: {
59
+ id: string;
60
+ envelopeBytes: Uint8Array;
61
+ };
62
+ }
53
63
  | {
54
64
  action: "conversation.messages";
55
65
  id: string;
@@ -17,9 +17,13 @@ import type {
17
17
  RemoteAttachment,
18
18
  TransactionReference,
19
19
  WalletSendCalls,
20
+ WasmVisibilityConfirmationOptions,
21
+ WorkerConfigOptions,
20
22
  } from "@xmtp/wasm-bindings";
21
23
  import type { DecodedMessage } from "@/DecodedMessage";
22
24
 
25
+ export type VisibilityConfirmationOptions = WasmVisibilityConfirmationOptions;
26
+
23
27
  export type XmtpEnv =
24
28
  | "local"
25
29
  | "dev"
@@ -115,10 +119,25 @@ export type OtherOptions = {
115
119
  * Logging level
116
120
  */
117
121
  loggingLevel?: LogLevel;
122
+ /**
123
+ * Tuning for the background worker scheduler (intervals, jitter, per-worker
124
+ * overrides, and disabled workers). All fields are optional; omitting this
125
+ * object preserves the default worker behavior.
126
+ *
127
+ * Intervals are specified in nanoseconds.
128
+ */
129
+ workerConfig?: WorkerConfigOptions;
118
130
  /**
119
131
  * Disable automatic registration when creating a client
120
132
  */
121
133
  disableAutoRegister?: boolean;
134
+ /**
135
+ * Options for waiting until client registration is visible on the network.
136
+ *
137
+ * When set, `registerIdentity` will wait for the specified quorum of nodes
138
+ * to confirm the registration before resolving.
139
+ */
140
+ waitForRegistrationVisible?: VisibilityConfirmationOptions;
122
141
  };
123
142
 
124
143
  export type ClientOptions = (NetworkOptions | { backend: Backend }) &
@@ -65,7 +65,7 @@ export class WorkerBridge<T extends UnknownAction> {
65
65
  });
66
66
  const promise = new Promise((resolve, reject) => {
67
67
  this.#promises.set(promiseId, {
68
- resolve: resolve as (value: unknown) => void,
68
+ resolve: resolve,
69
69
  reject,
70
70
  });
71
71
  });
@@ -6,9 +6,15 @@ import {
6
6
  type Backend,
7
7
  type Identifier,
8
8
  } from "@xmtp/wasm-bindings";
9
- import type { ClientOptions, NetworkOptions } from "@/types/options";
9
+ import type { ClientOptions } from "@/types/options";
10
10
  import { createBackend, envToString } from "@/utils/createBackend";
11
11
 
12
+ type CreateClientOptions = ClientOptions extends infer T
13
+ ? T extends ClientOptions
14
+ ? Omit<T, "codecs">
15
+ : never
16
+ : never;
17
+
12
18
  const networkOptionKeys = [
13
19
  "env",
14
20
  "apiUrl",
@@ -21,7 +27,7 @@ const hasBackend = (options: object): options is { backend: Backend } => {
21
27
  };
22
28
 
23
29
  const resolveBackend = async (
24
- options?: Omit<ClientOptions, "codecs">,
30
+ options?: CreateClientOptions,
25
31
  ): Promise<Backend> => {
26
32
  if (!options) {
27
33
  return createBackend();
@@ -43,12 +49,12 @@ const resolveBackend = async (
43
49
  }
44
50
 
45
51
  // No backend provided — build one from NetworkOptions
46
- return createBackend(options as NetworkOptions);
52
+ return createBackend(options);
47
53
  };
48
54
 
49
55
  export const createClient = async (
50
56
  identifier: Identifier,
51
- options?: Omit<ClientOptions, "codecs">,
57
+ options?: CreateClientOptions,
52
58
  ) => {
53
59
  const backend = await resolveBackend(options);
54
60
 
@@ -80,6 +86,7 @@ export const createClient = async (
80
86
  dbPath,
81
87
  options?.dbEncryptionKey,
82
88
  deviceSyncMode,
89
+ options?.workerConfig,
83
90
  isLogging
84
91
  ? {
85
92
  structured: options.structuredLogging ?? false,