CONTEXT — waconector
Problema e visão
Existem várias APIs não-oficiais de WhatsApp (uazapi, WAHA, Evolution GO, Wuzapi, Whapi, Z-API, WPPConnect, QuePasa, izapia), todas fazendo essencialmente as mesmas ~20 operações — conectar instância/QR, enviar texto e mídia, receber mensagens, acks, grupos, contatos — mas com auth, endpoints, payloads e webhooks diferentes.
O waconector é um pacote npm que fixa um contrato único e implementa um adapter por provider, no espírito do Vercel AI SDK (um SDK, N providers de LLM). Trocar de provider deve significar trocar apenas a configuração.
Linguagem do domínio
| Termo | Significado |
|---|---|
| Provider | Uma API não-oficial de WhatsApp (WAHA, Evolution GO, Z-API, ...). |
| Adapter | Implementação do contrato WaAdapter para um provider. É "burro": só traduz (map-out/map-in). |
| Conector | createConnector(adapter) — camada de ergonomia/política: validação, capabilities, eventos, parsing seguro. |
| Capability | Operação que um adapter declara suportar (messages.sendText, instance.connect...). Chamar fora do conjunto lança UnsupportedCapabilityError. |
| Evento canônico | Formato único para webhooks de qualquer provider (message.received, message.ack, connection.update, unknown...). |
| Instância | Sessão de um número de WhatsApp no provider (Evolution chama "instance", WAHA "session", Whapi "channel"). |
| Dossiê | Documento por provider em docs/providers/ com auth, endpoints e payloads reais de webhook, escrito ANTES do adapter. |
| Fixture | Payload real capturado de um provider, versionado em src/adapters/<nome>/fixtures/, base dos testes. |
| Suite de contrato | test/contract/adapter-contract.ts — testes que TODO adapter precisa passar. |
| ChatId canônico | Telefone só-dígitos (E.164 sem +) ou JID explícito (...@g.us, ...@s.whatsapp.net). |
Princípios (ver ADRs)
- Normalizar o comum, preservar o específico — todo objeto normalizado carrega
raw(ADR-0002). - Capabilities declaradas — sem mínimo denominador comum, sem vazamento de especificidades (ADR-0005).
- Webhooks canônicos e framework-agnostic —
parsenunca lança; payload desconhecido vira eventounknown(ADR-0003). - Zero dependências de runtime —
fetchnativo, Node ≥ 20 (ADR-0004). - Pacote único com subpath exports —
waconector/waha,waconector/evolution... (ADR-0001). - Segurança — tokens nunca aparecem em logs/erros (redação via
redactSecrets).
Detalhes Técnicos
- Linguagem/runtime: TypeScript 5.9 strict, Node ≥ 20 (fetch nativo), ESM + CJS.
- Build: tsup (entradas
index,testing/index,adapters/<provider>/index; ESM + CJS +.d.ts/.d.cts). - Testes: vitest (unit + suite de contrato) + smoke test do pacote empacotado (
scripts/smoke.mjs). - QA/cobertura:
npm run test:coverage(@vitest/coverage-v8). Thresholds emvitest.config.tssão um piso calibrado contra o baseline real (não uma meta aspiracional) — travam regressão, não medem "qualidade" por si só. CI roda a versão com cobertura e publica o relatório como artifact. - Lint/format: Biome.
- Versionamento/publicação: changesets + GitHub Actions (
release.yml, requer secretNPM_TOKEN; publica com provenance). Desde o v1.0 (semver de verdade): breaking changes exigem bump major + issue de discussão prévia — não são mais aceitáveis em minors como na fase 0.x. - Contribuição: CONTRIBUTING.md, templates de issue (bug/feature/novo adapter) e template de PR com checklist de QA em
.github/. Dependabot para deps (npm + GitHub Actions), semanal. - Branches:
mainprotegido (sem push/merge direto);developé o branch de integração. PRs paramainsó são aceitos vindos dedevelop— aplicado pelo jobguard-main-sourceemci.yml(o GitHub não tem regra de proteção nativa para restringir a origem de um PR). A configuração de branch protection em si é feita pelo dono do repo no GitHub, não por um agente. - Docs/wiki sempre em dia:
docs-sync.ymlroda a cada merge emmain(Claude Code Action) e atualiza docs/ADRs/wiki quando o diff genuinamente exigir, commitando emdevelop(nunca emmain). Requer secretCLAUDE_CODE_OAUTH_TOKEN(mesmo usado porclaude-code-review.yml) — ver CONTRIBUTING.md. - Estrutura:
src/core/— tipos, erros, capabilities, http client, eventos, conector.src/adapters/<provider>/— (F1+) um diretório por adapter, comfixtures/.src/testing/—MockAdapterpublicado emwaconector/testing.test/contract/— suite de contrato compartilhada.docs/providers/— dossiês.
Metodologia por adapter (F1+)
- Dossiê em
docs/providers/<nome>.md(auth, modelo de sessão, endpoints, payloads reais de webhook). - Fixtures versionados.
- Adapter:
map-out(canônico → provider) emap-in(provider → canônico). - Suite de contrato passando 100%.
- Smoke test contra instância real (WAHA/Evolution GO/Wuzapi/QuePasa rodam via Docker; comerciais exigem conta).
Roadmap
- F0 — Fundação ✅ (2026-07-10): scaffold, core completo, MockAdapter, suite de contrato, CI/release.
- F1 — Prova da abstração ✅ (2026-07-11): adapters WAHA + Evolution GO (
instance.connect/status/logout,messages.sendText/sendMedia,webhooks.parse), dossiês emdocs/providers/, fixtures e testes de contrato próprios, auditados adversarialmente contra a documentação/código-fonte oficial de cada provider.instance.pairingCodedeliberadamente fora do escopo (contratoInstanceApi.connect()não recebe telefone).v0.1.0publicado no npm. - F2 — Largura ✅ (2026-07-11): adapters uazapi, Z-API e Wuzapi implementados e auditados adversarialmente (mesmo rigor da F1).
v0.2.0publicado no npm.reply/quotejá estava coberto (campoquotedId, ver ADR-0008).messages.sendReactionadicionado ao core e retrofitado nos 5 adapters existentes (ADR-0008), cada um mapeado para a particularidade real do provider de remover reação (sentinel"remove"no Evolution GO/Wuzapi, endpoint dedicado na Z-API, emoji vazio no WAHA/uazapi). Capabilitygroups.*completa (ADR-0009): as 14 operações pesquisadas — núcleo (create/getInfo/list), participantes (add/remove/promote/demoteParticipants), configurações (updateSubject/updateDescription/updatePicture) e convites+saída (getInviteLink/revokeInviteLink/joinViaInviteLink/leaveGroup) — implementadas nos 5 adapters. Webhooks de grupo (GroupUpdateEvent) populados em 4/5 (WAHA completo, Evolution GO/Wuzapi reconstruído do whatsmeow, Z-API só participantes); uazapi sem parsing estruturado por falta de qualquer payload de exemplo na doc oficial. Capabilitycontacts.*completa (ADR-0010): descoberta + perfil (list/get/checkExists/getProfilePicture/getAbout, 4/5 adapters — uazapi semgetAbout) e moderação (block/unblock/listBlocked,listBlockedem 3/5 — WAHA e Z-API sem endpoint) implementadas.getPresencefora do escopo por ser majoritariamente assíncrono/webhook (incremento futuro à parte). - F3 — Profundidade e DX ✅: adapter Whapi ✅ (2026-07-11) — capabilities núcleo (
instance.connect/status/logout,messages.sendText/sendMedia,webhooks.parse), dossiê emdocs/providers/whapi.md, auditado adversarialmente (achou e corrigiu 1 bug real: legenda de mídia recebida via webhook nunca extraída paraWaMessage.text, inconsistente com o adapter Z-API).messages.sendReaction/instance.pairingCode/groups.*/contacts.*confirmados suportados pelo provider mas deliberadamente não implementados nesta fase. Adapter QuePasa ✅ (2026-07-11) — capabilities núcleo menores que o padrão dos demais adapters:instance.status/logout(logout é soft-stop — desconecta mas preserva credenciais salvas),messages.sendText/sendMedia,groups.getInviteLink,contacts.getProfilePicture,webhooks.parse, dossiê emdocs/providers/quepasa.md.instance.connectdeliberadamente NÃO implementada (limitação técnica real:GET /scandevolve PNG binário cru, incompatível com oHttpClientatual, que decodifica toda resposta não-JSON como texto UTF-8);instance.pairingCodefora de escopo pelo motivo usual. Repositório oficial (nocodeleaks/quepasa) bloqueado no GitHub por DMCA (módulo de VoIP, não relacionado a mensagens) — pesquisa feita em três forks/mirrors não bloqueados, com o dossiê documentando a metodologia e o nível de confiança por seção. Auditoria adversarial achou e corrigiu 1 bug real (getProfilePicturelia o campo errado do envelope de resposta) e corrigiu 3 afirmações incorretas de "recurso não existe". Lista de providers-alvo atualizada: Zapo saiu (é uma lib cliente Node, sem endpoints HTTP — não se encaixa no modelo de adapter) e foi substituída por WPPConnect Server (self-hosted, Docker, mesmo perfil de WAHA/Evolution GO). Adapter WPPConnect Server ✅ (2026-07-11) — capabilities mais amplas entre os adapters F3 até agora:instance.connect/status/logout,messages.sendText(commentionsviaPOST /send-mentioned)/sendMedia/sendReaction, 13 operações degroups.*(create/getInfo/participantes/subject/description/picture/invite links/join/leave),contacts.checkExists/block/unblock/listBlocked,webhooks.parse, dossiê emdocs/providers/wppconnect.md. Sourcing limpo (diferente do QuePasa): docs Swagger acessíveis e repositório oficial ativo, verificado antes de qualquer suposição.instance.pairingCodenão declarada (mesmo obstáculo estrutural de todo adapter deste pacote);groups.listecontacts.list/get/getProfilePicture/getAboutdeliberadamente fora do escopo (endpoints existem, mas sem shape de resposta confirmado pela pesquisa, ou marcados deprecated pelo próprio provider). Auditoria adversarial encontrou e corrigiu 2 bugs reais de corrupção silenciosa de dados (não lançavam exceção, apenas perdiam o id real):POST /send-messagee os endpoints de mídia devolvemresponsecomo array de um elemento, não o objeto bare esperado;POST /create-groupaninhaid/nameemgroupInfo[0], não diretamente na raiz.v0.3.0publicado no npm (fechamessages.sendReaction/groups.*/contacts.*da F2 e os adapters Whapi/QuePasa/WPPConnect da F3). Site de documentação (VitePress + GitHub Pages) publicado emhttps://alltomatos.github.io/waconector/, consumindodocs/existente (CONTEXT.md, ADRs, dossiês) sem duplicar conteúdo; páginadocs/capabilities.mdgerada 100% do código (scripts/generate-capabilities-matrix.mjs, mesma técnica de instanciação fake do smoke test), com checagem de CI (ci.yml) garantindo que nunca fica desatualizada. Comandonpx waconector doctor --provider <nome>(ADR-0011) para diagnóstico de conectividade/auth via variáveisWACONECTOR_*(só leitura,instance.status(), nuncaconnect()), implementado como entry point ESM-only próprio (src/cli/,node:util.parseArgs, zero dependência de runtime — ADR-0004). Exemplos executáveis emexamples/expresseexamples/nextjs(App Router), usandoMockAdapterpor padrão. Fecha a F3 por completo.v1.0.0publicado no npm (ver Epic 5/6 emORCHESTRATOR-ROADMAP.md). Atualização pós-v1.0: a frase acima sobremessages.sendReaction/groups.*/contacts.*do Whapi estarem "deliberadamente não implementados" ficou desatualizada — uma auditoria de gaps encontrou que todas essas 22 capabilities tinham endpoint confirmado e implementável; foram implementadas no Epic 6 (Whapi agora em 29/30). A frase acima sobregroups.listecontacts.list/get/getProfilePicture/getAboutdo WPPConnect estarem "fora do escopo por shape de resposta não confirmado" também ficou desatualizada — a mesma auditoria de gaps encontrou que a lib subjacente@wppconnect-team/wppconnecttipa ou expõe o shape de resposta de todas as 5; foram implementadas no Epic 6 (WPPConnect agora em 29/30,groups.listviaPOST /list-chatsem vez do endpoint deprecadoGET /all-groups). - v1.0 ✅ (2026-07-11): publicado no npm. 8 adapters passando 100% da suite de contrato (bem acima do piso de "3+"); API pública estável desde o
v0.1.0(1 única breaking change em toda a história, ocorrida antes de existir qualquer adapter além de WAHA/Evolution GO — todas as releases seguintes foram 100% aditivas). A partir daqui, breaking changes exigem bump major. - Adapter izapia ✅ (2026-07-16): 9º provider suportado — SaaS multi-tenant próprio do usuário (
github.com/alltomatos/izapia, repo privado), construído sobretulir/whatsmeow(v0.2.0, ~100 endpoints, confirmado ao vivo + código-fonte). Na época, segunda maior cobertura entre todos os adapters do pacote (64/68 capabilities, atrás só do Whapi, 66/68 — ver nota da Epic 12 abaixo para os números atuais, pós-expansão do contrato) —instance.connect/status/logout(fluxo de duas etapas: sessão criada fora do contratoWaAdapter,connect()só pareia),messages.*completo excetoforward(endpoint real só aceita texto pronto, e o izapia não guarda histórico para resolvê-lo a partir de ummessageId— descasamento real de forma),groups.*/contacts.*/chats.*/presence.*/labels.*completos,channels.*excetodeleteebusiness.getProfilesemupdateProfile(ambos501 NOT_IMPLEMENTEDhoje — owhatsmeownão expõe essas operações), ecalls.*— com umCallManagerde voz genuíno por trás (sinalização + bridge WebRTC), diferente da "chamada vazia" dos demais 8 adapters, ainda que o contrato canônico atual não modele áudio. Dossiê emdocs/providers/izapia.md. Fatiado e implementado via Epic 10/issues #44-#57 (ORCHESTRATOR-ROADMAP.md). - Expansão do contrato:
messages.download+channels.*mensageria ✅ (2026-07-17): motivada por uma pergunta direta do usuário durante a Epic 10 (por que o izapia, com ~100 endpoints, só declarava 64/68 capabilities do contrato central) — a resposta é o design pretendido (ADR-0005: o contrato é o vocabulário COMUM entre providers, não o teto de nenhum um deles), mas isso motivou uma pesquisa dedicada (Epic 11) sobre se algum "extra" do izapia convergia o suficiente com outros providers já implementados para virar capability nova. Duas rodadas recombinando dossiês existentes não confirmaram convergência; uma terceira rodada — baixando e inspecionando ao vivo os specs OpenAPI reais de uazapi/Whapi, em vez de só reler dossiê — reverteu a conclusão por completo e confirmou 4 providers paramessages.download(Evolution GO, uazapi, Whapi, izapia) e 2-4 para o namespacechannels.*mensageria (getMessagesnos mesmos 4;markViewed/reactToPostem uazapi/izapia) — bem acima do piso histórico de 2+ (ver ADR-0019,calls.makepromovido com só 2/8). Lição registrada: recombinar pesquisa antiga tem valor limitado quando a pergunta é genuinamente nova.messages.download(ADR-0020) baixa o anexo de uma mensagem recebida —DownloadMediaInput.raw(oWaMessage.raworiginal) é necessário para os providers STATELESS (Evolution GO, Whapi, izapia — nenhum guarda histórico server-side), enquanto uazapi resolve só commessageId;MediaRefganha um campo novoid?: string.channels.getMessages/markViewed/reactToPost(ADR-0021,ChannelPostnovo) cobrem o CONTEÚDO dos posts de um canal — lacuna real do namespacechannels.*(ADR-0017), que até aqui só cobria metadados. Enum de capabilities cresce de 68 para 72; cobertura pós-implementação: izapia e Whapi empatados na maior cobertura do pacote (68/72 cada), uazapi em 66/72, Evolution GO em 53/72 — verdocs/capabilities.md(gerado do código, sempre a fonte da verdade para os números atuais). Também estendeu oHttpClient(core) comresponseType: 'base64'para suportar respostas binárias cruas (necessário para oGET /media/{MediaID}do Whapi). Fatiado e implementado via Epic 12/issues #60-#67 (ORCHESTRATOR-ROADMAP.md).
Riscos mapeados
- APIs não-oficiais mudam sem aviso → fixtures + suite de contrato tornam quebras detectáveis; cada adapter documenta a versão testada.
- ToS do WhatsApp → o pacote é um client HTTP para APIs de terceiros; disclaimer no README; sem afiliação com a Meta.
- Segredos → nunca logar tokens;
redactSecretsem todo texto de erro.