Skip to content

Release v5.2.0 — paginação de companies corrigida (1-based) + listV2 (API v2, cursor)#42

Merged
andrenfe merged 4 commits into
masterfrom
release/v5.2.0
Jul 14, 2026
Merged

Release v5.2.0 — paginação de companies corrigida (1-based) + listV2 (API v2, cursor)#42
andrenfe merged 4 commits into
masterfrom
release/v5.2.0

Conversation

@andrenfe

Copy link
Copy Markdown
Member

Resumo

Corrige o contrato de paginação v1 de companies (quebrado desde sempre nos helpers de auto-paginação) e inicia a migração para a API v2 com o novo listV2() cursor-based — tudo provado por sondas ao vivo contra contas reais (2026-07-13/14). CHANGELOG da 5.2.0 tem o detalhe completo, incluindo a nota de migração.

Pendências pós-merge rastreadas em #41.

🐛 fix(companies): paginação 1-based + teto de pageCount + semântica PUT

  • listAll()/listIterator() falhavam na 1ª chamada por dois motivos independentes: iniciavam em pageIndex=0 (a API é 1-based e rejeita 0) e pediam pageCount=100 (a API aceita 2–50 — rejeita 1, apesar da própria mensagem dizer "between 1 and 50"). Consertados transitivamente os 4 métodos dependentes (findByTaxNumber, findByName, getCompaniesWithCertificates, getCompaniesWithExpiringCertificates). Prova ao vivo: listAll() = 352 empresas / 8 páginas.
  • ⚠️ Mudança de comportamento (minor assumido): list() deixou de subtrair 1 da resposta — page.pageIndex agora é 1-based, fiel ao fio. Justificativa: a convenção mista nunca foi documentada e o caminho de auto-paginação estava 100% quebrado. Nota de migração no CHANGELOG.
  • JSDoc de update() era ativamente enganoso: prometia PATCH ("only fields to update"), mas a API faz PUT (substituição total) — risco de zerar campos silenciosamente. Documentado com exemplo read-modify-write; create() documenta os obrigatórios reais (taxRegime, address; email não existe no corpo). Exemplos do README idem.
  • Specs locais: minimum: 1 no pageIndex e maximum: 50 no pageCount de /v1/companies (fix na origem nfe/docs pendente — ver companies: pendências da migração v2 — enumeração bloqueada por 500 no cursor; tightening de create/update adiado #41).
  • Testes: mock espelhando as validações reais da API (pegaria ambos os bugs) + alignment test dos corpos de escrita (company-write-alignment.test-d.ts).

✨ feat(companies): listV2() + deprecação do list() v1

A v1 de companies está sendo descontinuada. listV2({ limit, startingAfter, endingBefore }){ data, hasMore } usa a contribuintes-v2 (api.nfse.io/v2/companies); contrato provado ao vivo (limit 1–50 default 10; validação client-side). list() v1 fica @deprecated e funcional na convivência.

Por que listAll/listIterator NÃO migraram: o gate de paridade reprovou — (1) enumeração completa via v2 quebra com 500 determinístico num registro específico (repro exata em #41, reportado ao backend); (2) as projeções v1/v2 divergem de campos e até de valores (municipalTaxNumber). Detalhes em #41.

🔧 chore(test): vitest coletava a suíte 5×

O glob **/*.test.ts atravessava os symlinks client-php/client-ruby (loops de volta) — 3710 testes aparentes eram 698 reais, com flakes por corrida no .test-temp. include restrito a tests/**.

Verificação

  • npm run typecheck ✅ · npm run test:types ✅ (sem erros de tipo) · npm test -- --run702 passed | 44 skipped
  • npm run validate:spec && npm run generate ✅ (10/15, 5 known-skipped esperados)
  • Sondas ao vivo read-only: contratos v1 (2 contas) e v2, listAll ponta a ponta, listV2 via SDK

Método

Todo contrato afirmado neste PR foi provado contra a API real antes do código — os specs/tasks completos (com repros e evidências) estão nos OpenSpec changes locais fix-companies-pagination-1-based e migrate-companies-to-v2.

andrenfe added 4 commits July 14, 2026 00:35
O glob '**/*.test.ts' atravessava os symlinks client-php/client-ruby (que
apontam de volta para este repo), coletando a mesma suíte 5x (210 arquivos/
3710 testes aparentes vs 42/698 reais) e causando flakes por corrida no
.test-temp compartilhado do generation.test.ts.
…documentada

Contratos provados por sonda ao vivo (2026-07-13/14, duas contas):

- listAll()/listIterator() falhavam na 1ª chamada por dois motivos
  independentes: iniciavam em pageIndex=0 (a API rejeita: 1-based) e pediam
  pageCount=100 (a API aceita 2-50; rejeita 1 apesar da mensagem 'between 1
  and 50'). Corrigidos (início em 1, AUTO_PAGINATION_PAGE_SIZE=50) — conserta
  transitivamente findByTaxNumber, findByName, getCompaniesWithCertificates
  e getCompaniesWithExpiringCertificates. Provado: 352 empresas / 8 páginas.
- list() deixou de subtrair 1 da resposta: convenção 1-based nos dois lados
  (nota de migração no CHANGELOG).
- JSDoc de update() era enganoso: dizia 'only fields to update', mas a API
  faz PUT (substituição total) — risco de zerar campos. Documentado com
  exemplo read-modify-write; create() documenta os obrigatórios reais.
- Specs: minimum:1 no pageIndex e maximum:50 no pageCount de /v1/companies.
- Testes: mock espelha as validações reais da API; alignment test pina os
  corpos de escrita (Create/Update iguais = evidência PUT, sem email).
- Docs/skills: exemplos 0-based corrigidos em API.md, README e na skill
  nfeio-node-sdk (que também afirmava shape errado para taxCodes).
A API v1 de companies (api.nfe.io/v1/companies) está sendo descontinuada;
o substituto é a contribuintes-v2 (api.nfse.io/v2/companies), cursor-based.

- listV2({ limit, startingAfter, endingBefore }) -> { data, hasMore }, via o
  client v2 já injetado. Contrato provado ao vivo (2026-07-14): limit 1-50
  (default 10), >50 rejeitado; limit=0 devolveria página vazia silenciosa e
  é rejeitado client-side. Itens seguem a projeção v2 (CompanyResourceItem),
  shape distinto do Company v1. Tipos: CompanyV2ListOptions/Response.
- list() v1 marcado @deprecated apontando para listV2/listAll/listIterator;
  segue funcional durante a convivência.
- listAll()/listIterator() permanecem no transporte v1 nesta release: o gate
  de paridade reprovou — enumeração completa via v2 quebra com 500
  determinístico num registro específico (bug reportado ao backend) e as
  projeções v1/v2 divergem (inclusive municipalTaxNumber com valores
  diferentes). Pendências rastreadas em #41.

Refs #41
@github-actions

Copy link
Copy Markdown

📋 OpenAPI Spec Validation

✅ All specs validated and types generated successfully

Specs processed:

  • calculo-impostos-v1.yaml - 27.90 KB, 853 lines
  • consulta-cnpj.yaml - 34.28 KB, 1128 lines
  • consulta-cpf.yaml - 3.39 KB, 83 lines
  • consulta-cte-v2.yaml - 18.33 KB, 578 lines
  • consulta-endereco.yaml - 11.17 KB, 343 lines
  • consulta-nf-consumidor.yaml - 43.41 KB, 1279 lines
  • consulta-nf.yaml - 137.87 KB, 3119 lines
  • consulta-nfe-distribuicao-v1.yaml - 53.07 KB, 1775 lines
  • nf-consumidor-v2.yaml - 293.87 KB, 7609 lines
  • nf-produto-v2.yaml - 309.41 KB, 8204 lines
  • nf-servico-v1.yaml - 257.67 KB, 6255 lines
  • nfeio.yaml - 15.86 KB, 630 lines
  • product-invoice-rtc-v1.yaml - 198.88 KB, 5084 lines
  • service-invoice-rtc-v1.yaml - 95.30 KB, 1728 lines

Generated types available as artifact in src/generated/.

@andrenfe andrenfe self-assigned this Jul 14, 2026
@andrenfe
andrenfe merged commit 531159e into master Jul 14, 2026
13 checks passed
@andrenfe
andrenfe deleted the release/v5.2.0 branch July 14, 2026 12:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant