Skip to content

Commit 7637a62

Browse files
authored
Allow passing a backend into static methods (#1724)
# Allowing passing a backend into static methods - Upgrades to a `dev` release of the Node and WASM bindings, which changes the way backend configuration is passed for all static methods - This deprecates the old way of passing configuration in to static methods `Client.someStaticMethod(env, gatewayHost, ...)` and replaces it with a new method that takes a backend. We don't break the old way, but if you use it you will see a deprecation warning in your IDE. - Makes some changes to the `ClientOptions` to allow callers to pass in a backend explicitly. This lets you re-use the same backend between static methods and instantiated clients like we do on mobile. - Deprecates the `ApiUrls` and moves those constants to Rust ## Changes ### Design Documentation - Add comprehensive design document outlining the migration strategy and backwards compatibility approach ### Bindings Updates - Upgrade `@xmtp/wasm-bindings` from 1.9.1 to 1.10.0-dev.a2bdd0a - Upgrade `@xmtp/node-bindings` from 1.10.0-dev.55280fa to 1.10.0-dev.a2bdd0a - Fix method renames: `findInboxIdByIdentifier` → `findInboxIdByIdentity`, member management method updates - Update `sendSyncRequest` to accept `ArchiveOptions` and `serverUrl` parameters ### New Backend Utilities - Add `createBackend` utility in both SDKs that wraps `BackendBuilder` with proper environment mapping - Export `Backend`, `BackendBuilder`, and `createBackend` from both SDK index files ### Type System Refactoring - Move `appVersion` from `OtherOptions` to `NetworkOptions` - Extract `DeviceSyncOptions` containing `historySyncUrl` and `disableDeviceSync` - Update `ClientOptions` to union type: `(NetworkOptions | { backend: Backend }) & DeviceSyncOptions & ...` - Add runtime validation preventing simultaneous `backend` and `NetworkOptions` usage ### Static Method Migration - Update all static methods to accept `envOrBackend?: XmtpEnv | Backend` parameter - Add `resolveBackend` helper that handles backwards compatibility - Deprecate `env` and `gatewayHost` parameters with JSDoc annotations - Methods updated: - `Client.revokeInstallations` - `Client.fetchInboxStates` - `Client.canMessage` - `Client.isAddressAuthorized` (node-sdk only) - `Client.isInstallationAuthorized` (node-sdk only) ### Client Creation Updates - Replace `createClient`/`createWasmClient` with `createClientWithBackend` - Update `getInboxIdForIdentifier` to use `Backend` instead of raw host parameters - Migrate utility functions (`inboxState`, `installations`) to accept `Backend` ### Testing - Add comprehensive tests for `createBackend` in both SDKs - Mock `BackendBuilder.build()` in node-sdk tests for Tokio runtime compatibility - Use real WASM bindings in browser-sdk tests - Update test helpers to work with new type system The migration maintains full backwards compatibility - existing code using `env` and `gatewayHost` parameters continues to work unchanged, while new code can use the more flexible `Backend` approach. <!-- Macroscope's pull request summary starts here --> <!-- Macroscope will only edit the content between these invisible markers, and the markers themselves will not be visible in the GitHub rendered markdown. --> <!-- If you delete either of the start / end markers from your PR's description, Macroscope will append its summary at the bottom of the description. --> > [!NOTE] > ### Allow passing a Backend instance into static Client methods across browser and node SDKs > - Adds a `createBackend` utility (browser and node) that constructs a typed `Backend` from env/apiUrl/gatewayHost options, and exports it from both SDKs. > - Overloads static methods (`canMessage`, `fetchInboxStates`, `revokeInstallations`, `isAddressAuthorized`, `isInstallationAuthorized`) to accept either a pre-built `Backend` or the legacy `env`+`gatewayHost` strings (now deprecated). > - Adds a `Client.env` getter so callers can read the environment after initialization; the worker response for `client.init` now includes `env`. > - Updates `sendSyncRequest` to require `(options: ArchiveOptions, serverUrl: string)`; `ConversationsNavbar` resolves `serverUrl` from `HistorySyncUrls[client.env]`. > - Expands `XmtpEnv` to an explicit string union (`local | dev | production | testnet-staging | testnet-dev | testnet | mainnet`) and introduces `DeviceSyncOptions` split from `NetworkOptions`. > - Risk: `sendSyncRequest` signature is breaking — callers that invoke it with no arguments will need to pass `options` and `serverUrl`. > > <!-- Macroscope's review summary starts here --> > > <sup><a href="https://app.macroscope.com">Macroscope</a> summarized 35cc13b.</sup> > <!-- Macroscope's review summary ends here --> > <!-- macroscope-ui-refresh --> <!-- Macroscope's pull request summary ends here -->
1 parent 26aeb9b commit 7637a62

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+894
-303
lines changed

apps/xmtp.chat/src/components/App/AppLayout.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,10 @@ export const AppLayout: React.FC = () => {
4343
}, [client]);
4444

