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 🙏

© 2025 – Pkg Stats / Ryan Hefner

whatsmeow-node

v0.1.3

Published

TypeScript N-API bindings for go.mau.fi/whatsmeow via Go c-shared bridge

Readme

whatsmeow-node

TypeScript bindings for the Go library go.mau.fi/whatsmeow, via a minimal c-shared bridge loaded with koffi.

You get a type-safe Node/TypeScript API without fragile native addons. The Go bridge is compiled once into a shared library (DLL/.dylib/.so) and loaded at runtime.

Features

  • Typed API: Container, Device, Client, event stream, message send helpers
  • QR login flow and phone pairing-code flow
  • Media upload and download-by-path
  • Presence, group invite link, and many client calls via a generic dispatcher
  • Device management: first/new device, list all devices, get by JID
  • Optional logging levels configured at runtime (database/client, with or without color)

Requirements

  • Go 1.25+
  • Node.js 18+ (tested on Node 22 with koffi)
  • CGO toolchain for your OS (needed by github.com/mattn/go-sqlite3)
    • Windows: MSYS2/MinGW GCC
    • macOS: Xcode Command Line Tools
    • Linux: GCC + libc dev headers

Install & Build

npm install
npm run build

This will produce:

  • build/whatsmeow.dll (or .dylib/.so) from the Go bridge
  • dist/ with compiled JavaScript and type declarations

Note: The repo vendors protobuf-generated JS/TS under proto/. The module src/protos.ts re-exports them for convenience and src/index.ts re-exports proto as well.

Prebuilt binaries (recommended for npm consumers)

This package can ship without native build tools by downloading a prebuilt native library during npm install.

  • On install, the postinstall script will:
    1. Try to download a release asset from GitHub based on your platform triplet.
    2. If not found, try copying from the local prebuilt/<triplet>/ folder (if included in the package tarball).
    3. If still not found and WHATS_BUILD_FROM_SOURCE=true, it will attempt a local build with Go (npm run build:go).

Asset naming (GitHub Releases):

  • The asset file name must be whatsmeow-<triplet>.<ext>, e.g.:
    • whatsmeow-win32-x64.dll
    • whatsmeow-darwin-arm64.dylib
    • whatsmeow-linux-x64-gnu.so
    • whatsmeow-linux-x64-musl.so

Environment variables:

  • WHATS_PREBUILT_BASEURL – override base URL to download from (default derives from package.json repository and version tag v<version>)
  • WHATS_PREBUILT_REPO – override GitHub repo slug user/repo
  • WHATS_PREBUILT_VERSION – override tag name (default: v<package.json version>)
  • WHATS_BUILD_FROM_SOURCE=true – enable local build fallback if no prebuilt is found
  • WHATS_SKIP_POSTINSTALL=true – skip the postinstall step entirely

CI workflow:

  • A GitHub Actions workflow at .github/workflows/prebuild.yml builds and uploads the correct assets to the release for common triplets (Win x64, macOS x64/arm64, Linux x64 glibc/musl). Extend as needed for other triplets.

Quick Start (QR login + send text)

// Local repo import path:
import { openContainer, Client, JID, proto } from './dist/index.js'

// If installed as a dependency:
// import { openContainer, Client, JID, proto } from 'whatsmeow-node'

async function main() {
    const container = await openContainer({
        dialect: 'sqlite3',
        address: 'file:session.db?_foreign_keys=on'
    })
    const device = await container.getFirstDevice()
    const client = await Client.create(device)

    // Obtain QR channel BEFORE connect
    const qr = await client.getQRChannel()
    ;(async () => {
        for (;;) {
            const ev = await qr.next(60000)
            if (ev.event === 'code') console.log('[qr]', ev.code)
            else {
                console.log('[qr]', ev.event)
                break
            }
        }
    })().catch(console.error)

    await client.connect()

    // Send a text message
    const to: JID = '[email protected]'
    const msg: proto.WAWebProtobufsE2E.IMessage = { conversation: 'Hello from Node!' }
    const resp = await client.send(to, msg)
    console.log('Sent:', resp)
}

main().catch(console.error)

Pair Phone (link by code)

// After client.connect()
const code = await client.pairPhone(
    '5511999999999', // digits only, international format
    true, // show push notification on primary
    1, // clientType: 1=Chrome (2=Edge, 3=Firefox, 6=Safari, ...)
    'Chrome (Windows)'
)
console.log('Pairing code:', code)

On the phone, open WhatsApp > Link a device > Link with phone number/code and type the code printed in your console.

Device Management

const all = await container.getAllDevices() // Device[]
const byJID = await container.getDevice('[email protected]') // Device | null
const first = await container.getFirstDevice() // Device (existing first or new)

Tip: If you want multiple concurrent sessions, persist device JIDs and use getDevice(jid) instead of getFirstDevice().

