Skip to content

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

TermoSignificado
ProviderUma API não-oficial de WhatsApp (WAHA, Evolution GO, Z-API, ...).
AdapterImplementação do contrato WaAdapter para um provider. É "burro": só traduz (map-out/map-in).
ConectorcreateConnector(adapter) — camada de ergonomia/política: validação, capabilities, eventos, parsing seguro.
CapabilityOperação que um adapter declara suportar (messages.sendText, instance.connect...). Chamar fora do conjunto lança UnsupportedCapabilityError.
Evento canônicoFormato único para webhooks de qualquer provider (message.received, message.ack, connection.update, unknown...).
InstânciaSessã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.
FixturePayload real capturado de um provider, versionado em src/adapters/<nome>/fixtures/, base dos testes.
Suite de contratotest/contract/adapter-contract.ts — testes que TODO adapter precisa passar.
ChatId canônicoTelefone só-dígitos (E.164 sem +) ou JID explícito (...@g.us, ...@s.whatsapp.net).

Princípios (ver ADRs)

  1. Normalizar o comum, preservar o específico — todo objeto normalizado carrega raw (ADR-0002).
  2. Capabilities declaradas — sem mínimo denominador comum, sem vazamento de especificidades (ADR-0005).
  3. Webhooks canônicos e framework-agnosticparse nunca lança; payload desconhecido vira evento unknown (ADR-0003).
  4. Zero dependências de runtimefetch nativo, Node ≥ 20 (ADR-0004).
  5. Pacote único com subpath exportswaconector/waha, waconector/evolution... (ADR-0001).
  6. 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 em vitest.config.ts sã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 secret NPM_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: main protegido (sem push/merge direto); develop é o branch de integração. PRs para main só são aceitos vindos de develop — aplicado pelo job guard-main-source em ci.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.yml roda a cada merge em main (Claude Code Action) e atualiza docs/ADRs/wiki quando o diff genuinamente exigir, commitando em develop (nunca em main). Requer secret CLAUDE_CODE_OAUTH_TOKEN (mesmo usado por claude-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, com fixtures/.
    • src/testing/MockAdapter publicado em waconector/testing.
    • test/contract/ — suite de contrato compartilhada.
    • docs/providers/ — dossiês.

Metodologia por adapter (F1+)

  1. Dossiê em docs/providers/<nome>.md (auth, modelo de sessão, endpoints, payloads reais de webhook).
  2. Fixtures versionados.
  3. Adapter: map-out (canônico → provider) e map-in (provider → canônico).
  4. Suite de contrato passando 100%.
  5. 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 em docs/providers/, fixtures e testes de contrato próprios, auditados adversarialmente contra a documentação/código-fonte oficial de cada provider. instance.pairingCode deliberadamente fora do escopo (contrato InstanceApi.connect() não recebe telefone). v0.1.0 publicado no npm.
  • F2 — Largura ✅ (2026-07-11): adapters uazapi, Z-API e Wuzapi implementados e auditados adversarialmente (mesmo rigor da F1). v0.2.0 publicado no npm. reply/quote já estava coberto (campo quotedId, ver ADR-0008). messages.sendReaction adicionado 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). Capability groups.* 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. Capability contacts.* completa (ADR-0010): descoberta + perfil (list/get/checkExists/getProfilePicture/getAbout, 4/5 adapters — uazapi sem getAbout) e moderação (block/unblock/listBlocked, listBlocked em 3/5 — WAHA e Z-API sem endpoint) implementadas. getPresence fora 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ê em docs/providers/whapi.md, auditado adversarialmente (achou e corrigiu 1 bug real: legenda de mídia recebida via webhook nunca extraída para WaMessage.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ê em docs/providers/quepasa.md. instance.connect deliberadamente NÃO implementada (limitação técnica real: GET /scan devolve PNG binário cru, incompatível com o HttpClient atual, que decodifica toda resposta não-JSON como texto UTF-8); instance.pairingCode fora 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 (getProfilePicture lia 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 (com mentions via POST /send-mentioned)/sendMedia/sendReaction, 13 operações de groups.* (create/getInfo/participantes/subject/description/picture/invite links/join/leave), contacts.checkExists/block/unblock/listBlocked, webhooks.parse, dossiê em docs/providers/wppconnect.md. Sourcing limpo (diferente do QuePasa): docs Swagger acessíveis e repositório oficial ativo, verificado antes de qualquer suposição. instance.pairingCode não declarada (mesmo obstáculo estrutural de todo adapter deste pacote); groups.list e contacts.list/get/getProfilePicture/getAbout deliberadamente 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-message e os endpoints de mídia devolvem response como array de um elemento, não o objeto bare esperado; POST /create-group aninha id/name em groupInfo[0], não diretamente na raiz. v0.3.0 publicado no npm (fecha messages.sendReaction/groups.*/contacts.* da F2 e os adapters Whapi/QuePasa/WPPConnect da F3). Site de documentação (VitePress + GitHub Pages) publicado em https://alltomatos.github.io/waconector/, consumindo docs/ existente (CONTEXT.md, ADRs, dossiês) sem duplicar conteúdo; página docs/capabilities.md gerada 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. Comando npx waconector doctor --provider <nome> (ADR-0011) para diagnóstico de conectividade/auth via variáveis WACONECTOR_* (só leitura, instance.status(), nunca connect()), implementado como entry point ESM-only próprio (src/cli/, node:util.parseArgs, zero dependência de runtime — ADR-0004). Exemplos executáveis em examples/express e examples/nextjs (App Router), usando MockAdapter por padrão. Fecha a F3 por completo. v1.0.0 publicado no npm (ver Epic 5/6 em ORCHESTRATOR-ROADMAP.md). Atualização pós-v1.0: a frase acima sobre messages.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 sobre groups.list e contacts.list/get/getProfilePicture/getAbout do 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/wppconnect tipa ou expõe o shape de resposta de todas as 5; foram implementadas no Epic 6 (WPPConnect agora em 29/30, groups.list via POST /list-chats em vez do endpoint deprecado GET /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 sobre tulir/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 contrato WaAdapter, connect() só pareia), messages.* completo exceto forward (endpoint real só aceita texto pronto, e o izapia não guarda histórico para resolvê-lo a partir de um messageId — descasamento real de forma), groups.*/contacts.*/chats.*/presence.*/labels.* completos, channels.* exceto delete e business.getProfile sem updateProfile (ambos 501 NOT_IMPLEMENTED hoje — o whatsmeow não expõe essas operações), e calls.* — com um CallManager de 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ê em docs/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 para messages.download (Evolution GO, uazapi, Whapi, izapia) e 2-4 para o namespace channels.* mensageria (getMessages nos mesmos 4; markViewed/reactToPost em uazapi/izapia) — bem acima do piso histórico de 2+ (ver ADR-0019, calls.make promovido 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 (o WaMessage.raw original) é necessário para os providers STATELESS (Evolution GO, Whapi, izapia — nenhum guarda histórico server-side), enquanto uazapi resolve só com messageId; MediaRef ganha um campo novo id?: string. channels.getMessages/ markViewed/reactToPost (ADR-0021, ChannelPost novo) cobrem o CONTEÚDO dos posts de um canal — lacuna real do namespace channels.* (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 — ver docs/capabilities.md (gerado do código, sempre a fonte da verdade para os números atuais). Também estendeu o HttpClient (core) com responseType: 'base64' para suportar respostas binárias cruas (necessário para o GET /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; redactSecrets em todo texto de erro.

Client HTTP para APIs de terceiros — sem afiliação com Meta/WhatsApp.