4545
useEffect(() => {
46-
if (!client) {
46+
const sessionSdkEnv = client?.env;
47+
if (!client || !sessionSdkEnv) {
4748
return;
4849
}
49-
50-
// the session's actual SDK environment from client options
51-
const sessionSdkEnv = client.options?.env ?? "dev";
5250
const isInvalidEnvironment =
5351
!envParam ||
5452
!isValidEnvironment(envParam) ||

apps/xmtp.chat/src/components/Conversations/ConversationsNavbar.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
import { Badge, Box, Group, Stack, Text } from "@mantine/core";
2+
import {
3+
BackupElementSelectionOption,
4+
HistorySyncUrls,
5+
} from "@xmtp/browser-sdk";
26
import { useCallback, useEffect, useRef } from "react";
37
import { ConversationsList } from "@/components/Conversations/ConversationList";
48
import { ConversationsMenu } from "@/components/Conversations/ConversationsMenu";
@@ -48,7 +52,21 @@ export const ConversationsNavbar: React.FC = () => {
4852
}, [syncAll, startStreams, stopStreams]);
4953

5054
const handleSendSyncRequest = useCallback(async () => {
51-
await client.sendSyncRequest();
55+
const env = client.env;
56+
if (!env) {
57+
return;
58+
}
59+
const serverUrl = HistorySyncUrls[env];
60+
await client.sendSyncRequest(
61+
{
62+
elements: [
63+
BackupElementSelectionOption.Messages,
64+
BackupElementSelectionOption.Consent,
65+
],
66+
excludeDisappearingMessages: false,
67+
},
68+
serverUrl,
69+
);
5270
}, [client]);
5371

5472
// loading conversations on mount, and start streaming

apps/xmtp.chat/src/contexts/XMTPContext.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import {
22
Client,
33
type BuiltInContentTypes,
44
type ClientOptions,
5+
type NetworkOptions,
56
type Signer,
7+
type XmtpEnv,
68
} from "@xmtp/browser-sdk";
79
import {
810
createContext,
@@ -20,7 +22,7 @@ export type ContentTypes = BuiltInContentTypes;
2022
export type InitializeClientOptions = {
2123
apiUrl?: string;
2224
dbEncryptionKey?: Uint8Array;
23-
env?: ClientOptions["env"];
25+
env?: XmtpEnv;
2426
loggingLevel?: ClientOptions["loggingLevel"];
2527
signer: Signer;
2628
gatewayHost?: string;
@@ -122,7 +124,7 @@ export const XMTPProvider: React.FC<XMTPProviderProps> = ({
122124
dbEncryptionKey,
123125
appVersion: "xmtp.chat/0",
124126
gatewayHost,
125-
});
127+
} as Omit<ClientOptions & NetworkOptions, "codecs">);
126128
setClient(xmtpClient);
127129
} catch (e) {
128130
setClient(undefined);

apps/xmtp.chat/src/helpers/attachment.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ export const getFileType = (filename: string) => {
146146
}
147147
};
148148

149-
export const formatFileSize = (fileSize: number) => {
149+
export const formatFileSize = (fileSize: number | undefined) => {
150150
if (!fileSize) return "";
151151
const kb = fileSize / 1024;
152152
if (kb < 1024) {

apps/xmtp.chat/src/helpers/names.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
createBackend,
23
getInboxIdForIdentifier,
34
IdentifierKind,
45
type XmtpEnv,
@@ -84,14 +85,11 @@ export const getInboxIdForAddress = async (
8485
return null;
8586
}
8687

87-
const inboxId = await getInboxIdForIdentifier(
88-
{
89-
identifier: address.toLowerCase(),
90-
identifierKind: IdentifierKind.Ethereum,
91-
},
92-
environment,
93-
gatewayHost,
94-
);
88+
const backend = await createBackend({ env: environment, gatewayHost });
89+
const inboxId = await getInboxIdForIdentifier(backend, {
90+
identifier: address.toLowerCase(),
91+
identifierKind: IdentifierKind.Ethereum,
92+
});
9593

9694
return inboxId ?? null;
9795
};

content-types/content-type-primitives/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
]
6363
},
6464
"dependencies": {
65-
"@xmtp/node-bindings": "1.9.1"
65+
"@xmtp/node-bindings": "1.10.0-dev.2334796"
6666
},
6767
"devDependencies": {
6868
"@rollup/plugin-terser": "^0.4.4",

packages/xmtp-cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888
"@oclif/plugin-help": "^6.2.37",
8989
"@oclif/plugin-not-found": "^3.2.74",
9090
"@oclif/plugin-warn-if-update-available": "^3.1.55",
91-
"@xmtp/node-sdk": "5.3.0",
91+
"@xmtp/node-sdk": "5.4.0",
9292
"viem": "^2.44.4"
9393
},
9494
"devDependencies": {

packages/xmtp-cli/src/commands/client/info.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ application instance using your identity.`;
3434
async run(): Promise<void> {
3535
const client = await this.initClient();
3636

37-
const options = client.options ?? {};
37+
const options = client.options;
38+
const hasNetworkOptions = options && "env" in options;
3839

3940
const properties = {
4041
address: client.accountIdentifier?.identifier,
@@ -46,17 +47,17 @@ application instance using your identity.`;
4647
};
4748

4849
const clientOptions = {
49-
env: options.env,
50-
apiUrl: options.apiUrl,
51-
historySyncUrl: options.historySyncUrl,
52-
gatewayHost: options.gatewayHost,
53-
dbPath: options.dbPath,
54-
loggingLevel: options.loggingLevel,
55-
structuredLogging: options.structuredLogging,
56-
disableAutoRegister: options.disableAutoRegister,
57-
disableDeviceSync: options.disableDeviceSync,
58-
appVersion: options.appVersion,
59-
nonce: options.nonce,
50+
env: client.env,
51+
apiUrl: hasNetworkOptions ? options.apiUrl : undefined,
52+
historySyncUrl: options?.historySyncUrl,
53+
gatewayHost: hasNetworkOptions ? options.gatewayHost : undefined,
54+
dbPath: options?.dbPath,
55+
loggingLevel: options?.loggingLevel,
56+
structuredLogging: options?.structuredLogging,
57+
disableAutoRegister: options?.disableAutoRegister,
58+
disableDeviceSync: options?.disableDeviceSync,
59+
appVersion: hasNetworkOptions ? options.appVersion : undefined,
60+
nonce: options?.nonce,
6061
};
6162

6263
if (this.jsonOutput) {

packages/xmtp-cli/src/utils/client.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { mkdir } from "node:fs/promises";
22
import { dirname } from "node:path";
3-
import { Client, IdentifierKind, LogLevel, type Signer } from "@xmtp/node-sdk";
3+
import {
4+
Client,
5+
IdentifierKind,
6+
LogLevel,
7+
type NetworkOptions,
8+
type Signer,
9+
} from "@xmtp/node-sdk";
410
import { isHex, toBytes } from "viem";
511
import { privateKeyToAccount } from "viem/accounts";
612
import type { XmtpConfig } from "./config.js";
@@ -79,15 +85,19 @@ export async function createClient(config: XmtpConfig): Promise<Client> {
7985
await mkdir(dirname(config.dbPath), { recursive: true });
8086
}
8187

82-
const client = await Client.create(signer, {
88+
const networkOptions: NetworkOptions = {
8389
env: config.env,
90+
gatewayHost: config.gatewayHost,
91+
appVersion: config.appVersion,
92+
};
93+
94+
const client = await Client.create(signer, {
95+
...networkOptions,
8496
dbEncryptionKey: hexToBytes(config.dbEncryptionKey),
8597
dbPath: config.dbPath ?? undefined,
86-
gatewayHost: config.gatewayHost,
8798
loggingLevel: parseLogLevel(config.logLevel),
8899
structuredLogging: config.structuredLogging,
89100
disableDeviceSync: config.disableDeviceSync,
90-
appVersion: config.appVersion,
91101
});
92102

93103
return client;

sdks/agent-sdk/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
},
6767
"dependencies": {
6868
"@xmtp/content-type-primitives": "3.0.1",
69-
"@xmtp/node-sdk": "5.3.0",
69+
"@xmtp/node-sdk": "5.4.0",
7070
"viem": "^2.37.6"
7171
},
7272
"devDependencies": {

0 commit comments

Comments
 (0)