npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

baileyrs

v0.0.4

Published

A WebSockets library for interacting with WhatsApp Web

Readme

Baileyrs (Rust Fork)

A WhatsApp Web API library powered by a Rust/WASM bridge for protocol handling, encryption, and media operations.

This is a fork of Baileys that replaces internal JavaScript protocol logic with whatsapp-rust compiled to WASM via whatsapp-rust-bridge.

What's Different

| Area | Original Baileys | This Fork | |---|---|---| | Signal Protocol | JS (libsignal) | Rust/WASM | | Media encrypt/decrypt | Node.js crypto | Rust AES-256-CBC + HMAC | | Media upload/download | JS fetch + temp files | Rust with CDN failover, auth refresh, resumable upload | | Key management | JS auth state | Rust PersistenceManager | | Auto-reconnect | Manual startSock() loop | Built-in with fibonacci backoff |

Install

yarn add baileyrs  # or your fork's package name

Then import:

import makeWASocket from 'baileyrs'

Quick Start

import makeWASocket, { DisconnectReason, useMultiFileAuthState } from 'baileyrs'
import { Boom } from '@hapi/boom'

const { state } = await useMultiFileAuthState('auth_info')
const sock = makeWASocket({ auth: state })

sock.ev.on('connection.update', ({ connection, lastDisconnect }) => {
    if (connection === 'close') {
        const statusCode = (lastDisconnect?.error as Boom)?.output?.statusCode
        if (statusCode === DisconnectReason.loggedOut) {
            console.log('Logged out')
        }
        // Auto-reconnect is handled by the Rust engine — no need to call makeWASocket again
    }
    if (connection === 'open') {
        console.log('Connected')
    }
})

sock.ev.on('messages.upsert', ({ messages }) => {
    for (const msg of messages) {
        console.log('received message', msg.key.id)
    }
})

Connecting

QR Code

const sock = makeWASocket({
    auth: state,
    browser: Browsers.ubuntu('My App')
})

A QR code will be emitted via connection.update events. Scan it with your phone.

Pairing Code

const sock = makeWASocket({ auth: state })

if (!sock.isLoggedIn) {
    const code = await sock.requestPairingCode('5511999999999')
    console.log('Pairing code:', code)
}

Auth State

All state — crypto keys, Signal sessions, device identity, and push name — is managed and persisted by the Rust bridge. No creds.json, no saveCreds callback.

import { useMultiFileAuthState } from 'baileyrs'

const { state } = await useMultiFileAuthState('auth_folder')
const sock = makeWASocket({ auth: state })

Files created in auth_folder/:

  • device-*.bin — Rust device state (noise keys, identity, push name, etc.)
  • session-*.bin — Signal sessions
  • identity-*.bin — Signal identity keys
  • pre-key-*.bin — Signal pre-keys
  • sender-key-*.bin — Group sender keys

Sending Messages

// Text
await sock.sendMessage(jid, { text: 'Hello!' })

// Quote
await sock.sendMessage(jid, { text: 'Reply' }, { quoted: msg })

// Mention
await sock.sendMessage(jid, {
    text: '@12345678901',
    mentions: ['[email protected]']
})

// Forward
await sock.sendMessage(jid, { forward: msg })

// Location
await sock.sendMessage(jid, {
    location: { degreesLatitude: 24.12, degreesLongitude: 55.11 }
})

// Contact
await sock.sendMessage(jid, {
    contacts: {
        displayName: 'Jeff',
        contacts: [{ vcard: 'BEGIN:VCARD\nVERSION:3.0\nFN:Jeff\nEND:VCARD' }]
    }
})

// Reaction
await sock.sendMessage(jid, { react: { text: '💖', key: msg.key } })

// Poll
await sock.sendMessage(jid, {
    poll: { name: 'My Poll', values: ['Yes', 'No'], selectableCount: 1 }
})

// Delete (for everyone)
await sock.sendMessage(jid, { delete: msg.key })

// Edit
await sock.sendMessage(jid, { text: 'updated text', edit: msg.key })

// Disappearing messages
await sock.sendMessage(jid, { disappearingMessagesInChat: 604800 }) // 7 days

