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

@konstit/dne

v0.0.2

Published

Build, update and query a local SQLite database of Brazilian postal codes.

Readme

CLI para e-DNE dos Correios

Crie em segundos uma base SQLite local, compacta e pronta para consultar os CEPs do Brasil com dados do e-DNE dos Correios. Depois da carga, as consultas funcionam sem servidor e sem acesso à rede.

O @konstit/dne recebe um diretório, um arquivo ZIP local ou o arquivo público mais recente do e-DNE. Os dados são processados em fluxo e gravados diretamente em uma única tabela dne indexada, sem tabelas brutas intermediárias.

Principais vantagens:

  • Rápido e compacto: em um benchmark de 10 execuções realizado em 2026-08-30, a geração completa a partir do arquivo público levou 5,73 segundos em média, incluindo o download. A base final continha 1.605.136 registros e ocupava 125,4 MiB.
  • Consultas locais simples: procure um CEP, consulte vários CEPs em lote ou execute SQL somente leitura diretamente no arquivo SQLite.
  • Atualizações seguras: o modo WAL e uma única transação permitem atualizar a base sem expor dados parciais aos processos de leitura.
  • Atualizações automáticas: registre uma agenda com Bun.cron para manter a base atualizada no Linux ou macOS. Cada job usa uma versão fixada do pacote, e o lock exclusivo evita cargas simultâneas.
  • Pronto para automação: a CLI oferece saídas JSON e JSONL estáveis, códigos de saída documentados e separação entre dados e mensagens de progresso.

Nas mesmas condições do benchmark, uvx edne-correios-loader load --database-url sqlite:///dne.db levou 30 segundos e gerou uma base de 394 MiB sem VACUUM. Nessa comparação, o @konstit/dne foi 5,2 vezes mais rápido e usou 68,2% menos espaço em disco.

Este pacote fornece somente uma CLI. Ele não expõe uma API pública de biblioteca.

Requisitos

  • Bun 1.4 ou mais recente
  • macOS ou Linux

Início rápido

bunx @konstit/dne build --db ./dne.db
bunx @konstit/dne get 01001-000 --db ./dne.db

Execução

Execute a CLI sem instalação global:

bunx @konstit/dne --version
bunx @konstit/dne --json doctor --offline

Os nomes de comandos, subcomandos, opções e flags permanecem em inglês. A ajuda, o progresso, os resultados textuais e as mensagens de erro são exibidos em português. O contrato JSON mantém chaves, códigos e valores de estado estáveis em inglês.

--db é uma opção global. Ela pode aparecer antes ou depois de um subcomando. O caminho da base segue esta ordem de precedência:

  1. --db PATH
  2. ./dne.db

Use --color para forçar cores e --no-color para desativá-las. Sem essas opções, a CLI detecta o terminal. A presença da variável NO_COLOR sempre desativa as cores.

Criar e atualizar

bunx @konstit/dne build --db ./dne.db
bunx @konstit/dne build --db ./dne.db --source ./eDNE_Basico.zip
bunx @konstit/dne build --db ./dne.db --source ./Delimitado
bunx @konstit/dne build --db ./dne.db --source https://example.com/eDNE_Basico.zip
bunx @konstit/dne build --db ./dne.db --force
bunx @konstit/dne build --db ./dne.db --check --json

build grava cada etapa e sua duração em formato compacto (ms, s, min ou h) em stderr. O resultado final é gravado em stdout. Use --quiet para ocultar o progresso. Use --json para receber o caminho da base, a quantidade de registros, o tamanho em bytes, os metadados da fonte, o tempo total em elapsed_ms e o estado da atualização.

Cada carga grava a versão do @konstit/dne em edne_metadata. Ao usar uma fonte remota, a CLI também grava Last-Modified, ETag, tamanho do conteúdo, URL da fonte e horário da carga. Todos os validadores disponíveis devem continuar iguais para a base ser considerada atual. Uma execução posterior de build não recria a base se a fonte não mudou e mostra o Last-Modified remoto na saída textual. --check valida a estrutura de uma fonte local ou verifica se há uma atualização remota, sem alterar a base. --force ignora os metadados e recria a base.

Para fontes HTTP ou HTTPS, a CLI tenta obter os metadados com HEAD. Se o servidor não aceitar esse método, ela usa uma requisição GET limitada ao primeiro byte. Requisições têm timeout de 30 segundos e até duas novas tentativas para falhas transitórias, respostas 408, 425, 429 e 5xx.

As entradas do ZIP são verificadas com seus valores CRC32 durante a leitura. A carga também valida CEP, UF, código IBGE, campos obrigatórios e referências entre os arquivos. Qualquer rejeição interrompe a transação. Uma carga válida grava em quality_report as linhas lidas, aceitas e rejeitadas por etapa e arquivo.

Acesso simultâneo

Cada execução de build que pode alterar a base adquire um lock exclusivo antes de verificar a fonte. Outra instância do @konstit/dne aguarda por até 30 segundos, então verifica novamente os metadados e evita uma carga duplicada se a primeira instância já atualizou a base. build --check não adquire esse lock.

O lock fica no diretório temporário do sistema, dentro de konstit-dne-<uid>. Seu nome contém um hash do caminho absoluto da base, evitando conflitos entre bases com o mesmo nome. Ele é removido quando a execução termina e também ao receber SIGHUP, SIGINT ou SIGTERM. Se o processo for encerrado sem executar essa limpeza, a próxima execução identifica o PID inativo e remove o lock antes de continuar. Processos que coordenam a mesma base devem usar o mesmo host e usuário do sistema.

Uma base existente é atualizada no próprio arquivo com o modo WAL do SQLite e uma única transação. Outros processos podem manter a base aberta para leitura durante a atualização. Esses processos não veem uma tabela atualizada parcialmente.

Um leitor com uma transação ativa continua vendo a versão anterior até o fim da transação. A próxima transação vê os dados atualizados.

O SQLite permite um escritor por vez. Se outro processo mantiver uma transação de escrita, a atualização aguarda por até 30 segundos. Depois desse período, ela falha se o bloqueio continuar ativo.

Atualização automática

Instale uma atualização semanal para a base:

bunx @konstit/dne cron install --db ./dne.db

O padrão 0 0 * * 5 executa à meia-noite de sexta-feira, no fuso local do cron. Informe outra expressão como argumento quando necessário:

bunx @konstit/dne cron install '30 6 * * 5' --db ./dne.db
bunx @konstit/dne cron install --db ./dne.db --source https://example.com/eDNE_Basico.zip
bunx @konstit/dne cron install '@weekly' --db ./dne.db --dry-run
bunx @konstit/dne cron status --db ./dne.db --json
bunx @konstit/dne cron remove --db ./dne.db

cron install usa Bun.cron para registrar o job no agendador do sistema operacional: crontab no Linux e launchd no macOS. O título contém um hash do caminho absoluto da base. Uma nova instalação para a mesma base substitui o job existente, enquanto bases diferentes mantêm agendamentos independentes. cron remove usa Bun.cron.remove.

Cada job recebe um módulo persistente e metadados no diretório de estado do usuário. O módulo executa os caminhos absolutos do bunx, da base e de fontes locais. A versão atual do @konstit/dne e a fonte ficam fixadas no comando. Alterar somente a expressão preserva a fonte já instalada. Execute cron install novamente depois de atualizar o pacote para usar a nova versão. A execução usa --quiet, descarta a saída normal e mantém erros em stderr para o agendador do sistema. cron status mostra a fonte e também calcula a próxima execução com Bun.cron.parse.

Consultar CEPs

Consulta individual:

bunx @konstit/dne get 01001000
bunx @konstit/dne get 01001-000 --json

Consulta em lote:

bunx @konstit/dne get 01001000 20040002 --json
bunx @konstit/dne get --file ./ceps.txt --jsonl
printf '01001000\n20040002\n' | bunx @konstit/dne get --jsonl

Os formatos aceitos são 01001000 e 01001-000. A entrada por arquivo ou stdin pode usar espaços, vírgulas ou pontos e vírgulas como separadores.

get abre o SQLite em modo somente leitura. Se o caminho da base não existir, nenhum arquivo será criado. O formato JSONL lê a entrada e grava cada resultado de forma incremental, sem manter o lote completo na memória.

Inspecionar a base

bunx @konstit/dne status --json
bunx @konstit/dne schema --json
bunx @konstit/dne schema --expected
bunx @konstit/dne doctor --json
bunx @konstit/dne doctor --offline --json

status informa o tamanho do arquivo, a quantidade de registros, o esquema atual, a versão do pacote, o Last-Modified da fonte e os demais metadados da carga. A saída textual formata datas e números com a localidade pt-BR; a saída JSON mantém os valores originais. schema lê o esquema real do SQLite. schema --expected mostra o esquema definido pela CLI. doctor verifica o Bun, a configuração da base e o acesso à fonte remota. O modo offline não faz a verificação de rede.

SQL somente leitura

bunx @konstit/dne sql 'SELECT cep, municipio, uf FROM dne WHERE uf = "SP" LIMIT 10' --json
bunx @konstit/dne sql 'PRAGMA page_size' --limit 20 --json

O comando sql abre a base em modo somente leitura. Ele aceita SELECT, WITH, PRAGMA e EXPLAIN. O valor padrão de --limit é 100, com limite máximo de 10.000 registros.

Contrato JSON

--json grava em stdout um envelope estável:

{
  "ok": true,
  "data": {}
}

Erros de execução e de argumentos usam este formato em stderr:

{
  "ok": false,
  "error": {
    "code": "invalid-cep",
    "message": "CEP inválido 'x'. Use 01001000 ou 01001-000."
  }
}

O progresso e os diagnósticos são gravados em stderr. A saída JSON em stdout não inclui mensagens de progresso.

Códigos de saída:

  • 0: comando concluído
  • 1: falha na base, na fonte, na rede ou na execução
  • 2: argumentos inválidos ou CEP em formato inválido
  • 3: um ou mais CEPs válidos não foram encontrados

Schema SQLite

CREATE TABLE "dne" (
  "cep" TEXT NOT NULL /* Contém somente os oito dígitos do CEP, sem separadores. */,
  "logradouro" TEXT,
  "complemento" TEXT,
  "bairro" TEXT,
  "municipio" TEXT NOT NULL,
  "municipio_cod_ibge" INTEGER NOT NULL,
  "uf" TEXT NOT NULL,
  "nome" TEXT,
  PRIMARY KEY ("cep")
) WITHOUT ROWID;

cep é a chave primária, sem digito separador. A tabela usa WITHOUT ROWID e páginas de 32 KiB para oferecer consultas diretas com menor uso de espaço.

Drizzle ORM schema

import { sqliteTable } from 'drizzle-orm/sqlite-core';

export const dneTable = sqliteTable('dne', (t) => ({
  cep: t.text().primaryKey(),
  logradouro: t.text(),
  complemento: t.text(),
  bairro: t.text(),
  municipio: t.text().notNull(),
  municipio_cod_ibge: t.integer().notNull(),
  uf: t.text().notNull(),
  nome: t.text(),
}));

Este schema serve para consultar um banco criado pelo @konstit/dne. O Drizzle não representa WITHOUT ROWID; portanto, usar o Drizzle Kit para gerar ou migrar essa tabela cria uma estrutura física diferente.

Desenvolvimento

O comando zip é necessário para os testes de desenvolvimento que usam fixtures e arquivos ZIP aninhados.

bun test
bun run lint
bun run fmt
bun run benchmark
bun run benchmark:download

Outros scripts de benchmark específicos estão listados em package.json.