Allow passing a backend into static methods#1724
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
760a170 to
220177f
Compare
220177f to
83fad46
Compare
83fad46 to
d42805b
Compare
d42805b to
6a3df6d
Compare
9d1cf83 to
1266dba
Compare
10f80eb to
e258bdd
Compare
1266dba to
6e4a641
Compare
| ], | ||
| excludeDisappearingMessages: false, | ||
| }, | ||
| serverUrl, |
There was a problem hiding this comment.
Thanks for making these updates
55ec173 to
8a24441
Compare
Merge activity
|
Documents the plan to migrate SDK static methods and Client.create/build to use the new Backend/BackendBuilder abstraction from the bindings, while maintaining backwards compatibility via deprecated env/gatewayHost params. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
8a24441 to
d08c9bd
Compare
d08c9bd to
d935c60
Compare
Update @xmtp/node-bindings and @xmtp/wasm-bindings from 1.10.0-dev.82e8ff4 to 1.10.0-dev.2334796 across node-sdk, browser-sdk, and content-type-primitives. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
d935c60 to
35cc13b
Compare
| const inboxId = await getInboxIdForIdentifier(identifier, env); | ||
| const inboxId = await getInboxIdForIdentifier(backend, identifier); | ||
| canMessageMap.set(identifier.identifier.toLowerCase(), inboxId !== null); | ||
| } |
There was a problem hiding this comment.
🟠 High src/Client.ts:851
Client.canMessage uses inboxId !== null to check if an identity exists, but getInboxIdForIdentifier returns undefined when not found. Since undefined !== null is true, the method incorrectly reports that non-existent users can be messaged. Consider using inboxId != null or !!inboxId to handle both null and undefined.
- canMessageMap.set(identifier.identifier.toLowerCase(), inboxId !== null);
+ canMessageMap.set(identifier.identifier.toLowerCase(), inboxId != null);Also found in 1 other location(s)
sdks/browser-sdk/src/utils/createClient.ts:19
The
hasBackendfunction incorrectly identifies an object as having a valid backend if thebackendproperty exists but is set toundefined(e.g.,{ backend: undefined }). This occurs because the function relies solely on theinoperator, which returns true for keys with undefined values.
This creates two bugs:
- If a caller passes
{ backend: undefined },resolveBackendreturnsundefinedinstead of creating a default backend. This causescreateClientto crash downstream whenundefinedis passed togetInboxIdForIdentifier. - If a caller passes
{ backend: undefined, env: 'production' }, the function incorrectly detects a conflict between a 'backend' and network options, throwing an error instead of creating the backend using the provided options.
Update hasBackend to check that options.backend is not null or undefined.
🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file sdks/node-sdk/src/Client.ts around line 851:
`Client.canMessage` uses `inboxId !== null` to check if an identity exists, but `getInboxIdForIdentifier` returns `undefined` when not found. Since `undefined !== null` is `true`, the method incorrectly reports that non-existent users can be messaged. Consider using `inboxId != null` or `!!inboxId` to handle both `null` and `undefined`.
Evidence trail:
sdks/node-sdk/src/Client.ts line 850: `canMessageMap.set(identifier.identifier.toLowerCase(), inboxId !== null);` - uses strict inequality with null. sdks/node-sdk/src/utils/inboxId.ts lines 15-19: `getInboxIdForIdentifier` returns result from `getInboxIdByIdentity`. sdks/node-sdk/test/inboxId.test.ts lines 20-25: Test explicitly named "should return `undefined` inbox ID for unregistered address" confirms the function returns `undefined` (not `null`) when identity not found.
Also found in 1 other location(s):
- sdks/browser-sdk/src/utils/createClient.ts:19 -- The `hasBackend` function incorrectly identifies an object as having a valid backend if the `backend` property exists but is set to `undefined` (e.g., `{ backend: undefined }`). This occurs because the function relies solely on the `in` operator, which returns true for keys with undefined values.
This creates two bugs:
1. If a caller passes `{ backend: undefined }`, `resolveBackend` returns `undefined` instead of creating a default backend. This causes `createClient` to crash downstream when `undefined` is passed to `getInboxIdForIdentifier`.
2. If a caller passes `{ backend: undefined, env: 'production' }`, the function incorrectly detects a conflict between a 'backend' and network options, throwing an error instead of creating the backend using the provided options.
Update `hasBackend` to check that `options.backend` is not null or undefined.
There was a problem hiding this comment.
🟡 Medium src/Client.ts:843
Client.canMessage returns a Map where keys are the lowercased identifier strings (identifier.identifier.toLowerCase()). This breaks lookups when callers use the original casing — for example, a checksummed Ethereum address like 0xAbC stored as 0xabc will cause map.get("0xAbC") to return undefined instead of the correct boolean. Consider using the original identifier as the map key.
- const canMessageMap = new Map<string, boolean>();
- for (const identifier of identifiers) {
- const inboxId = await getInboxIdForIdentifier(backend, identifier);
- canMessageMap.set(identifier.identifier.toLowerCase(), inboxId !== null);
- }
- return canMessageMap;
+ const canMessageMap = new Map<string, boolean>();
+ for (const identifier of identifiers) {
+ const inboxId = await getInboxIdForIdentifier(backend, identifier);
+ canMessageMap.set(identifier.identifier, inboxId !== null);
+ }
+ return canMessageMap;🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file sdks/node-sdk/src/Client.ts around lines 843-854:
`Client.canMessage` returns a Map where keys are the lowercased identifier strings (`identifier.identifier.toLowerCase()`). This breaks lookups when callers use the original casing — for example, a checksummed Ethereum address like `0xAbC` stored as `0xabc` will cause `map.get("0xAbC")` to return `undefined` instead of the correct boolean. Consider using the original identifier as the map key.
Evidence trail:
sdks/node-sdk/src/Client.ts:850 - canMessageMap.set(identifier.identifier.toLowerCase(), inboxId !== null) shows the key is lowercased
sdks/node-sdk/test/helpers.ts:53-54 - createIdentifier explicitly lowercases: identifier: user.account.address.toLowerCase()
sdks/node-sdk/test/helpers.ts:71 - createSigner returns address: user.account.address.toLowerCase()
sdks/node-sdk/test/Client.test.ts:130-132 - Test uses already-lowercased addresses so doesn't catch the bug
| const hasBackend = (options: object): options is { backend: Backend } => { | ||
| return "backend" in options; | ||
| }; |
There was a problem hiding this comment.
🟡 Medium utils/createClient.ts:19
hasBackend returns true when backend is explicitly set to undefined, causing resolveBackend to return undefined instead of creating a backend. The caller in createClient then crashes when accessing backend.env. Consider checking that options.backend is truthy, not just that the key exists.
+const hasBackend = (options: object): options is { backend: Backend } => {
+ return "backend" in options && (options as Record<string, unknown>).backend !== undefined;
+};🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file sdks/browser-sdk/src/utils/createClient.ts around lines 19-21:
`hasBackend` returns true when `backend` is explicitly set to `undefined`, causing `resolveBackend` to return `undefined` instead of creating a backend. The caller in `createClient` then crashes when accessing `backend.env`. Consider checking that `options.backend` is truthy, not just that the key exists.
Evidence trail:
sdks/browser-sdk/src/utils/createClient.ts lines 18-20 (hasBackend uses `"backend" in options` which checks key existence), lines 36-38 (resolveBackend returns `options.backend` when hasBackend returns true), line 49 (createClient receives backend from resolveBackend), line 55 (crashes on `backend.env` when backend is undefined). Verified at commit REVIEWED_COMMIT.

