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

baileys-wrap

v0.1.0

Published

A strongly-typed abstraction layer over Baileys that maps raw WhatsApp events into a clean, predictable domain API.

Downloads

102

Readme

baileys-wrap

A thin abstraction layer over Baileys that replaces its raw, complex event system with a clean, strongly-typed API.

Instead of wrestling with verbose payloads, multiple events firing for the same user action, and deeply nested structures — baileys-wrap does that mapping once so you can focus on your bot's logic.


Features

  • Normalized Message type covering text, image, video, audio, document, sticker, location, contact, poll, event, and reaction
  • Automatic detection of forwarded / edited / deleted messages
  • Quoted message support including media download references
  • Group metadata and permission events with intuitive boolean semantics
  • Full TypeScript support — all events and payloads are strictly typed
  • Two usage modes: managed session (WhatsAppClient) or bring-your-own-socket (WhatsAppEventMapper)

Installation

pnpm add baileys-wrap @whiskeysockets/baileys

Baileys is a peer dependency — install it alongside this package.

Note: Requires @whiskeysockets/baileys >= 7.0.0-rc.9. Not compatible with Baileys 6.x.


Quick start

Option A — Managed session (WhatsAppClient)

WhatsAppClient handles everything: auth state, QR generation, automatic reconnection, and credential cleanup on logout.

import { WhatsAppClient } from 'baileys-wrap';
import qrcode from 'qrcode-terminal';

const client = new WhatsAppClient();
await client.start();

client.on('session.qr', (qr) => qrcode.generate(qr, { small: true }));
client.on('session.ready', () => console.log('Connected!'));

client.on('session.close', (reason) => {
	if (reason === 'logged_out') console.warn('Session revoked — waiting for new QR scan');
	else if (reason === 'reconnecting') console.warn('Connection lost — reconnecting...');
});

client.on('message.new', (msg) => {
	console.log(`[${msg.chatId}] ${msg.sender.id}: ${msg.content?.text}`);
});

Option B — Bring your own socket (WhatsAppEventMapper)

Already managing the Baileys socket yourself? Plug in WhatsAppEventMapper directly:

import makeWASocket, { useMultiFileAuthState } from '@whiskeysockets/baileys';
import { WhatsAppEventMapper } from 'baileys-wrap';

const { state, saveCreds } = await useMultiFileAuthState('auth');
const sock = makeWASocket({ auth: state });
sock.ev.on('creds.update', saveCreds);

const mapper = new WhatsAppEventMapper(sock);
mapper.on('message.new', (msg) => console.log(msg));

Custom auth state

By default credentials are stored on disk in ./baileys_auth_info. To use a different backend, pass a factory function that returns a WhatsAppAuthState to the constructor.

A WhatsAppAuthState has three fields:

| Field | Type | Description | | -------------- | --------------------- | ---------------------------------------- | | state | AuthenticationState | Baileys auth state (creds + signal keys) | | saveCreds | () => Promise<void> | Called by Baileys on credential updates | | clearSession | () => Promise<void> | Called on logout to wipe stored data |

Why a factory function? After a logout the session is cleared and a fresh instance must be created — otherwise stale in-memory credentials would suppress the QR prompt on reconnect.

PostgreSQL

A ready-to-use Postgres implementation is included:

import { WhatsAppClient, usePostgresAuthState } from 'baileys-wrap';
import { Pool } from 'pg';

const pool = new Pool({ connectionString: process.env.DATABASE_URL });

const client = new WhatsAppClient(() => usePostgresAuthState({ pool, sessionName: 'my-bot' }));

await client.start();

The table whatsapp_auth is created automatically on first run. Multiple sessions can share the same database — they are isolated by sessionName.


Downloading media

When a message contains media, msg.media.url holds a WAMessage reference you can pass directly to Baileys' downloadMediaMessage:

import { downloadMediaMessage, type WAMessage } from '@whiskeysockets/baileys';
import { mkdir, writeFile } from 'fs/promises';

client.on('message.new', async (msg) => {
	if (msg.type !== 'image' || !msg.media) return;

	const buffer = await downloadMediaMessage(msg.media.url as WAMessage, 'buffer', {});
	const ext = msg.media.mimetype.split('/')[1] ?? 'jpg';

	await mkdir('downloads', { recursive: true });
	await writeFile(`downloads/${msg.id}.${ext}`, buffer);
});

The same pattern works for quoted message media via msg.quotedMessage?.media.


Type reference

Message

| Field | Type | Description | | --------------- | ------------------- | ------------------------------------------------------------------------------------------- | | id | string | Unique message ID | | chatId | string | JID of the chat (individual or group) | | sender | SenderProp | Who sent the message | | status | MessageStatus | 'new' · 'forwarded' · 'edited' · 'deleted' | | type | MessageType | See message types below | | timestamp | number | Unix timestamp in milliseconds | | content | ContentProp? | Present for text-bearing messages. Contains text and hasLink | | media | MediaProp? | Present for image, video, audio, document, sticker. Contains url and mimetype | | mentions | string[] \| 'all' | Mentioned JIDs, or 'all' for @everyone | | quotedMessage | QuotedMessage? | The message being replied to, if any | | reaction | ReactionProp? | Present for reaction type | | location | LocationProp? | Present for location type | | contact | ContactProp? | Present for contact type | | poll | PollProp? | Present for poll type | | event | EventProp? | Present for event type | | waMessage | WAMessage | Raw Baileys message — always available as an escape hatch |

MessageType

'text' · 'image' · 'video' · 'audio' · 'document' · 'sticker' · 'reaction' · 'location' · 'contact' · 'poll' · 'event' · 'unknown'

SenderProp

| Field | Type | Description | | -------- | --------- | --------------------------------------- | | id | string | Sender JID | | name | string? | Push name (display name), if available | | fromMe | boolean | true when the message was sent by you |


Event reference

Session events (WhatsAppClient only)

| Event | Payload | Description | | --------------- | ---------------------------------------------------------------- | --------------------------------------------------- | | session.qr | qr: string | New QR code ready to scan. Rotates every ~20 s | | session.ready | — | Authenticated and connected | | session.close | reason: 'logged_out' \| 'reconnecting' \| 'connection_failure' | Connection closed | | session.error | error: Error | Unrecoverable error (e.g. outdated Baileys version) |

Message events

| Event | Payload | Description | | ----------------- | --------- | ------------------------------------------------------------------------------- | | message.new | Message | New inbound message | | message.forward | Message | Forwarded message | | message.edit | Message | Edited message — id matches the original | | message.delete | Message | Revoked message — id matches the original. Most payload fields will be absent |

Group events

| Event | Payload | Description | | ------------------------- | -------------------------------------------- | -------------------------------------------- | | group.update.metadata | { chatId, author, type, value? } | Name or description changed | | group.update.permission | { chatId, author, permission, value } | Permission toggled | | group.update.members | { chatId, author, action, participants[] } | Members added / removed / promoted / demoted |

group.update.permission — permission semantics

| permission | value: true | value: false | | ---------------- | -------------------------------------- | --------------------------------- | | canEdit | All members can edit group info | Only admins can edit group info | | sendMessages | All members can send messages | Only admins can send messages | | addMembers | All members can add others | Only admins can add members | | approveMembers | New members require admin approval | New members join without approval |

approveMembers is the exception: value: true means approval is required (more restrictive).


License

MIT