UNPKG

whatsapp-rust-bridge

Version:

A high-performance utilities for WhatsApp, powered by Rust and WebAssembly.

1,299 lines (1,120 loc) 101 kB
/* 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