wa-spec
Daily-extracted WhatsApp Web protocol bindings.
Seven independent packages, one shared fetcher:
@vinikjkkj/wa-fetcher— downloads every loaded JS bundle from web.whatsapp.com to disk and writes a manifest. That's all it does — extraction lives in the consumers.@vinikjkkj/wa-mex— Mex GraphQL persist IDs, variable shapes, and response shapes. Publishesindex.json(IR) +index.js(CJS) +index.d.ts(TS types).@vinikjkkj/wa-proto— Protobuf message definitions. PublishesWAProto.proto(SDL) +dist/index.{js,d.ts}(pbjs/pbts compiled).@vinikjkkj/wa-appstate— AppState (Syncd) action schemas: wire name, collection, version, value field and index shape for every multi-device sync action. Publishesindex.json+index.js+index.d.ts.@vinikjkkj/wa-xml— XML/stanza schemas for every Smax IQ operation AND every incoming server-initiated stanza (<notification>,<receipt>,<message>,<chatstate>,<presence>,<ib>,<call>,<status>, etc.): per-op request + response trees AND per-handler stanza trees (tag, attrs, children, content) recovered fromWASmax*RPCmodules- the three
WAWebCommsHandle*Stanzadispatch tables. Publishesindex.json+index.js+index.d.ts.
- the three
@vinikjkkj/wa-wam— WAM (analytics/metrics) event schemas: event id + falco name + channel + weights + per-field id/type/enum for everydefineEventscall, plus globals, private-stats bucket ids, and resolved enum value sets. Publishesindex.json+index.js+index.d.ts.@vinikjkkj/wa-version— just the WhatsApp Web version string (WA_VERSION) the rest of the packages were extracted from. Tiny standalone so consumers can pin handshake / WAM event-version / pairing-flow checks to the same build. Publishesversion.json+index.js+index.d.ts.
External consumers (e.g. wa-diff) can
depend on @vinikjkkj/wa-fetcher directly without pulling proto/mex/appstate.
Architecture
┌─────────────────────────────────────────────────────────────────┐
│ @vinikjkkj/wa-fetcher │
│ │
│ puppeteer-real-browser → web.whatsapp.com │
│ │
│ dump/ │
│ ├─ raw/<wa-version>/*.js every loaded bundle │
│ └─ manifest.json { waVersion, bundles[] } │
└─────────────────────────────────────────────────────────────────┘
│
┌──────────┼──────────┬──────────────┐
↓ ↓ ↓ ↓
┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌─────────────┐ ┌──────────────┐
│ @vinikjkkj/│ │ @vinikjkkj/│ │ @vinikjkkj/│ │ @vinikjkkj/│ │ @vinikjkkj/│ │ @vinikjkkj/ │ │ wa-diff │
│ wa-mex │ │ wa-proto │ │ wa-appstate│ │ wa-xml │ │ wa-wam │ │ wa-version │ │ (external) │
│ │ │ │ │ │ │ │ │ │ │ │ │ │
│ index.json │ │ WAProto. │ │ index.json │ │ index.json │ │ index.json │ │ version.json│ │ <its own> │
│ index.js │ │ proto │ │ index.js │ │ index.js │ │ index.js │ │ index.js │ │ │
│ index.d.ts │ │ dist/ │ │ index.d.ts │ │ index.d.ts │ │ index.d.ts │ │ index.d.ts │ │ │
└────────────┘ └────────────┘ └────────────┘ └────────────┘ └────────────┘ └─────────────┘ └──────────────┘
Each extractor reads the raw bundle directory directly. No intermediate JSON contract between fetcher and consumers — bundles are the source of truth.
Daily flow
GitHub Actions cron fires once a day:
- Fetcher dumps bundles
- Each extractor regenerates its outputs
- Diff against previous commit; if anything changed, bump version and publish
Local dev
# One-time
npm install
# Pull fresh bundles + regenerate everything
npm run fetch
npm run extract:mex
npm run extract:proto
npm run extract:appstate
npm run extract:xml
npm run extract:wam
npm run extract:version
Packages
| Package | Purpose | Output |
|---|---|---|
fetcher |
Scrape WA Web bundles | dump/raw/*.js + dump/manifest.json |
mex |
Mex GraphQL bindings | index.json + index.js + index.d.ts |
proto |
Protobuf bindings | WAProto.proto + dist/index.{js,d.ts} |
appstate |
AppState (Syncd) schemas | index.json + index.js + index.d.ts |
xml |
Smax IQ stanza schemas (request + response trees) | index.json + index.js + index.d.ts |
wam |
WAM (analytics/metrics) event schemas | index.json + index.js + index.d.ts |
version |
WhatsApp Web version string (WA_VERSION) |
version.json + index.js + index.d.ts |