Media Upload and Send (image)

import fs from 'node:fs/promises'

const data = await fs.readFile('./img.png')
const up = await client.uploadBytes(data, 'image')
const img: any = {
    URL: up.url,
    directPath: up.direct_path,
    mediaKey: up.media_key, // base64
    fileEncSHA256: up.file_enc_sha256,
    fileSHA256: up.file_sha256,
    fileLength: up.file_length,
    mimetype: 'image/png',
    caption: 'Hello with image'
}
const message: proto.WAWebProtobufsE2E.IMessage = { imageMessage: img }
await client.send('[email protected]', message)

Download by Direct Path

const buf = await client.downloadByPath({
    direct_path: '...',
    enc_sha256: '...', // base64
    sha256: '...', // base64
    media_key: '...', // base64
    file_length: 12345,
    type: 'image', // one of: image | video | audio | document | history | appstate | sticker-pack | thumbnail-link
    mms_type: '' // optional
})
await fs.writeFile('./downloaded.bin', buf)

Presence and Group Invite

await client.sendPresence('available')
const invite = await client.getGroupInviteLink('[email protected]')
console.log('Invite link:', invite)

Events (minimal loop)

;(async () => {
    for await (const ev of client.events(60000)) {
        if (ev.type !== 'timeout') console.log('[event]', ev.type)
    }
})().catch(console.error)

Workers (non-blocking)

If you want to run multiple clients concurrently without blocking the main thread, use the built-in workers. Each client can have its own event worker, and you can also offload the QR polling to a worker.

Event worker:

import { spawnEventWorker } from './dist/index.js'

const events = spawnEventWorker(client, 500) // timeoutMs = 500ms

const unEv = events.onEvent((ev) => {
    if (ev.type !== 'timeout') console.log('[worker event]', ev.type)
})
const unErr = events.onError((err) => console.error('[worker error]', err))

// ... later
await events.stop()
unEv()
unErr()

QR worker:

import { spawnQRWorker } from './dist/index.js'

const qr = spawnQRWorker(client, 2000) // timeoutMs = 2s
const unQREv = qr.onEvent((ev) => console.log('[qr]', ev.event === 'code' ? ev.code : ev.event))
const unQRErr = qr.onError((err) => console.error('[qr worker error]', err))

// ... later
await qr.stop()
unQREv()
unQRErr()

Logging Configuration (optional)

The bridge supports runtime log configuration for Database and Client modules. Settings apply to newly created containers/clients after the call.

// Local repo import path:
import { native } from './dist/native.js'

// Or, if installed as a dependency (not re-exported by index):
// import { native } from 'whatsmeow-node/dist/native.js'

await native.setLogOptions({
    database: 'none', // DEBUG | INFO | WARN | ERROR | none
    client: 'INFO',
    color: false
})

Running the Comprehensive Example

src/example.ts is a feature-rich, flag-driven example. Build and run:

npm run build
node dist/example.js --MODE=qr --ECHO_BOT=true \
  --LOG_CLIENT=INFO --LOG_DB=none --LOG_COLOR=false \
  [email protected] --SEND_TEXT="hello"

Supported flags (also via env vars of the same name):

  • MODE=qr|pairphone
  • DIALECT=sqlite3|postgres
  • ADDRESS=... (SQL DSN)
  • DEVICE_JID=...
  • LOG_CLIENT=DEBUG|INFO|WARN|ERROR|none
  • LOG_DB=DEBUG|INFO|WARN|ERROR|none
  • LOG_COLOR=true|false
  • ECHO_BOT=true|false
  • USE_QR_WORKER=true|false
  • EVENT_TIMEOUT=ms
  • QR_TIMEOUT=ms
  • PHONE=digits (for pairphone)
  • PUSH=true|false, CLIENT_TYPE=1, DISPLAY_NAME="Browser (OS)"
  • PRESENCE=available|unavailable
  • WAIT_LOGIN=true|false
  • WAIT_LOGIN_MS=ms
  • SEND_TO=jid, SEND_TEXT=...
  • SEND_IMAGE=path, IMAGE_CAPTION=...
  • INVITE_JID=...

API Surface (summary)

  • Container
    • getFirstDevice()
    • getAllDevices()
    • getDevice(jid)
  • Client
    • connect(), isLoggedIn(), waitForConnection()
    • getQRChannel(), events(timeoutMs)
    • send(to, message, extra?) (typed via proto.WAWebProtobufsE2E.IMessage)
    • pairPhone(phone, showPush, clientType, displayName)
    • sendPresence(state), subscribePresence(jid), sendChatPresence(...)
    • uploadBytes(data, type), downloadByPath(params)
    • getGroupInviteLink(jid, reset?), disconnect()
  • native (not re-exported by index)
    • setLogOptions(opts) and low-level wrappers

License

MPL-2.0