Allowing passing a backend into static methods
devrelease of the Node and WASM bindings, which changes the way backend configuration is passed for all static methodsClient.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.ClientOptionsto 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.ApiUrlsand moves those constants to RustChanges
Design Documentation
Bindings Updates
@xmtp/wasm-bindingsfrom 1.9.1 to 1.10.0-dev.a2bdd0a@xmtp/node-bindingsfrom 1.10.0-dev.55280fa to 1.10.0-dev.a2bdd0afindInboxIdByIdentifier→findInboxIdByIdentity, member management method updatessendSyncRequestto acceptArchiveOptionsandserverUrlparametersNew Backend Utilities
createBackendutility in both SDKs that wrapsBackendBuilderwith proper environment mappingBackend,BackendBuilder, andcreateBackendfrom both SDK index filesType System Refactoring
appVersionfromOtherOptionstoNetworkOptionsDeviceSyncOptionscontaininghistorySyncUrlanddisableDeviceSyncClientOptionsto union type:(NetworkOptions | { backend: Backend }) & DeviceSyncOptions & ...backendandNetworkOptionsusageStatic Method Migration
envOrBackend?: XmtpEnv | BackendparameterresolveBackendhelper that handles backwards compatibilityenvandgatewayHostparameters with JSDoc annotationsClient.revokeInstallationsClient.fetchInboxStatesClient.canMessageClient.isAddressAuthorized(node-sdk only)Client.isInstallationAuthorized(node-sdk only)Client Creation Updates
createClient/createWasmClientwithcreateClientWithBackendgetInboxIdForIdentifierto useBackendinstead of raw host parametersinboxState,installations) to acceptBackendTesting
createBackendin both SDKsBackendBuilder.build()in node-sdk tests for Tokio runtime compatibilityThe migration maintains full backwards compatibility - existing code using
envandgatewayHostparameters continues to work unchanged, while new code can use the more flexibleBackendapproach.Note
Allow passing a Backend instance into static Client methods across browser and node SDKs
createBackendutility (browser and node) that constructs a typedBackendfrom env/apiUrl/gatewayHost options, and exports it from both SDKs.canMessage,fetchInboxStates,revokeInstallations,isAddressAuthorized,isInstallationAuthorized) to accept either a pre-builtBackendor the legacyenv+gatewayHoststrings (now deprecated).Client.envgetter so callers can read the environment after initialization; the worker response forclient.initnow includesenv.sendSyncRequestto require(options: ArchiveOptions, serverUrl: string);ConversationsNavbarresolvesserverUrlfromHistorySyncUrls[client.env].XmtpEnvto an explicit string union (local | dev | production | testnet-staging | testnet-dev | testnet | mainnet) and introducesDeviceSyncOptionssplit fromNetworkOptions.sendSyncRequestsignature is breaking — callers that invoke it with no arguments will need to passoptionsandserverUrl.Macroscope summarized 35cc13b.