Media Messages

Media encryption, upload, and CDN failover are handled entirely by the Rust bridge.

// Image
await sock.sendMessage(jid, { image: readFileSync('photo.jpg'), caption: 'Hello' })

// Video
await sock.sendMessage(jid, { video: { url: './clip.mp4' }, caption: 'Watch this' })

// Audio
await sock.sendMessage(jid, { audio: { url: './audio.ogg' }, mimetype: 'audio/ogg; codecs=opus' })

// Document
await sock.sendMessage(jid, {
    document: { url: './file.pdf' },
    mimetype: 'application/pdf',
    fileName: 'report.pdf'
})

// View once
await sock.sendMessage(jid, { image: { url: './photo.jpg' }, viewOnce: true })

// GIF (mp4 with gifPlayback)
await sock.sendMessage(jid, { video: readFileSync('anim.mp4'), gifPlayback: true })

Thumbnails are auto-generated for images (requires jimp or sharp). Video thumbnails need ffmpeg on your system, or provide jpegThumbnail directly.

Streaming Upload (Large Files)

For files that are too large to buffer in memory, pass a processMedia hook to sendMessage:

await sock.sendMessage(jid, { video: readFileSync('large-video.mp4') }, {
    processMedia: async (buffer, mediaType, waClient) => {
        const { Writable } = await import('node:stream')
        const fs = await import('node:fs')
        const os = await import('node:os')
        const path = await import('node:path')

        const tmpEnc = path.join(os.tmpdir(), `enc-${Date.now()}`)

        // Streaming encrypt → temp file (constant ~40KB memory)
        const input = new Blob([new Uint8Array(buffer)]).stream()
        const output = Writable.toWeb(fs.createWriteStream(tmpEnc))
        const enc = await waClient.encryptMediaStream(input, output, mediaType)

        // Upload from temp file
        const encData = await fs.promises.readFile(tmpEnc)
        await fs.promises.unlink(tmpEnc)

        const upload = await waClient.uploadEncryptedMediaStream(
            () => new Blob([new Uint8Array(encData)]).stream(),
            enc.mediaKey, enc.fileSha256, enc.fileEncSha256, enc.fileLength, mediaType
        )

        return { upload: { ...upload, ...enc } }
    }
})

Downloading Media

The recommended way to download media is via the socket method — it handles CDN failover, auth refresh, HMAC verification, and media re-upload automatically:

// Buffer (recommended)
const buffer = await sock.downloadMedia(msg, 'buffer')
await writeFile('photo.jpg', buffer)

// Stream
const stream = await sock.downloadMedia(msg, 'stream')
for await (const chunk of stream) {
    // process chunk — only ~64KB in memory at a time
}

If you need to download media outside of a socket context (e.g., custom logger or reupload logic), use the standalone downloadMediaMessage utility:

import { downloadMediaMessage } from 'baileyrs'

const buffer = await downloadMediaMessage(msg, 'buffer', {}, {
    logger: sock.logger,
    reuploadRequest: m => sock.updateMediaMessage(m),
    waClient: sock.waClient!
})

Re-upload Expired Media

await sock.updateMediaMessage(msg)

Read Receipts

await sock.readMessages([{ remoteJid: msg.key.remoteJid!, id: msg.key.id! }])

Presence

// Online/offline
await sock.sendPresenceUpdate('available')
await sock.sendPresenceUpdate('unavailable')

// Subscribe to a contact's presence
await sock.presenceSubscribe(jid)

// Typing indicator
await sock.sendChatState(jid, 'composing')
await sock.sendChatState(jid, 'paused')

Chat Actions

await sock.pinChat(jid, true)
await sock.muteChat(jid, 8 * 60 * 60 * 1000) // mute 8h
await sock.muteChat(jid, null)                 // unmute
await sock.archiveChat(jid, true)
await sock.starMessage(jid, msgId, true)

Reject Calls

await sock.rejectCall(callId, callFrom)

User Queries

// Check if on WhatsApp
const [result] = await sock.onWhatsApp('+1234567890')
if (result.exists) console.log('JID:', result.jid)

