From 38575fb2d8485af397921f1b42eb9dafe739a8d2 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 9 Feb 2026 02:31:52 +0000 Subject: [PATCH] fix: replace static whatsapp-rust-bridge imports with lazy dynamic loading The whatsapp-rust-bridge package contains a top-level await on an inline WASM binary, which propagates through the ESM graph and causes ERR_REQUIRE_ASYNC_MODULE when CJS consumers try to require() this package. Replace all static imports from whatsapp-rust-bridge with a lazy-loading bridge module (wasm-bridge.ts) that uses import().then() instead of top-level await, keeping the dependency out of the static ESM graph. https://claude.ai/code/session_01XaA7GwNaB6azTHFYQ8WEpB --- src/Utils/chat-utils.ts | 4 ++-- src/Utils/crypto.ts | 2 +- src/Utils/index.ts | 1 + src/Utils/lt-hash.ts | 4 ++-- src/Utils/wasm-bridge.ts | 40 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 46 insertions(+), 5 deletions(-) create mode 100644 src/Utils/wasm-bridge.ts diff --git a/src/Utils/chat-utils.ts b/src/Utils/chat-utils.ts index d35ec93a..ff872a26 100644 --- a/src/Utils/chat-utils.ts +++ b/src/Utils/chat-utils.ts @@ -19,7 +19,7 @@ import { type MessageLabelAssociation } from '../Types/LabelAssociation' import { type BinaryNode, getBinaryNodeChild, getBinaryNodeChildren, isJidGroup, jidNormalizedUser } from '../WABinary' -import { expandAppStateKeys } from 'whatsapp-rust-bridge' +import { expandAppStateKeys } from './wasm-bridge' import { aesDecrypt, aesEncrypt, hmacSign } from './crypto' import { toNumber } from './generics' import type { ILogger } from './logger' @@ -102,7 +102,7 @@ const makeLtHashGenerator = ({ indexValueMap, hash }: Pick { - const result = LT_HASH_ANTI_TAMPERING.subtractThenAdd(new Uint8Array(hash), addBuffs, subBuffs) + const result = LT_HASH_ANTI_TAMPERING().subtractThenAdd(new Uint8Array(hash), addBuffs, subBuffs) return { hash: Buffer.from(result), diff --git a/src/Utils/crypto.ts b/src/Utils/crypto.ts index 9152b01f..982828a5 100644 --- a/src/Utils/crypto.ts +++ b/src/Utils/crypto.ts @@ -118,7 +118,7 @@ export function sha256(buffer: Buffer) { return createHash('sha256').update(buffer).digest() } -export { hkdf, md5 } from 'whatsapp-rust-bridge' +export { hkdf, md5 } from './wasm-bridge' export async function derivePairingCodeKey(pairingCode: string, salt: Buffer): Promise { // Convert inputs to formats Web Crypto API can work with diff --git a/src/Utils/index.ts b/src/Utils/index.ts index 0b376f3d..2c8e793b 100644 --- a/src/Utils/index.ts +++ b/src/Utils/index.ts @@ -9,6 +9,7 @@ export * from './noise-handler' export * from './history' export * from './chat-utils' export * from './lt-hash' +export { wasmBridgeReady } from './wasm-bridge' export * from './auth-utils' export * from './use-multi-file-auth-state' export * from './link-preview' diff --git a/src/Utils/lt-hash.ts b/src/Utils/lt-hash.ts index 9bae3362..8da2a1b6 100644 --- a/src/Utils/lt-hash.ts +++ b/src/Utils/lt-hash.ts @@ -1,3 +1,3 @@ -import { LTHashAntiTampering } from 'whatsapp-rust-bridge' +import { getLTHashAntiTampering } from './wasm-bridge' -export const LT_HASH_ANTI_TAMPERING = new LTHashAntiTampering() +export { getLTHashAntiTampering as LT_HASH_ANTI_TAMPERING } diff --git a/src/Utils/wasm-bridge.ts b/src/Utils/wasm-bridge.ts new file mode 100644 index 00000000..1c603907 --- /dev/null +++ b/src/Utils/wasm-bridge.ts @@ -0,0 +1,40 @@ +type WasmBridgeModule = typeof import('whatsapp-rust-bridge') + +let _bridge: WasmBridgeModule | undefined + +// Start loading eagerly using .then() instead of top-level await. +// This prevents the whatsapp-rust-bridge top-level await from propagating +// through the ESM graph, which would break CJS require() consumers. +const _bridgeReady = import('whatsapp-rust-bridge').then(m => { + _bridge = m + return m +}) + +export { _bridgeReady as wasmBridgeReady } + +function getBridge(): WasmBridgeModule { + if (!_bridge) { + throw new Error( + 'whatsapp-rust-bridge not yet loaded. ' + + 'Ensure async operations have started before calling crypto functions.' + ) + } + + return _bridge +} + +export const hkdf: WasmBridgeModule['hkdf'] = (...args) => getBridge().hkdf(...args) + +export const md5: WasmBridgeModule['md5'] = (...args) => getBridge().md5(...args) + +export const expandAppStateKeys: WasmBridgeModule['expandAppStateKeys'] = (...args) => getBridge().expandAppStateKeys(...args) + +let _ltHash: InstanceType | undefined + +export function getLTHashAntiTampering(): InstanceType { + if (!_ltHash) { + _ltHash = new (getBridge().LTHashAntiTampering)() + } + + return _ltHash +}