whatsapp-rust-bridge
Version:
A high-performance utilities for WhatsApp, powered by Rust and WebAssembly.
1,299 lines (1,120 loc) • 101 kB
TypeScript
/* tslint:disable */
/* eslint-disable */
/**
* The `ReadableStreamType` enum.
*
* *This API requires the following crate features to be activated: `ReadableStreamType`*
*/
export type ReadableStreamType = "bytes";
/**
* JS HTTP client callbacks. Implement using fetch() or any HTTP library.
*/
export interface JsHttpClientConfig {
execute(url: string, method: string, headers: Record<string, string>, body: Uint8Array | null): Promise<{ statusCode: number; body: Uint8Array }>;
}
/**
* JS transport callbacks for WebSocket management.
*
* Passed to `createWhatsAppClient` as the transport config.
*
* `connect(handle)` is called when the client needs a connection:
* - Create a WebSocket
* - Wire ws.onopen → handle.onConnected()
* - Wire ws.onmessage → handle.onData(data)
* - Wire ws.onclose → handle.onDisconnected()
*
* `send(data)` sends raw bytes over the active WebSocket.
* `disconnect()` closes the WebSocket.
*/
export interface JsTransportHandle {
onConnected(): void;
onData(data: Uint8Array): void;
onDisconnected(): void;
}
export interface JsTransportCallbacks {
connect(handle: JsTransportHandle): void | Promise<void>;
send(data: Uint8Array): void | Promise<void>;
disconnect(): void | Promise<void>;
}
/**
* Native crypto callbacks installed via `initWasmEngine`.
* Pass `makeNativeCryptoProvider()` from the baileyrs host wrapper.
*/
export interface JsCryptoCallbacks {
aesCbc256Encrypt(key: Uint8Array, iv: Uint8Array, plaintext: Uint8Array): Uint8Array;
aesCbc256Decrypt(key: Uint8Array, iv: Uint8Array, ciphertext: Uint8Array): Uint8Array;
aesGcm256Encrypt(key: Uint8Array, nonce: Uint8Array, aad: Uint8Array, plaintext: Uint8Array): Uint8Array;
aesGcm256Decrypt(key: Uint8Array, nonce: Uint8Array, aad: Uint8Array, ciphertextWithTag: Uint8Array): Uint8Array;
hmacSha256(key: Uint8Array, data: Uint8Array): Uint8Array;
}
/**
* Participant info as carried inside an event-time `GroupNotificationAction`.
* Distinct from `GroupMetadataParticipant` (returned by `getGroupMetadata`,
* which carries stringified `jid`/`phoneNumber` plus `isAdmin`).
*/
export interface GroupParticipantInfo {
jid: Jid;
phone_number?: Jid | null;
}
/**
* Server-pushed MEX (GraphQL) update, e.g.
* `NotificationUserReachoutTimelockUpdate`. Routed by `op_name`.
*/
export interface MexNotification {
op_name: string;
from?: Jid | null;
stanza_id?: string | null;
offline: boolean;
payload: Record<string, unknown>;
}
/** GraphQL error returned inside a MEX response's `errors` array. */
export interface MexGraphQLError {
message: string;
extensions?: {
error_code?: number | null;
severity?: string | null;
is_retryable?: boolean | null;
} | null;
}
/** Response from `mexQuery`. `data` is the GraphQL payload (op-specific). */
export interface MexResponse {
data: Record<string, unknown> | null;
errors: MexGraphQLError[] | null;
}
/** One audio codec advertised inside a call `<offer>` child. */
export interface CallAudioCodec {
enc: string;
rate: number;
}
/**
* Lifecycle action carried inside an inbound `<call>` stanza. The discriminant
* (`type`) matches the stanza child name; `pre_accept` is the snake_case form
* of `<pre-accept>`.
*/
export type CallAction =
| {
type: "offer";
call_id: string;
call_creator: Jid;
caller_pn?: Jid | null;
caller_country_code?: string | null;
device_class?: string | null;
joinable: boolean;
is_video: boolean;
audio: CallAudioCodec[];
}
| { type: "pre_accept"; call_id: string; call_creator: Jid }
| { type: "accept"; call_id: string; call_creator: Jid }
| { type: "reject"; call_id: string; call_creator: Jid }
| {
type: "terminate";
call_id: string;
call_creator: Jid;
duration?: number | null;
audio_duration?: number | null;
};
/**
* Inbound `<call>` stanza parsed into a typed event. `timestamp` is unix
* seconds (not ISO) because the core serializes via
* `chrono::serde::ts_seconds`.
*/
export interface IncomingCall {
from: Jid;
/** Stanza-level `id`; distinct from `CallAction.call_id`. */
stanza_id: string;
notify?: string | null;
platform?: string | null;
version?: string | null;
timestamp: number;
offline: boolean;
action: CallAction;
}
/** WhatsApp JID (Jabber ID) — identifies a user, group, or device. */
export interface Jid {
user: string;
server: string;
agent: number;
device: number;
integrator: number;
}
/** Addressing mode for a group (phone number vs LID). */
export type AddressingMode = "pn" | "lid";
/** App state synchronization key for WhatsApp's app state protocol. */
export interface AppStateSyncKey {
key_data: Uint8Array;
fingerprint: Uint8Array;
timestamp: number;
}
export interface ArchiveUpdate {
/** The chat being archived or unarchived. */
jid: Jid;
timestamp: number;
action: ArchiveChatAction;
from_full_sync: boolean;
}
/** Action to perform on a blocklist entry. */
export type BlocklistAction = "block" | "unblock";
export type BotEditType = "first" | "inner" | "last";
export type BusinessHourMode = "open_24h" | "specific_hours" | "appointment_only" | string;
/** Parsed `<notification type="business">` stanza. */
export interface BusinessNotification {
from: Jid;
stanza_id: string;
timestamp: number;
notification_type: BusinessNotificationType;
jid?: Jid | null;
hash?: string | null;
verified_name?: VerifiedName | null;
product_ids: string[];
collection_ids: string[];
subscriptions: BusinessSubscription[];
}
/** Business notification type based on child element. */
export type BusinessNotificationType = "remove_jid" | "remove_hash" | "verified_name_jid" | "verified_name_hash" | "profile" | "profile_hash" | "product" | "collection" | "subscriptions" | "unknown";
/** Business status update notification. */
export interface BusinessStatusUpdate {
/** The business account whose status changed. */
jid: Jid;
update_type: BusinessUpdateType;
timestamp: number;
target_jid?: Jid | null;
hash?: string | null;
verified_name?: string | null;
product_ids: string[];
collection_ids: string[];
subscriptions: BusinessSubscription[];
}
/** Business subscription information (SMB features). */
export interface BusinessSubscription {
id: string;
status: string;
expiration_date?: number | null;
creation_time?: number | null;
}
/** Type of business status update. */
export type BusinessUpdateType = "removed_as_business" | "verified_name_changed" | "profile_updated" | "products_updated" | "collections_updated" | "subscriptions_updated" | "unknown";
/** Minimal cached form of a Noise certificate. Mirrors the JSON shape WA Web persists in `waNoiseInfo.certificateChainBuffer` (only `key` plus the validity window — signatures and issuer_serial are intentionally dropped). */
export interface CachedNoiseCert {
/** 32-byte X25519 public key from `NoiseCertificate.Details.key`. */
key: any;
/** Unix epoch seconds. Validation window from `NoiseCertificate.Details`. */
not_before: number;
not_after: number;
}
/** Cached form of the server's two-cert chain. `leaf.key` is the server static public key consumed by Noise IK; the intermediate is kept solely to mirror WA Web's expiry checks. */
export interface CachedServerCertChain {
intermediate: CachedNoiseCert;
leaf: CachedNoiseCert;
}
/** Fields kept per-variant (not a shared `BasicCallMeta`) so the `serde` shape mirrors the stanza 1:1 for downstream JS consumers. */
export type CallAction =
| { type: "offer"; call_id: string; call_creator: Jid; caller_pn?: Jid | null; caller_country_code?: string | null; device_class?: string | null; joinable: boolean; is_video: boolean; audio: CallAudioCodec[]; group_jid?: Jid | null }
| { type: "offer_notice"; call_id: string; call_creator: Jid; is_video: boolean; is_group: boolean }
| { type: "pre_accept"; call_id: string; call_creator: Jid }
| { type: "accept"; call_id: string; call_creator: Jid }
| { type: "reject"; call_id: string; call_creator: Jid }
| { type: "terminate"; call_id: string; call_creator: Jid; duration?: number | null; audio_duration?: number | null };
export interface CallAudioCodec {
enc: string;
rate: number;
}
/** Identifies a specific message within a chat. */
export interface ChatMessageId {
chat: Jid;
id: string;
}
export type ChatPresence = "composing" | "paused";
export type ChatPresenceMedia = "" | "audio";
export interface ChatPresenceUpdate {
source: MessageSource;
state: ChatPresence;
media: ChatPresenceMedia;
}
/** Chat state type for typing indicators. */
export type ChatStateType = "composing" | "recording" | "paused";
export interface ConnectFailure {
reason: ConnectFailureReason;
message: string;
raw?: any | null;
}
/** Wire codes: 400=Generic, 401=LoggedOut, 402=TempBanned, 403=AccountLocked, 406=UnknownLogout, 405=ClientOutdated, 409=BadUserAgent, 413=CatExpired, 414=CatInvalid, 415=NotFound, 418=ClientUnknown, 500=InternalServerError, 501=Experimental, 503=ServiceUnavailable */
export type ConnectFailureReason = number;
/** A contact changed their phone number. Emitted from `<notification type="contacts"><modify old="..." new="..." old_lid="..." new_lid="..."/>`. The library updates the global LID-PN cache when both `old_lid` and `new_lid` are present, mirroring `WAWebDBCreateLidPnMappings`. No Signal session is wiped (WA Web `WAWebHandleContactNotification` also leaves sessions intact). Group participant updates arrive via separate `w:gp2` notifications, so per-group caches are not touched here. Consumers can subscribe and refresh their own caches if needed. */
export interface ContactNumberChanged {
/** Old phone number JID. */
old_jid: Jid;
/** New phone number JID. */
new_jid: Jid;
/** Old LID (if provided by server). */
old_lid?: Jid | null;
/** New LID (if provided by server). */
new_lid?: Jid | null;
timestamp: number;
}
/** Server requests a full contact re-sync. Emitted from `<notification type="contacts"><sync after="..."/>`. */
export interface ContactSyncRequested {
after?: number | null;
timestamp: number;
}
export interface ContactUpdate {
/** The chat/contact this sync action applies to. */
jid: Jid;
timestamp: number;
action: ContactAction;
from_full_sync: boolean;
}
/** A contact's profile changed (server notification). Emitted from `<notification type="contacts"><update jid="..."/>`. WA Web resets cached presence and refreshes the profile picture on this event — consumers should invalidate any cached presence/profile data. Not to be confused with [`ContactUpdate`] which comes from app-state sync mutations (different source, different payload). */
export interface ContactUpdated {
/** The contact whose profile was updated. */
jid: Jid;
timestamp: number;
}
export type DayOfWeek = "sun" | "mon" | "tue" | "wed" | "thu" | "fri" | "sat" | string;
export type DecryptFailMode = "show" | "hide";
export interface DeleteChatUpdate {
/** The chat being deleted. */
jid: Jid;
/** From the index, not the proto — DeleteChatAction only has messageRange. */
delete_media: boolean;
timestamp: number;
action: DeleteChatAction;
from_full_sync: boolean;
}
export interface DeleteMessageForMeUpdate {
/** The chat containing the deleted message. */
chat_jid: Jid;
participant_jid?: Jid | null;
message_id: string;
from_me: boolean;
timestamp: number;
action: DeleteMessageForMeAction;
from_full_sync: boolean;
}
export interface Device {
pn?: Jid | null;
lid?: Jid | null;
registration_id: number;
noise_key: KeyPair;
identity_key: KeyPair;
signed_pre_key: KeyPair;
signed_pre_key_id: number;
signed_pre_key_signature: any;
adv_secret_key: any;
account?: AdvSignedDeviceIdentity | null;
push_name: string;
app_version_primary: number;
app_version_secondary: number;
app_version_tertiary: number;
app_version_last_fetched_ms: number;
device_props: DeviceProps;
/** Runtime-only. Set before `connect()` on every process start. */
client_profile: ClientProfile;
/** Edge routing info received from server, used for optimized reconnection. When present, this should be sent as a pre-intro before the Noise handshake. */
edge_routing_info?: Uint8Array | null;
/** Hash from the last props (A/B experiment config) fetch. Sent on subsequent connects to enable delta updates instead of full fetches. */
props_hash?: string | null;
/** Monotonically increasing counter for one-time pre-key ID generation. Matches WhatsApp Web's `NEXT_PK_ID` pattern: only increases, never resets. Prevents prekey ID collisions when prekeys are consumed non-sequentially. */
next_pre_key_id: number;
/** Persisted flag matching WA Web's `signal_sever_has_pre_keys` metadata. */
server_has_prekeys: boolean;
/** NCT salt provisioned by the server via app state sync or history sync. */
nct_salt?: Uint8Array | null;
/** Runtime-only marker that an authoritative nct_salt_sync mutation was seen. This prevents stale history sync data from resurrecting a cleared salt. */
nct_salt_sync_seen: boolean;
/** Server cert chain cached from the last successful XX (or XX-fallback) handshake. Enables Noise IK on the next connect by exposing `leaf.key` as the server's static public key, and lets us reject stale entries via `not_after` before even attempting IK. `None` forces XX on the next connect. */
server_cert_chain?: CachedServerCertChain | null;
/** Login counter sent as `ClientPayload.lc` on every login. WA Web's `WAWebUserPrefsGeneral.getLoginCounter()` reads (and bumps) this from localStorage on each connect; the server uses it as an anti-abuse signal. Persisted so it survives restarts. */
login_counter: number;
}
/** Device element from notification. Wire format: ```xml <device jid="185169143189667:75@lid" key-index="2" lid="..."/> ``` Device ID is extracted from the JID's device part (e.g., 75 from "user:75@lid"). Per WhatsApp Web: if both `jid` and `lid` attributes are present, the device IDs must match or the notification is rejected. */
export interface DeviceElement {
/** Device JID (contains user and device ID) */
jid: Jid;
/** Optional key index */
key_index?: number | null;
/** Optional LID (device ID must match jid's device ID if present) */
lid?: Jid | null;
}
/** Device information for registry tracking. */
export interface DeviceInfo {
/** The device ID (0 = primary device, 1+ = companion devices) */
device_id: number;
/** The key index, if known */
key_index?: number | null;
}
/** Device list record matching WhatsApp Web's DeviceListRecord structure. */
export interface DeviceListRecord {
/** The user part of the JID (phone number or LID) */
user: string;
/** List of known devices for this user */
devices: DeviceInfo[];
/** Timestamp when this record was last updated */
timestamp: number;
/** Participant hash from usync, if available */
phash?: string | null;
/** ADV raw_id from `ADVKeyIndexList` — used to detect identity changes. When this changes, all sessions and sender keys for the user must be cleared. */
raw_id?: number | null;
}
/** Device list update notification. Emitted when a user's device list changes (device added/removed/updated). */
export interface DeviceListUpdate {
/** The user whose device list changed (from attribute) */
user: Jid;
/** Optional LID user (for LID-PN mapping) */
lid_user?: Jid | null;
/** Type of update (add/remove/update) */
update_type: DeviceListUpdateType;
/** Affected devices with detailed info */
devices: DeviceNotificationInfo[];
/** Key index info (for add/remove) */
key_index?: KeyIndexInfo | null;
/** Contact hash (for update - used for contact lookup) */
contact_hash?: string | null;
}
/** Type of device list update notification. Matches WhatsApp Web's device notification types. */
export type DeviceListUpdateType = "add" | "remove" | "update";
/** Parsed device notification stanza. Wire format: ```xml <notification from="185169143189667@lid" id="..." t="..." type="devices" lid="..."> <remove> <device jid="185169143189667:75@lid"/> <key-index-list ts="1769296600"/> </remove> </notification> ``` Reference: WhatsApp Web `WAWebHandleDeviceNotification` parser (5Yec01dI04o.js:23125-23183) Per WhatsApp Web: Only ONE operation per notification is processed. Priority order: remove > add > update */
export interface DeviceNotification {
/** User JID (from attribute) */
from: Jid;
/** Optional LID user (for LID-PN mapping learning) */
lid_user?: Jid | null;
/** Stanza ID (for ACK) */
stanza_id: string;
/** Timestamp */
timestamp: number;
/** The operation (one per notification, priority: remove > add > update) */
operation: DeviceOperation;
}
/** Device information from notification. */
export interface DeviceNotificationInfo {
/** Device ID (extracted from JID) */
device_id: number;
/** Optional key index */
key_index?: number | null;
}
/** Device notification operation type. Wire format: Child element tag of `<notification type="devices">` - `<add>` - Device was added - `<remove>` - Device was removed - `<update>` - Device info updated (hash-based lookup) */
export type DeviceNotificationType = "add" | "remove" | "update";
/** Operation content (add/remove/update child element). Wire format per WhatsApp Web (5Yec01dI04o.js:23141-23180): ```xml <add> <device jid="user:75@lid" key-index="2"/> <key-index-list ts="...">SIGNED_BYTES</key-index-list> </add> <!-- OR --> <remove> <device jid="user:75@lid"/> <key-index-list ts="..."/> <!-- ts required for remove --> </remove> <!-- OR --> <update hash="CONTACT_HASH"/> ``` Note: WhatsApp Web does NOT read any attributes from add/remove nodes. The `device_hash` attribute (if present) is not used by the official client. */
export interface DeviceOperation {
/** Operation type (add/remove/update) */
operation_type: DeviceNotificationType;
/** Contact hash (for update only) - from `hash` attribute, used for contact lookup */
contact_hash?: string | null;
/** Device elements (for add/remove, single device per WhatsApp Web) */
devices: DeviceElement[];
/** Key index info (required for add/remove per WhatsApp Web) */
key_index?: KeyIndexInfo | null;
}
export interface DeviceSentMeta {
destination_jid: string;
phash: string;
}
export type DirtyType = "account_sync" | "groups" | "syncd_app_state" | "newsletter_metadata" | string;
export type DisallowedListAction = "add" | "remove";
/** A contact's default disappearing messages setting changed. Sent by the server as `<notification type="disappearing_mode">`. WA Web: `WAWebHandleDisappearingModeNotification` → `WAWebUpdateDisappearingModeForContact`. */
export interface DisappearingModeChanged {
/** The contact whose setting changed. */
from: Jid;
/** New duration in seconds (0 = disabled, 86400 = 24h, etc.). */
duration: number;
/** When the setting was changed. Consumers should only apply this if it's newer than their stored value. */
setting_timestamp: number;
}
export type EditAttribute = "" | "1" | "2" | "3" | "7" | "8" | string;
export interface GroupInfo {
participants: Jid[];
addressing_mode: AddressingMode;
}
/** All possible group notification action types. Maps 1:1 to `GROUP_NOTIFICATION_TAG` child element tags from WhatsApp Web. The `#[wire = "..."]` attribute is the SINGLE source of truth for each variant's wire tag: the JSON discriminator (via the auto-derived `Serialize`), the parser dispatch (via the auto-generated sibling `GroupNotificationActionTag` enum), and `wire_tag()` / `tag_name()` all read from the same table. */
export type GroupNotificationAction =
| { type: "add"; participants: GroupParticipantInfo[]; reason?: string | null }
| { type: "remove"; participants: GroupParticipantInfo[]; reason?: string | null }
| { type: "promote"; participants: GroupParticipantInfo[] }
| { type: "demote"; participants: GroupParticipantInfo[] }
| { type: "modify"; participants: GroupParticipantInfo[] }
| { type: "subject"; subject: string; subject_owner?: Jid | null; subject_time?: number | null }
| { type: "description"; id: string; description?: string | null }
| { type: "locked"; threshold?: string | null }
| { type: "unlocked" }
| { type: "announcement" }
| { type: "not_announcement" }
| { type: "ephemeral"; expiration: number; trigger?: number | null }
| { type: "membership_approval_mode"; enabled: boolean }
| { type: "membership_approval_request"; request_method: MembershipRequestMethod; parent_group_jid?: Jid | null }
| { type: "created_membership_requests"; request_method: MembershipRequestMethod; parent_group_jid?: Jid | null; requests: GroupParticipantInfo[] }
| { type: "revoked_membership_requests"; participants: Jid[] }
| { type: "member_add_mode"; mode: string }
| { type: "no_frequently_forwarded" }
| { type: "frequently_forwarded_ok" }
| { type: "invite"; code: string }
| { type: "revoke" }
| { type: "growth_locked"; expiration: number; lock_type: string }
| { type: "growth_unlocked" }
| { type: "create" }
| { type: "delete"; reason?: string | null }
| { type: "link"; link_type: string }
| { type: "unlink"; unlink_type: string; unlink_reason?: string | null }
| { type: "linked_group_promote"; participants: GroupParticipantInfo[] }
| { type: "linked_group_demote"; participants: GroupParticipantInfo[] }
| { type: "suspended" }
| { type: "unsuspended" }
| { type: "auto_add_disabled" }
| { type: "is_capi_hosted_group" }
| { type: "group_safety_check" }
| { type: "limit_sharing_enabled"; trigger?: number | null }
| { type: "allow_admin_reports" }
| { type: "not_allow_admin_reports" }
| { type: "reports" }
| { type: "allow_non_admin_sub_group_creation" }
| { type: "not_allow_non_admin_sub_group_creation" }
| { type: "created_sub_group_suggestion" }
| { type: "revoked_sub_group_suggestions" }
| { type: "change_number"; new_owner?: Jid | null; sub_group_suggestions: Jid[] }
| { type: string; tag: string };
/** Participant info extracted from `<participant>` child elements. Wire format: ```xml <participant jid="..." type="..." lid="..." phone_number="..." username="..." display_name="..." join_time="..."/> ``` `display_name` is the server-rendered label (e.g. `"+55∙∙∙∙∙∙∙∙∙79"` when the requester is not in the participant's contacts). `type` flags admin/superadmin tier; LID-addressed groups also carry `lid` and `username`. WA Web's `WAWebHandleGroupNotification` y() reads all of these into the participant model so the UI can render notifications and patch admin caches without resolving the contact locally. */
export interface GroupParticipantInfo {
jid: Jid;
phone_number?: Jid | null;
/** Server-provided display label for this participant. Only populated for `<participant>` children inside group notifications; `None` for `<requested_user>` (WA Web doesn't read it there either). */
display_name?: string | null;
/** Admin tier. Defaults to `Participant` when the attr is missing. */
type?: GroupParticipantType | null;
/** LID JID when this `<participant>` carries a separate `lid` attr. Distinct from `jid` which may already be a LID. */
lid?: Jid | null;
/** Username, gated by `WAWebUsernameGatingUtils`. Empty in classic PN-addressed groups. */
username?: string | null;
/** Unix seconds since the participant joined the group. Used by admin UI for tenure display. */
join_time?: number | null;
}
/** Admin tier from `<participant type="...">`. Mirrors `GROUP_PARTICIPANT_TYPES` in `WAWebGroupApiConst`. */
export type GroupParticipantType = "participant" | "admin" | "superadmin";
/** Query request type. */
export type GroupQueryRequestType = "interactive";
/** Group update notification. Emitted for each action in a `<notification type="w:gp2">` stanza. A single notification may produce multiple `GroupUpdate` events (one per action). */
export interface GroupUpdate {
/** The group this update applies to */
group_jid: Jid;
/** The admin/user who triggered the change (`participant` attribute) */
participant?: Jid | null;
/** Phone number JID of the participant (for LID-addressed groups) */
participant_pn?: Jid | null;
/** When the change occurred */
timestamp: number;
/** Whether the group uses LID addressing mode */
is_lid_addressing_mode: boolean;
/** The specific action */
action: GroupNotificationAction;
}
export type HostType = "primary" | "fallback" | string;
/** Identity key changed for a user (e.g., user reinstalled WhatsApp). Emitted after device record cleanup so sessions and sender keys are cleared. */
export interface IdentityChange {
/** The user whose identity changed */
user: Jid;
/** Optional LID for the user */
lid_user?: Jid | null;
}
export interface IncomingCall {
from: Jid;
/** Stanza id; distinct from `CallAction::call_id`. */
stanza_id: string;
notify?: string | null;
platform?: string | null;
version?: string | null;
timestamp: number;
offline: boolean;
action: CallAction;
}
/** IQ request type for WhatsApp protocol queries. */
export type InfoQueryType = "set" | "get";
/** Key index information from `<key-index-list>` element. Wire format: ```xml <!-- For add: has signed bytes content --> <key-index-list ts="1769296600">SIGNED_BYTES</key-index-list> <!-- For remove: empty, ts required --> <key-index-list ts="1769296600"/> ``` Required for add/remove operations per WhatsApp Web. */
export interface KeyIndexInfo {
/** Timestamp (required for remove per WhatsApp Web) */
timestamp: number;
/** Signed key index bytes (only present for add) */
signed_bytes?: Uint8Array | null;
}
/** The source from which a LID-PN mapping was learned. Different sources have different trust levels and handling for identity changes. */
export type LearningSource = "usync" | "peer_pn_message" | "peer_lid_message" | "recipient_latest_lid" | "migration_sync_latest" | "migration_sync_old" | "blocklist_active" | "blocklist_inactive" | "pairing" | "device_notification" | "other";
/** An entry in the LID-PN cache containing the full mapping information. */
export interface LidPnEntry {
/** The LID user part (e.g., "100000012345678") */
lid: string;
/** The phone number user part (e.g., "559980000001") */
phone_number: string;
/** Unix timestamp when the mapping was first learned */
created_at: number;
/** The source from which this mapping was learned */
learning_source: LearningSource;
}
/** Entry representing a LID to Phone Number mapping. */
export interface LidPnMappingEntry {
/** The LID user part (e.g., "100000012345678") */
lid: string;
/** The phone number user part (e.g., "559980000001") */
phone_number: string;
/** Unix timestamp when the mapping was first learned */
created_at: number;
/** Unix timestamp when the mapping was last updated */
updated_at: number;
/** The source from which this mapping was learned (e.g., "usync", "peer_pn_message") */
learning_source: string;
}
export interface LoggedOut {
on_connect: boolean;
reason: ConnectFailureReason;
}
export interface MarkChatAsReadUpdate {
/** The chat being marked as read or unread. */
jid: Jid;
timestamp: number;
action: MarkChatAsReadAction;
from_full_sync: boolean;
}
/** Member link mode for group invite links. */
export type MemberLinkMode = "admin_link" | "all_member_link";
/** Who can share message history with new members. */
export type MemberShareHistoryMode = "admin_share" | "all_member_share";
/** How a membership request was initiated. Maps to `WAWebRequestMethodType` in WhatsApp Web JS. */
export type MembershipRequestMethod = "invite_link" | "linked_group_join" | "non_admin_add";
export type MessageCategory = "" | "peer" | string;
export interface MessageInfo {
source: MessageSource;
id: string;
server_id: number;
type: string;
push_name: string;
timestamp: number;
category: MessageCategory;
multicast: boolean;
media_type: string;
edit: EditAttribute;
bot_info?: MsgBotInfo | null;
meta_info: MsgMetaInfo;
verified_name?: VerifiedNameCertificate | null;
device_sent_meta?: DeviceSentMeta | null;
/** Ephemeral duration in seconds, extracted from `contextInfo.expiration`. */
ephemeral_expiration?: number | null;
/** Whether this message was delivered during offline sync. */
is_offline: boolean;
/** Set when this message was recovered via PDO rather than normal decryption. Contains the PDO request message ID. */
unavailable_request_id?: string | null;
/** Server-store timestamp in microseconds (envelope `sts` attr). Used by WA Web for read-self watermark ordering across companion devices. */
server_timestamp_us?: number | null;
/** Envelope `verified_level` attr (e.g. "unknown"/"low"/"high"). For business messages this is the server-asserted verification tier; for regular messages it is absent. */
verified_level?: string | null;
/** Envelope `verified_name` int attr (business name certificate serial). Separate from the `verified_name` child cert bytes already on this struct. */
verified_name_serial?: number | null;
/** Envelope `peer_recipient_pn` attr. Present on companion-device self-synced DM stanzas to identify the peer's PN (so the receipt goes to the right routing target). */
peer_recipient_pn?: Jid | null;
/** Broadcast-contact-list recipients from `<participants><to jid>` on an incoming broadcast/status stanza. Populated only for broadcasts; used to validate a `deviceSentMessage.phash` (WA Web `validateBclHash`). Empty otherwise. */
bcl_participants: Jid[];
}
export interface MessageSource {
chat: Jid;
sender: Jid;
is_from_me: boolean;
is_group: boolean;
addressing_mode?: AddressingMode | null;
sender_alt?: Jid | null;
recipient_alt?: Jid | null;
broadcast_list_owner?: Jid | null;
recipient?: Jid | null;
}
/** MEX GraphQL error extensions. */
export interface MexErrorExtensions {
error_code?: number | null;
is_summary?: boolean | null;
is_retryable?: boolean | null;
severity?: string | null;
}
/** MEX GraphQL error. */
export interface MexGraphQLError {
message: string;
extensions?: MexErrorExtensions | null;
}
/** `payload` shape depends on `op_name`. `offline` mirrors the raw string the server sets when replaying backlog (often a timestamp); presence alone signals backlog vs live. */
export interface MexNotification {
op_name: string;
from?: Jid | null;
stanza_id?: string | null;
offline?: string | null;
payload: Value;
}
/** MEX GraphQL response. */
export interface MexResponse {
data?: Value | null;
errors?: MexGraphQLError[] | null;
}
export interface MsgBotInfo {
edit_type?: BotEditType | null;
edit_target_id?: string | null;
edit_sender_timestamp_ms?: number | null;
}
export interface MsgMetaInfo {
target_id?: string | null;
target_sender?: Jid | null;
/** `<meta target_chat_jid="…">` — present when the bot reply addresses a chat distinct from the stanza-level `from` (used for msmsg secret lookup; see WA Web `decryptMsmsgBotMessage`). */
target_chat?: Jid | null;
deprecated_lid_session?: boolean | null;
thread_message_id?: string | null;
thread_message_sender_jid?: Jid | null;
/** `<meta content_type=...>` attr. Server marks reactions/edits as `"add_on"`; mirrors `WAWebHandleMsgParser` b()'s metadata read. */
content_type?: string | null;
/** `<meta appdata=...>` attr. `"default"` is the only observed value. */
appdata?: string | null;
/** `<reporting><reporting_tag>` content bytes (16 or 20). Pre-requisite for the server-side report-abuse flow. */
reporting_tag?: Uint8Array | null;
/** `<reporting><reporting_token>` content bytes (16). Pre-requisite for the server-side report-abuse flow. */
reporting_token?: Uint8Array | null;
/** `v` attr on `<reporting_token>`. WA Web defaults to 1 when missing. */
reporting_token_version?: number | null;
}
/** Message-secret write entry keyed by chat, sender, and message ID. */
export interface MsgSecretEntry {
chat: string;
sender: string;
msg_id: string;
secret: Uint8Array;
/** Absolute unix-seconds retention deadline. `0` means never expire. Computed by the caller from the parent message's event time plus a per-add-on-kind horizon (see `MsgSecretRetention`). The store prunes rows whose deadline has passed; it does not know the horizon itself. */
expires_at: number;
/** Parent message event time (unix seconds), or `0` when unknown. Kept so the receive path can enforce the edit-processing window (`editTs < message_ts + window`) the same way WhatsApp Web does. */
message_ts: number;
}
export interface MuteUpdate {
/** The chat being muted or unmuted. */
jid: Jid;
timestamp: number;
action: MuteAction;
from_full_sync: boolean;
}
/** Wire codes: 421=StaleGroupAddressingMode, 475=NewChatMessagesCapped, 487=ParsingError, 488=UnrecognizedStanza, 489=UnrecognizedStanzaClass, 490=UnrecognizedStanzaType, 491=InvalidProtobuf, 493=InvalidHostedCompanionStanza, 495=MissingMessageSecret, 496=SignalErrorOldCounter, 499=MessageDeletedOnPeer, 500=UnhandledError, 550=UnsupportedAdminRevoke, 551=UnsupportedLIDGroup, 552=DBOperationFailed */
export type NackReason = number;
/** A newsletter live update notification, typically containing updated reaction counts for one or more messages. */
export interface NewsletterLiveUpdate {
/** The newsletter channel this update belongs to. */
newsletter_jid: Jid;
messages: NewsletterLiveUpdateMessage[];
}
/** A single message entry in a newsletter live update. */
export interface NewsletterLiveUpdateMessage {
server_id: number;
reactions: NewsletterLiveUpdateReaction[];
}
/** A reaction count in a newsletter live update. */
export interface NewsletterLiveUpdateReaction {
code: string;
count: number;
}
export type NewsletterMessageType = "text" | "media" | "reaction" | "revoke" | "poll_creation" | "poll_vote" | "edit" | string;
export interface OfflineSyncCompleted {
count: number;
}
export interface OfflineSyncPreview {
total: number;
app_data_changes: number;
messages: number;
notifications: number;
receipts: number;
}
export interface PairError {
id: Jid;
lid: Jid;
business_name: string;
platform: string;
error: string;
}
export interface PairSuccess {
id: Jid;
lid: Jid;
business_name: string;
platform: string;
}
/** Participant type (admin level). */
export type ParticipantType = "member" | "admin" | "superadmin";
export interface PictureUpdate {
/** The JID whose picture changed (user or group). */
jid: Jid;
/** The user who made the change. Present for group picture changes (the admin who changed it). `None` for personal picture updates. */
author?: Jid | null;
timestamp: number;
/** Whether the picture was removed (true) or set/updated (false). */
removed: boolean;
/** The server-assigned picture ID (from `<set id="..."/>`). `None` for deletions. */
picture_id?: string | null;
}
export interface PinUpdate {
/** The chat being pinned or unpinned. */
jid: Jid;
timestamp: number;
action: PinAction;
from_full_sync: boolean;
}
export type PreKeyFetchReason = "identity" | "retry" | string;
export type Presence = "available" | "unavailable";
export interface PresenceUpdate {
/** The contact whose presence changed. */
from: Jid;
unavailable: boolean;
last_seen?: number | null;
}
export type PrivacyCategory = "last" | "online" | "profile" | "status" | "groupadd" | "readreceipts" | "calladd" | "messages" | "defense" | string;
export type PrivacySensitiveType = "1";
export type PrivacySetting = "all" | "contacts" | "contact_blacklist" | "match_last_seen" | "known" | "none" | "undefined";
export type PrivacySettingType = "group_add" | "last" | "status" | "profile" | "read_receipts" | "online" | "call_add";
export interface PrivacySettings {
group_add?: PrivacySetting | null;
last_seen?: PrivacySetting | null;
status?: PrivacySetting | null;
profile?: PrivacySetting | null;
read_receipts?: PrivacySetting | null;
call_add?: PrivacySetting | null;
online?: PrivacySetting | null;
}
export type PrivacyValue = "all" | "contacts" | "none" | "contact_blacklist" | "match_last_seen" | "known" | "off" | "on_standard" | string;
/** Profile picture type (preview thumbnail or full-size). */
export type ProfilePictureType = "preview" | "image";
export interface PushNameUpdate {
/** The contact who changed their push name. */
jid: Jid;
message: MessageInfo;
old_push_name: string;
new_push_name: string;
}
export type PushPriority = "high" | "high_force";
export interface Receipt {
source: MessageSource;
message_ids: string[];
timestamp: number;
type: ReceiptType;
}
export type ReceiptType =
| { type: "delivered" }
| { type: "sender" }
| { type: "retry" }
| { type: "enc_rekey_retry" }
| { type: "read" }
| { type: "read_self" }
| { type: "played" }
| { type: "played_self" }
| { type: "server_error" }
| { type: "inactive" }
| { type: "peer_msg" }
| { type: "history_sync" }
| { type: "other"; data: string };
/** Chat state type as received from incoming stanzas. Aligned with WhatsApp Web's `WAChatState` constants: - `typing` = ACTIVE_CHAT_STATE_TYPE.TYPING - `recording_audio` = ACTIVE_CHAT_STATE_TYPE.RECORDING_AUDIO - `idle` = IDLE_CHAT_STATE_TYPE.IDLE */
export type ReceivedChatState = "typing" | "recording_audio" | "idle";
export interface SelfPushNameUpdated {
from_server: boolean;
old_name: string;
new_name: string;
}
/** The type of spam flow indicating the source of the report. */
export type SpamFlow = "GroupSpamBannerReport" | "GroupInfoReport" | "MessageMenu" | "ContactInfo" | "StatusReport";
export interface StarUpdate {
/** The chat containing the starred or unstarred message. */
chat_jid: Jid;
/** The participant who sent the message. `Some` for group messages from others, `None` for self-authored or 1-on-1 messages (wire value `"0"`). */
participant_jid?: Jid | null;
message_id: string;
from_me: boolean;
timestamp: number;
action: StarAction;
from_full_sync: boolean;
}
/** Privacy setting sent in the `<meta>` node of the status stanza. Matches WhatsApp Web's `status_setting` attribute. */
export type StatusPrivacySetting = "contacts" | "allowlist" | "denylist";
export interface StreamError {
code: string;
raw?: any | null;
}
/** Trusted contact privacy token entry. Matches WhatsApp Web's Chat.tcToken / tcTokenTimestamp / tcTokenSenderTimestamp. */
export interface TcTokenEntry {
/** Raw token bytes received from the server. */
token: Uint8Array;
/** Unix timestamp (seconds) when the token was received. */
token_timestamp: number;
/** Unix timestamp (seconds) when we last issued our token to this contact. */
sender_timestamp?: number | null;
}
/** Wire codes: 101=SentToTooManyPeople, 102=BlockedByUsers, 103=CreatedTooManyGroups, 104=SentTooManySameMessage, 106=BroadcastList */
export type TempBanReason = number;
export interface TemporaryBan {
code: TempBanReason;
expire: number;
}
export type UnavailableType = "unknown" | "view_once";
export interface UndecryptableMessage {
info: MessageInfo;
is_unavailable: boolean;
unavailable_type: UnavailableType;
decrypt_fail_mode: DecryptFailMode;
}
export interface UserAboutUpdate {
/** The contact whose about text changed. */
jid: Jid;
status: string;
timestamp: number;
}
/** Usync context. */
export type UsyncContext = "interactive" | "background" | "message";
/** Usync mode. */
export type UsyncMode = "query" | "full";
/** Verified name certificate information. */
export interface VerifiedName {
name?: string | null;
serial?: string | null;
issuer?: string | null;
certificate?: Uint8Array | null;
}
export interface ILogger {
level: string;
trace(obj: object, msg?: string): void;
debug(obj: object, msg?: string): void;
info(obj: object, msg?: string): void;
warn(obj: object, msg?: string): void;
error(obj: object, msg?: string): void;
}
export interface WhatsAppClientConfig {
transport: JsTransportCallbacks;
httpClient: JsHttpClientConfig;
onEvent?: (event: WhatsAppEvent) => void;
}
/**
* JS storage callbacks for the persistent backend.
*
* The boundary is a two-level namespaced key/value store: `store` is one of the
* fixed STORE_* namespaces (e.g. "session", "msg_secret", "lid_mapping") and
* `key` is an opaque, namespace-scoped id; values are raw bytes.
*
* Only `get`/`set`/`delete` are MANDATORY — a 3-method store keeps working
* exactly as before. The remaining methods are OPTIONAL performance/structural
* primitives the core feature-detects (by handle presence) and uses when the
* host provides them:
* - `setMany`/`deleteMany` collapse N per-key FFI crossings into one (this is
* what turns a ~20k-secret history-sync write from 20k awaits into a single
* batched call).
* - `listKeys`/`listEntries` let the core enumerate a namespace directly,
* which lets it DROP its hand-maintained meta-index lists (msg_secret_keys,
* tc_token_jids, …). A host that cannot enumerate a category (e.g. an
* id-addressed Baileys keyStore) simply omits them; the core then keeps its
* self-maintained index for that backend.
* - `deletePrefix` accelerates unconditional bulk clears.
*
* `capabilities` is read ONCE at init. Omit it (or a field) and the core treats
* the corresponding primitive as absent. A capability declared `true` MUST have
* its method(s) present and working.
*/
export interface JsStoreCallbacks {
/** Read one value by (store, key). Null/undefined if absent. MANDATORY. */
get(store: string, key: string): Promise<Uint8Array | null>;
/** Write one value by (store, key). MANDATORY. */
set(store: string, key: string, value: Uint8Array): Promise<void>;
/** Delete one key. No-op if absent. MANDATORY. */
delete(store: string, key: string): Promise<void>;
/**
* Write many [key, value] pairs into ONE store in a single call. Entries are
* tuples (so keys may contain any character). Best-effort: if the medium has
* no cross-key atomicity (file-per-key) it MUST still apply every entry and
* fail-fast on error so the core can retry (writes are idempotent by key).
* Empty array is a valid no-op.
*/
setMany?(store: string, entries: [key: string, value: Uint8Array][]): Promise<void>;
/** Read many keys from ONE store; one entry per FOUND key, any order. */
getMany?(store: string, keys: string[]): Promise<[key: string, value: Uint8Array][]>;
/** Delete many keys from ONE store in a single call. Missing keys ignored. */
deleteMany?(store: string, keys: string[]): Promise<void>;
/** Enumerate live keys in `store` (optionally prefix-filtered). Unordered. */
listKeys?(store: string, prefix?: string): Promise<string[]>;
/**
* Like listKeys but returns [key, value] pairs, so the core can inspect the
* embedded timestamp prefix for delete-expired sweeps without N follow-up
* gets. If absent but listKeys exists, the core falls back to listKeys+getMany.
*/
listEntries?(store: string, prefix?: string): Promise<[key: string, value: Uint8Array][]>;
/** Delete every key in `store` starting with `prefix`. Returns count removed. */
deletePrefix?(store: string, prefix: string): Promise<number>;
/** Static capability declaration, read once at init. Omitted => all false. */
capabilities?: {
/** setMany/getMany/deleteMany are implemented. */
batch?: boolean;
/** listKeys/listEntries reliably enumerate a namespace. */
enumerate?: boolean;
/** deletePrefix is implemented. */
prefixDelete?: boolean;
/**
* Opt-in write-back: the bridge buffers writes in WASM and crosses to this
* store only on flush (disconnect/shutdown). Declare it ONLY for EPHEMERAL
* stores (a crash before flush loses un-flushed writes) — it keeps the
* per-message Signal-state persistence off the JS↔WASM boundary.
*/
writeBack?: boolean;
};
/** Optional durability barrier (flush pending writes). */
flush?(): Promise<void>;
}
/**
* Initialize the WASM engine. Call once before creating clients.
* @param logger Optional pino-compatible logger.
* @param crypto Optional native crypto callbacks — when provided, AES/HMAC
* primitives delegate to the host (e.g. `node:crypto`). Falls
* back to the Rust-soft implementation if omitted.
*/
export function initWasmEngine(logger?: any, crypto?: JsCryptoCallbacks): void;
/**
* Create a full WhatsApp client running in WASM.
*
* @param transport_config WebSocket transport callbacks (connect/send/disconnect)
* @param http_config HTTP client callbacks (execute via fetch)
* @param on_event Optional event callback — receives typed WhatsApp events in order
* @param store Optional JS storage callbacks — if provided, enables persistent storage
*/
export function createWhatsAppClient(
transport_config: JsTransportCallbacks,
http_config: JsHttpClientConfig,
on_event?: ((event: WhatsAppEvent) => void) | null,
store?: JsStoreCallbacks | null,
cache_config?: CacheConfig | null,
): Promise<WasmWhatsAppClient>;
/** Cache entry configuration. */
export interface CacheEntryConfig {
ttlSecs?: number;
capacity?: number;
store?: JsCacheStore;
}
/** Custom cache backend. */
export interface JsCacheStore {
get(namespace: string, key: string): Promise<Uint8Array | null>;
set(namespace: string, key: string, value: Uint8Array, ttlSecs?: number): Promise<void>;
delete(namespace: string, key: string): Promise<void>;
clear(namespace: string): Promise<void>;
}
/** Cache configuration — all fields optional. */
export interface CacheConfig {
store?: JsCacheStore;
group?: CacheEntryConfig;
device?: CacheEntryConfig;
deviceRegistry?: CacheEntryConfig;
lidPn?: CacheEntryConfig;
retriedGroupMessages?: CacheEntryConfig;
recentMessages?: CacheEntryConfig;
messageRetry?: CacheEntryConfig;
}
// Augment WasmWhatsAppClient with methods that need skip_typescript
// (Record returns can't be expressed by wasm-bindgen)
interface WasmWhatsAppClient {
/** Fetch all groups the user is participating in. */
groupFetchAllParticipating(): Promise<Record<string, GroupMetadataResult>>;
/** Fetch user info for one or more JIDs. */
fetchUserInfo(jids: string[]): Promise<Record<string, UserInfoResult>>;
}
/**
* A message key for `readMessages`.
*/
export interface ReadMessageKey {
remoteJid: string;
id: string;
participant?: string;
}
/**
* A participant change result from `groupParticipantsUpdate`.
*/
export interface ParticipantChangeResult {
jid: string;
status?: string;
error?: string;
}
/**
* A single entry from `fetchBlocklist`.
*/
export interface BlocklistEntryResult {
jid: string;
timestamp?: number;
}
/**
* A single entry from `fetchUserInfo`.
*/
export interface UserInfoResult {
jid: string;
lid?: string;
status?: string;
pictureId?: string;
isBusiness: boolean;
}
/**
* A single media host from `getMediaConn`.
*/
export interface MediaHost {
hostname: string;
}
/**
* A single voter entry for `getAggregateVotesInPollMessage`.
*/
export interface PollVoterEntry {
voter: string;
encPayload: Uint8Array;
encIv: Uint8Array;
}
/**
* Block/unblock action.
*/
export type BlockAction = "block" | "unblock";
/**
* Business category info.
*/
export interface BusinessCategoryResult {
id: string;
name: string;
}
/**
* Business hours config for a day.
*/
export interface BusinessHoursConfigResult {
dayOfWeek: string;
mode: string;
openTime: number;
closeTime: number;
}
/**
* Business hours.
*/
export interface BusinessHoursResult {
timezone?: string;
businessConfig?: BusinessHoursConfigResult[];
}
/**
* Chat state (typing indicator).
*/
export type ChatState = "composing" | "recording" | "paused";
/**
* Enabled features in this build.
* Use this to check feature availability at runtime before calling feature-gated functions.
*/
export interface EnabledFeatures {
/**
* Audio processing support (waveform generation, duration detection)
*/
audio: boolean;
/**
* Image processing support (thumbnails, profile pictures, format conversion)
*/
image: boolean;
/**
* Sticker metadata support (WebP EXIF for WhatsApp stickers)
*/
sticker: boolean;
}
/**
* Group join request action.
*/
export type GroupRequestAction = "approve" | "reject";
/**
* Group member add mode.
*/
export type MemberAddMode = "admin_add" | "all_member_add";
/**
* Group participant action.
*/
export type GroupParticipantAction = "add" | "remove" | "promote" | "demote";
/**
* Group participant info as returned from `getGroupMetadata` / cached group
* state. Distinct from `wa