// Fetch status
const status = await sock.fetchStatus(jid)

// Profile picture
const url = await sock.profilePictureUrl(jid, 'image')

// Business profile
const profile = await sock.getBusinessProfile(jid)

// On-demand history (messages arrive via messaging-history.set event)
await sock.fetchMessageHistory(50, oldestMsg.key, oldestMsg.messageTimestamp)

Profile

await sock.setPushName('My Name')
await sock.updateProfileStatus('Hello World!')
await sock.updateProfilePicture(jid, imageBytes)
await sock.removeProfilePicture()

Groups

// Create
const group = await sock.groupCreate('Group Name', ['[email protected]'])

// Participants
await sock.groupParticipantsUpdate(jid, ['[email protected]'], 'add')    // or 'remove', 'promote', 'demote'

// Metadata
const metadata = await sock.groupMetadata(jid)
const all = await sock.groupFetchAllParticipating()

// Settings
await sock.groupUpdateSubject(jid, 'New Name')
await sock.groupUpdateDescription(jid, 'New Description')
await sock.groupSettingUpdate(jid, 'announce', true)   // only admins send
await sock.groupSettingUpdate(jid, 'locked', true)      // only admins edit settings
await sock.groupToggleEphemeral(jid, 604800)            // 7 days
await sock.groupMemberAddMode(jid, 'admin_add')         // or 'all_member_add'

// Invite
const code = await sock.groupInviteCode(jid)
const newCode = await sock.groupRevokeInvite(jid)
const response = await sock.groupAcceptInvite(code)
const info = await sock.groupGetInviteInfo(code)

// Join requests
const requests = await sock.groupRequestParticipantsList(jid)
await sock.groupRequestParticipantsUpdate(jid, ['[email protected]'], 'approve')

// Leave
await sock.groupLeave(jid)

Privacy

// Block/unblock
await sock.updateBlockStatus(jid, 'block')
await sock.updateBlockStatus(jid, 'unblock')

// Fetch
const settings = await sock.fetchPrivacySettings()
const blocklist = await sock.fetchBlocklist()

// Update privacy settings
await sock.updateLastSeenPrivacy('contacts')        // 'all' | 'contacts' | 'contact_blacklist' | 'none'
await sock.updateOnlinePrivacy('all')                // 'all' | 'match_last_seen'
await sock.updateProfilePicturePrivacy('contacts')
await sock.updateStatusPrivacy('contacts')
await sock.updateReadReceiptsPrivacy('all')          // 'all' | 'none'
await sock.updateGroupsAddPrivacy('contacts')
await sock.updateDefaultDisappearingMode(604800)     // seconds, 0 to disable

Newsletters (Channels)

const channel = await sock.newsletterCreate('My Channel', 'Description')
const meta = await sock.newsletterMetadata(jid)
await sock.newsletterSubscribe(jid)
await sock.newsletterUnsubscribe(jid)

Memory Monitoring

import { getWasmMemoryBytes } from 'whatsapp-rust-bridge'

// WASM linear memory (total reserved)
const wasmBytes = getWasmMemoryBytes()
console.log(`WASM memory: ${(wasmBytes / 1024 / 1024).toFixed(1)} MB`)

// Detailed cache/collection diagnostics from the Rust engine
const diag = await sock.waClient!.getMemoryDiagnostics()
console.log(diag) // { signalCacheSessions, groupCache, deviceCache, ... }

Socket Config

const sock = makeWASocket({
    // Required
    auth: state,

    // Connection
    waWebSocketUrl: 'wss://web.whatsapp.com/ws/chat',
    connectTimeoutMs: 20_000,
    keepAliveIntervalMs: 30_000,

    // Identity
    version: [2, 3000, 1035194821],
    browser: Browsers.macOS('Chrome'),

    // Events
    emitOwnEvents: true,
    shouldIgnoreJid: jid => false,

    // Proxy (for fetch/HTTP requests — uses undici dispatcher)
    options: { dispatcher: undiciAgent },

    // Cache (Rust-side, see CacheConfig type)
    cache: { group: { ttlSecs: 7200 } }
})

License

MIT License - See LICENSE for details.