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

@sensio-erp/diffai-guard

v0.3.0

Published

SonarQube-style quality gate for TypeScript/JavaScript: source-code rules + TypeScript semantic checks + diffai signals, with ratings, a configurable quality gate, SARIF output and a CI exit code.

Readme

@sensio-erp/diffai-guard

Um SonarQube-like para o ecossistema diffai: analisa código-fonte (regras via AST) + os sinais destilados do diffai (.diffai/*.json), calcula métricas e ratings, aplica um Quality Gate com código de saída para CI e gera dashboard HTML e SARIF (também abre no Notepad o resumo, se pedido).

Instalação

npm i -g @sensio-erp/diffai-guard      # global → comando `diffai-guard`
# ou sem instalar:
npx @sensio-erp/diffai-guard --repo . --all

O que ele faz

Motor híbrido, em três fontes:

  • Regras sobre o código-fonte (ts-morph), cada achado com linha, rule key, tipo e severidade:
    • Segurança/correção: no-eval, credenciais hard-coded, http:// inseguro, catch vazio, ==/!=.
    • Segurança (SAST): command-injection (exec/execSync com comando dinâmico), sql-injection (query SQL concatenada/interpolada), unsafe-regex (ReDoS — grupo quantificado aninhado), prototype-pollution (atribuição a __proto__/constructor.prototype), insecure-randomness (Math.random() para token/senha/session), weak-crypto (md5/sha1, createCipher), insecure-cookie (cookie sem httpOnly/secure), unvalidated-redirect (redirect direto de req.query/params/body).
    • Async/Promises: no-floating-promises (promise não tratada), no-async-foreach, require-await, await-in-loop.
    • Estilo/manutenção: prefer-const (um let nunca reatribuído), no-var, console/debugger, complexidade cognitiva, aninhamento profundo, excesso de parâmetros, any explícito, código comentado, no-magic-numbers (INFO, tunável), TODO/FIXME.
  • Checker semântico (diagnósticos do compilador TypeScript): pega código que vai falhar em execução ou está morto — reatribuir uma const (semantic:const-reassignment, TS2588 → TypeError), variável não declarada (semantic:no-undef, TS2304/2552 → ReferenceError), uso antes da declaração (semantic:use-before-declaration, TS2448/2454), chamar algo que não é função (semantic:not-callable, TS2349 → TypeError), número errado de argumentos (semantic:wrong-arg-count, TS2554/2555) e declarações nunca usadas (semantic:unused-code, TS6133/6196/6198). Os primeiros são BUG (BLOCKER/MAJOR); unused-code é CODE_SMELL. Globais Node ausentes (TS2591) e módulos não resolvidos (TS2307) são ignorados de propósito — zero falso-positivo com console/process ou imports entre arquivos.
  • Sinais do diffai: fatos semânticos que um parser não vê — regra de negócio alterada sem testes, mudança de API/payload/permissões, regressões e riscos que a IA do diffai apontou.

E entrega os quatro pilares estilo Sonar:

| Pilar | Detalhe | | --- | --- | | Taxonomia + severidades | BUG / VULNERABILITY / CODE_SMELL / SECURITY_HOTSPOT · BLOCKERINFO | | Quality Gate | Condições configuráveis (Sonar way por padrão) → PASS/FAIL e exit code para CI | | Métricas & ratings | Reliability / Security / Maintainability / Security-Review (A–E), dívida técnica, duplicação (CPD por tokens), cobertura de linha/branch/new-code, LOC | | Dashboard HTML | Relatório navegável (sonar-report.html) + resumo .txt (Notepad) + .json (máquina) | | SARIF + CI | sonar-report.sarif (2.1.0) p/ GitHub Code Scanning, new code por linha (--base) e supressão inline | | Leak period | Achados novos desde a execução anterior (.diffai/baseline.json), com contadores dedicados no Quality Gate |

Fluxo

.diffai/*.json (diffai)          código-fonte do repo
        │                               │
   diffai signals        regras AST + checker semântico (ts-morph)  ── escopo "new code" (git diff; .diffai é fallback)
        └───────────────┬───────────────┘
                    issues (taxonomia Sonar)
                        │
        métricas + ratings + duplicação + cobertura
                        │
                 Quality Gate (PASS/FAIL)
                        │
      sonar-report.{json,html,txt,sarif}  ──►  browser / Notepad / GitHub Code Scanning

Uso

Depois de instalado, o comando é diffai-guard:

# analisa só o "new code" — arquivos alterados no git (fallback: .diffai) — e abre o dashboard
diffai-guard                       # sem --repo: usa o diretório atual

# analisa TODO o código-fonte do repo
diffai-guard --repo C:\caminho\do\repo --all

# uso em CI: sem abrir nada; sai com codigo != 0 se o gate falhar
diffai-guard --repo C:\caminho\do\repo --all --open none

# abrir o resumo no Notepad em vez do navegador
diffai-guard --repo C:\caminho\do\repo --open notepad

Em desenvolvimento (sem instalar), rode a partir do fonte:

npm install
npx tsx src/cli/bin.ts --repo C:\caminho\do\repo --all

No PowerShell/npm, prefira npx tsx src/cli/bin.ts ... a npm start -- ... (o npm consome as flags --repo).

Flags

| Flag | Efeito | | --- | --- | | --repo <path> | repositório a analisar (padrão: diretório atual) | | --all | analisa todo o código-fonte, não só os arquivos alterados | | --base <ref> | escopo "new code" por linha: só reporta issues nas linhas alteradas em <ref>...HEAD (estilo PR). Sem --base, usa as mudanças pendentes (git diff HEAD) | | --open <browser\|notepad\|none> | o que abrir ao final (padrão browser) | | --no-fail | sempre sai com código 0, mesmo com gate FAIL | | --config <path> | analyzer.config.json (padrão: <repo>/analyzer.config.json) | | --init | escreve analyzer.config.example.jsonc (todas as regras, comentadas) em --repo e sai — não sobrescreve se já existir | | --run-diffai / --diffai-path <path> | roda o diffai antes de analisar | | --ignore <pastas> | pastas (nome exato) a pular ao procurar repositórios git em subpastas, separadas por vírgula — ex.: --ignore pasta1,pasta2 | | --freeze-baseline | compara com .diffai/baseline.json sem sobrescrevê-lo (use em checks de PR; deixe as execuções na main atualizarem o baseline) |

Múltiplos repositórios (auto-descoberta de .git)

Se --repo (ou o diretório atual) não tiver um .git diretamente, o diffai-guard percorre as subpastas em busca de repositórios git — útil em uma pasta "mono" que contém vários projetos clonados lado a lado. Ele não desce dentro de uma pasta assim que encontra um .git nela (evita repos aninhados/submódulos duplicados) e já ignora por padrão node_modules, dist, build, out, coverage, .diffai — use --ignore para pular outras pastas específicas.

# c:\projetos\ contém repoA\, repoB\, terceiros\ (sem .git direto em c:\projetos\)
diffai-guard --repo c:\projetos --all --ignore terceiros

Cada repositório encontrado é analisado normalmente e grava seu próprio <repo>/.diffai/sonar-report.*. Quando mais de um repositório é encontrado, os relatórios individuais não são abertos automaticamente — em vez disso é gerado e aberto um relatório combinado em <pasta-raiz>/.diffai/sonar-report.html, com uma seção recolhível (<details>) por repositório, cada uma com seu próprio Quality Gate, ratings, métricas e lista de issues.

Códigos de saída

0 gate PASS · 1 gate FAIL (salvo --no-fail) · 2 erro na análise.

Configuração (analyzer.config.json)

Opcional, na raiz do repo (ou em qualquer caminho, via --config <path>). Precedência: defaults < analyzer.config.example.jsonc na raiz < analyzer.config.json na raiz < --config <path>. Aceita JSONC — comentários // e /* */ são removidos automaticamente antes do parse.

Se não existir analyzer.config.json mas houver um analyzer.config.example.jsonc na raiz do repo (por exemplo, o que o --init gerou), ele é usado automaticamente — sem precisar renomear nem passar --config.

{
  "qualityGate": {
    "conditions": [
      { "metric": "reliability_rating", "op": "LTE", "threshold": "A" },
      { "metric": "coverage", "op": "GTE", "threshold": 80, "onlyIfMeasured": true }
    ]
  },
  "rules": {
    "no-console": { "enabled": false },
    "explicit-any": { "severity": "INFO" },
    "diffai:permissions-change": { "severity": "MAJOR" }
  },
  "exclude": ["generated", "vendor"]
}

rules habilita/desabilita (enabled: false) ou re-severidade (severity) qualquer regra do catálogo — regras de código-fonte (source:*), do checker semântico (semantic:*) e os sinais do diffai (diffai:*). A chave aceita o rule key completo (diffai:permissions-change) ou só o sufixo (permissions-change).

Arquivos de teste (*.test.ts, *.spec.ts, *.test.tsx, *.spec.tsx e os equivalentes .js/.jsx) nunca são analisados — a ferramenta os ignora sempre, sem precisar de exclude. exclude é para o resto: pastas geradas, vendor, ou uma pasta de testes que não segue esse padrão de nome (ex.: __tests__). É substring "crua" (não glob), então evite trechos curtos/comuns que possam bater em outro lugar sem querer.

analyzer.config.example.jsonc traz todas as regras disponíveis, comentadas em português, já com a severidade padrão de cada uma. Rode:

diffai-guard --init                # escreve ./analyzer.config.example.jsonc
diffai-guard --init --repo <path>  # escreve <path>/analyzer.config.example.jsonc

Ele já é lido automaticamente se ficar na raiz do repo (não precisa de --config). Para customizar, copie/renomeie para analyzer.config.json e ajuste enabled/severity.

Saídas (<repo>/.diffai/)

sonar-report.json (máquina) · sonar-report.sarif (SARIF 2.1.0 → GitHub Code Scanning / viewers do VS Code) · sonar-report.html (dashboard) · sonar-report.txt (Notepad) · baseline.json (fingerprints da execução, usado pelo leak period — ver abaixo).

Quando múltiplos repositórios são descobertos (ver Múltiplos repositórios acima), o mesmo trio sonar-report.{json,html,txt} é gerado também na pasta raiz informada em --repo, agregando todos os repositórios (sem .sarif, já que cada repo já tem o seu).

Supressão inline

Silencie um falso-positivo pontual com um comentário — casa por chave completa (source:no-eval) ou curta (no-eval); sem regra listada = todas:

// analyzer-disable-next-line no-magic-numbers
const timeout = 3000;

save(); // analyzer-disable-line no-floating-promises

// analyzer-disable-file explicit-any   (arquivo inteiro)

New code / CI

--base <ref> cobra apenas as linhas alteradas em <ref>...HEAD (via git diff), tornando o Quality Gate justo em repositórios grandes. Com o exit code, bloqueia PRs:

npx tsx src/cli/bin.ts --repo . --base origin/main --open none   # exit 1 se o gate falhar
# depois: upload de sonar-report.sarif no github/codeql-action/upload-sarif

Leak period (achados novos)

Sem um servidor central, o diffai-guard mantém seu próprio "leak period": cada issue tem um fingerprint estável (rule key + arquivo + mensagem — a linha é ignorada de propósito, já que refatorações a deslocam). A cada execução, o fingerprint é comparado com .diffai/baseline.json da execução anterior:

  • achado ausente no baseline anterior → isNew: true (aparece com o badge NEW no dashboard/txt e a propriedade isNew no SARIF/JSON);
  • ao final, o baseline é sobrescrito com os fingerprints desta execução — a próxima rodada compara contra este resultado.

Isso alimenta métricas de Quality Gate dedicadas: new_issues, new_bugs, new_vulnerabilities, new_blocker_issues, new_critical_issues (ver exemplos comentados em analyzer.config.example.jsonc).

Como o baseline é sobrescrito a cada execução, use --freeze-baseline em checks de PR (para não corromper a referência da branch principal com o estado da PR) e deixe as execuções na main/master atualizá-lo normalmente:

# check de PR: compara, não sobrescreve
npx tsx src/cli/bin.ts --repo . --base origin/main --open none --freeze-baseline

# execução na main: atualiza o baseline para a próxima comparação
npx tsx src/cli/bin.ts --repo . --all --open none

Na primeira execução (sem baseline ainda), tudo é considerado novo — isso estabelece a dívida inicial do projeto.

Arquitetura

Clean Architecture + Dependency Injection — cada recurso externo atrás de um port (src/application/ports.ts):

src/
  domain/     # Issue, Rating, métricas, Quality Gate e leak period/baseline (puro)
  rules/      # catálogo de regras de código (ts-morph) + interface Rule
  schemas/    # Zod: entrada (diffai) + saída (analysis-report) + config
  application/# ports + AnalyzeUseCase (orquestra só com ports)
  infra/      # engine ts-morph, semantic-analyzer, scope-resolver, diffai-signals, duplicação (CPD por tokens), cobertura, baseline-store, writers (html/txt/json), opener, git-repo-finder, combined-report-writer
  config/     # config-loader (defaults < arquivo < flags)
  cli/        # Commander + composition root

Adicionar uma regra = 1 objeto em src/rules/catalog.ts. Trocar qualquer adapter = 1 linha no composition root.

Comandos

npm run typecheck   # tsc --noEmit
npm test            # vitest
npm run lint        # eslint
npm run build       # dist/

Licença

MIT