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

@zapo-js/wam

v0.1.1

Published

WhatsApp Web WAM (analytics/telemetry) plugin for zapo-js — emits the client-side metrics batches WA Web sends, for wire parity and anti-fingerprinting

Readme

@zapo-js/wam

WhatsApp Web WAM (analytics/telemetry) plugin for zapo-js. It emits the client-side w:stats metrics batches WA Web sends, for wire parity and anti-fingerprinting.

WA Web continuously uploads WAM (Falco) telemetry: message send/receive metrics, connection lifecycle, sync progress, UI interactions, and more. A headless client that uploads none of these has a conspicuous gap in its event profile. This plugin closes that gap by emitting the events a headless client can truthfully produce, and by fabricating plausible ambient UI activity (on by default).

Install

npm install @zapo-js/wam

zapo-js is a peer dependency. The WAM event registry (@vinikjkkj/wa-wam) is bundled.

Usage

import { WaClient } from 'zapo-js'
import { wamPlugin } from '@zapo-js/wam'

const client = new WaClient({
    store,
    sessionId: 'main',
    plugins: [wamPlugin()]
})

// Protocol events auto-emit as the client runs. You can also commit your own:
client.wam.commit('UiAction', { uiActionType: 'CHAT_OPEN' })

Synthetic UI telemetry is on by default. Disable it (or tune it) with:

plugins: [wamPlugin({ syntheticUi: false })]

Events emitted

131 of the registry's 426 events. They come from two independently toggled sources:

| Source | Flag | Default | Count | | ------------------ | ------------- | ------- | ----- | | Protocol lifecycle | autoEmit | on | 19 | | Integrator actions | autoEmit | on | 18 | | Synthetic UI | syntheticUi | on | 94 |

Protocol lifecycle (19) - derived from real inbound/outbound stanzas: E2eMessageSend, E2eMessageRecv, MessageSend, MessageReceive, WebcMessageSend, ReceiptStanzaReceive, MessageHighRetryCount, EditMessageSend, ClockSkewDifferenceT, GroupJoinC, WaOldCode, WebcSocketConnect, WebcStreamModeChange, WebcPageResume, WebcRawPlatforms, MdBootstrapHistoryDataReceived, UnknownStanza, OfflineCountTooHigh, WebWamForceFlush

Integrator actions (18) - the client's own sends and app-state mutations: ForwardSend, ReactionActions, PollsActions, SendDocument, StickerSend, PinInChatMessageSend, RevokeMessageSend, SendRevokeMessage, MessageDeleteActions, WaFsGroupJoinRequestAction, GroupCreate, GroupCreateC, EphemeralSettingChange, DisappearingModeSettingChange, ChatMute, ChatAction, StatusMute, MdSyncdDogfoodingFeatureUsage

Synthetic UI (94) - plausible activity, every event grounded in WA Web's own emit (only the field subset WA sets, with plausible values). The base stream — UiAction (chat/image/info opens), WebcChatOpen, AttachmentTrayActions, ContactSearchExperience, MemoryStat, UserActivity/TsBitArray, WebcEmojiOpen, StickerPickerOpened, AboutConsumption/AboutInteraction, WebcMediaLoad — plus a weighted ambient table of ~70 more UI events and 9 message-anchored ones (media playback/compose, mention picker, group catch-up, …). The table lives in src/synthetic/fabrications.ts.

9 events are capability-gated (default off) — firing them on an account that lacks the surface is itself a tell: ChannelOpen needs channels; the Community* events and GroupJourney need communities; the business/SMB events (WaShopsManagement, MdChatAssignmentSecondaryAction, StructuredMessageBuyerInteraction) need business.

Coverage

131 / 426 registry events (~31%). The rest are dominated by data a headless client cannot produce or plausibly fake — browser/runtime internals, device and OS state, mobile-app-only flows, crypto internals, ads, server-side aggregates — plus events carried on the private (non-w:stats) channels.

Two disciplines keep the emitted set a faithful fingerprint:

  • Auto-emitted protocol and integrator events are sent only when every field is honestly derivable from real client activity.
  • Synthetic UI events are fabricated, but each replicates WA Web's actual emit — only the field subset WA sets, with plausible values — verified against the deobfuscated web bundle. They are jittered, rate-limited, and confined to configurable active hours; a badly-timed or skeleton event is a worse tell than none, so events WA Web itself never fires are left out.

Options

wamPlugin(options), all optional:

| Option | Default | Description | | -------------------------- | ------------- | ------------------------------------------------------------------------ | | autoEmit | true | Emit protocol + integrator-action events by observing the client | | syntheticUi | true | Fabricate plausible UI telemetry (false to disable, or options object) | | serviceImprovementOptOut | false | service_improvement_opt_out consent bit | | appVersion | WA_VERSION | Override the advertised app version | | flushIntervalMs | 5000 | Coalesce window before a non-empty batch flushes | | maxBufferSize | 50000 | Byte size that forces an immediate flush | | logLevel | host client's | Minimum log level for the plugin |

syntheticUi also accepts an options object. Besides jitter/interval and activeHoursStartHour/activeHoursEndHour tuning, channels, communities, and business (all default false) enable the capability-gated ambient events for accounts that actually have those surfaces.

How it works

  1. Accumulate: committed events buffer into a per-channel batch whose globals derive from the client's own device identity, so they agree with the pairing ClientPayload.
  2. Flush: on the coalesce interval, on reaching maxBufferSize, or on dispose.
  3. Upload: as the <iq type="set" xmlns="w:stats"><add t> stanza WA Web sends. Best-effort; transient failures retry with backoff, and a permanently failing batch is dropped, never surfaced.

autoEmit observes the client's typed events and raw stanzas and maps each to the WAM event WA Web fires at the same point. syntheticUi fabricates UI telemetry — a weighted table of wa-web-grounded ambient events plus message-anchored ones — jittered and confined to configurable active hours.