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

impactwave

v1.3.0

Published

Blast radius analyzer CLI for TypeScript: find which exported symbols your Git changes modify, who consumes them, what tests cover them, and get a deterministic risk score before merging.

Readme

ImpactWave

🌐 English | Español

Analizador de blast radius para TypeScript.

ImpactWave es una CLI que analiza tus cambios en Git antes de hacer merge y responde una pregunta:

"¿Qué puedo romper con este cambio y qué debería probar?"


El problema

En un codebase real, un cambio "pequeño" puede romper cosas muy lejos del archivo editado. Los revisores lo detectan por intuición, los tests globales no te dicen qué probar primero, y el impacto real se descubre en producción.

ImpactWave convierte esa intuición en datos: qué símbolos exportados tocaste físicamente, quién los consume de verdad, qué áreas afectadas quedan sin tests y cuánto riesgo acumula el cambio — con un score determinístico y razones explicables.

Qué hace

Dado un rango de commits (rama base → HEAD), genera un reporte en consola con:

  • Símbolos modificados — funciones, clases, métodos públicos, interfaces, tipos… detectados vía AST (ts-morph), no texto plano: solo cuenta lo que el diff tocó físicamente.
  • Consumidores reales — cada uso activo de los símbolos modificados, con archivo, línea y snippet. Un import puro no ejecuta nada y no cuenta como impacto.
  • Blast radius — todos los archivos alcanzados a través del grafo de dependencias (incluye barrel files), agrupados por nivel de cascada.
  • Cobertura de impacto — qué porcentaje de las áreas afectadas está cubierto por tests, y la lista exacta de las que no.
  • Risk score 0–100 — determinístico (misma entrada → mismo score) y explicable: cada punto viene acompañado de su razón.

¿Qué es el blast radius?

El radio de explosión es el conjunto de código que puede verse afectado cuando cambias un archivo: quienes lo importan directamente, quienes importan a esos, y así en cascada. Conocerlo antes del merge significa saber exactamente dónde mirar y qué tests ejecutar — no descubrirlo por un bug report.

Instalación

npm install -g impactwave   # o úsalo sin instalar:
npx impactwave

Requisitos: Node ≥ 22.12, un repositorio Git local y un proyecto TypeScript.

Uso

Ejecútalo en la raíz de tu proyecto:

cd mi-proyecto
impactwave

analyze es el comando por defecto: impactwave y impactwave analyze son equivalentes. Solo se analizan cambios commiteados (base..HEAD); el working tree sin commitear no entra en el análisis.

$ impactwave --help

ImpactWave answers one question before you merge:

  "What can I break with this change, and what should I test?"

It combines your Git diff with AST analysis to find the exported symbols you
modified, who really consumes them, whether tests cover the affected areas,
and computes a deterministic risk score (0-100) with explainable reasons.

Usage: impactwave [options] [command]

Analyze the blast radius of your code changes before merging

Options:
  -V, --version      output the version number
  -h, --help         display help for command

Commands:
  analyze [options]  Analyze changed code impact in this repository
  help [command]     display help for command

Documentation: https://github.com/paleto30/impactwave#readme

Tip: running bare "impactwave" inside a Git repository is equivalent to
"impactwave analyze". Run "impactwave analyze --help" for options and examples.

Opciones

| Opción | Descripción | |---|---| | -b, --base <branch> | Rama base a comparar (autodetección: origin/HEADmain/masterHEAD~1) | | --risk-weights <json> | Pesos personalizados de los factores de riesgo. Ver modelo de riesgo | | --json | Reporte como JSON en stdout, con contrato versionado (meta.schemaVersion) y esquema publicado. Ideal para CI |

Ejemplos

# HEAD contra la rama base autodetectada
impactwave

# Comparar contra una rama base explícita
impactwave analyze -b main

# Dar más peso a los huecos de cobertura de tests
impactwave --risk-weights '{"callerImpact":30,"testGaps":35}'

# Puntuar solo por consumidores directos de los símbolos modificados
impactwave analyze --risk-weights '{"callerImpact":100}'

# Salida machine-readable para pipelines (stdout puro, warnings en stderr)
impactwave --json | jq '.risk'

# Gate de merge: falla si el nivel no es LOW ni MEDIUM
impactwave analyze --json -b main | jq -e '.risk.level | inside("LOW|MEDIUM")' > /dev/null

Cómo funciona

  1. Git: detecta el repo, la rama base y los archivos modificados (A/M/D).
  2. AST: ts-morph extrae exports e imports de los archivos cambiados, usando un único proyecto con los compilerOptions de tu tsconfig.json raíz y los archivos TypeScript (.ts, .tsx, .mts, .cts) descubiertos por un recorrido propio del árbol (tolerante a directorios ilegibles).
  3. Símbolos modificados: intersecta los rangos de líneas de cada símbolo exportado con las líneas del diff.
  4. Consumidores reales: findReferences encuentra los usos activos de cada símbolo (los imports puros no cuentan como impacto).
  5. Grafo de dependencias: índice inverso y directo de imports relativos —incluidas las cargas dinámicas import(...)/require(...) con argumento estático— + recorrido transitivo (BFS) con profundidad.
  6. Test mapping: detecta archivos *.test.ts/*.spec.ts y mapea qué código cubren.
  7. Risk engine: score determinístico 0-100 con razones explicables.

Modelo de riesgo

Cinco factores con umbrales de saturación, más uno opcional. Pesos por defecto (configurables con --risk-weights):

| Factor | Peso | Señal | |---|---|---| | callerImpact | 30 | consumidores directos de símbolos modificados (umbral 10) | | affectedFiles | 20 | archivos alcanzados transitivamente (umbral 15) | | dependencyDepth | 15 | niveles de profundidad máxima (umbral 4) | | testGaps | 20 | proporción de áreas afectadas sin tests | | changeSize | 15 | líneas modificadas (umbral 200) | | testCallerImpact | — | opcional: si se define, los consumidores que son tests salen de callerImpact y saturan este peso (0 los exime del score) |

Niveles: 0-25 LOW · 26-50 MEDIUM · 51-75 HIGH · 76-100 CRITICAL.

Los nombres de los factores son las claves JSON de --risk-weights (todas opcionales; las omitidas valen 0). Los puntos de cada razón del reporte suman exactamente el score, salvo cuando satura en 100.

Dos de los factores (affectedFiles y dependencyDepth, 35 puntos) se calculan sobre el grafo estático de dependencias, no sobre los consumidores reales: miden cuánta superficie del proyecto queda expuesta al archivo cambiado.

El reporte

Cada análisis imprime contexto Git, evaluación de riesgo con score y razones, cobertura de impacto, y por cada archivo cambiado: símbolos exportados (marcando los modificados), usos downstream con línea y snippet, blast radius en cascada y tests relacionados:

Reporte de ejemplo de ImpactWave

╭─ Risk Assessment ────────────────────────────────────────╮
│ 🟡 MEDIUM RISK (score: 31/100)                           │
│ Changes affect a few dependent modules. Verify them...   │
│ 4 unique dependent files at risk                         │
│ Reasons:                                                 │
│   • 4 consumers of modified symbols (12 pts)             │
│   • 4 affected files (transitive reach) (5 pts)          │
│   • Impact reaches depth 1 dependency level (4 pts)      │
│   • 1 affected area without detected tests (10 pts)      │
│   • 1 line modified                                      │
╰──────────────────────────────────────────────────────────╯

📖 Cómo leer el reporte completo, sección por seccióndocs/GUIA.md

Ejemplo visual

Ejecución real de ImpactWave antes de hacer merge a main: análisis del refactor de un módulo de usuarios en un proyecto NestJS.

Análisis de impacto antes de merge: refactor del módulo de usuarios en un proyecto NestJS

Nota: esta captura se tomó durante el desarrollo de la herramienta con una utilidad que guarda la salida completa de la terminal como imagen, por lo que pierde parte del diseño y formato del reporte. No representa exactamente el reporte final — es solo un ejemplo visual del análisis de impacto antes del merge; el reporte real se renderiza directamente en tu terminal.

Salida JSON para CI

impactwave analyze --json emite el mismo reporte como un único documento JSON en stdout (las advertencias van a stderr). El formato está versionado (meta.schemaVersion) y publicado como JSON Schema: dentro de una versión solo hay cambios aditivos. Los usos de símbolos viajan sin filtrar, marcados con importOnly: true cuando son solo cableado de contrato (import, re-exports).

Alcance

ImpactWave es un prototipo con un alcance deliberadamente acotado: hace bien una cosa y declara el resto fuera de alcance en vez de aproximarlo.

Dentro del alcance:

  • Proyectos TypeScript: .ts, .tsx, .mts, .cts.
  • Cambios ya commiteados, comparando base..HEAD en un repositorio Git local.
  • Imports relativos dentro del proyecto, estáticos y dinámicos con argumento estático.
  • Análisis local, sin red: tu código nunca sale de tu máquina.

Fuera del alcance (por decisión, no por descuido):

  • JavaScript (.js, .jsx, .mjs, .cjs). No se descubren ni se parsean. Si un cambio los toca, se omiten y el reporte lo dice con la advertencia unsupported-source-files: un reporte limitado es útil, uno que parece completo sin serlo es peligroso.
  • Otros lenguajes.
  • Cambios sin commitear en el working tree.
  • node_modules, path aliases no relativos y monorepos con varios tsconfigs (solo se usa el de la raíz).
  • Análisis semántico del cambio: la herramienta dice qué símbolo cambió y quién lo usa, no si el cambio rompe el contrato.

Limitaciones conocidas

  • Granularidad del "símbolo modificado": es la intersección del diff con el rango de líneas de la declaración. Un cambio no funcional (un comentario, un reformateo) dentro de ese rango marca el símbolo como modificado.
  • Archivos eliminados: no se analizan sus símbolos ni sus consumidores. Si borras un archivo que otros siguen importando, el reporte no lo señala.
  • Renombrados: se reportan como una modificación de la ruta nueva (con previousPath), comparada contra el contenido anterior.
  • La cobertura de tests es transitiva: un test cubre los archivos que alcanza a través del grafo de dependencias hasta 4 saltos (DEFAULT_TEST_COVERAGE_DEPTH, configurable por llamada), evitando que un test que importa la raíz del proyecto reclame cobertura sobre todo el codebase.
  • Los imports dinámicos con argumento no estático (template literals con variables, concatenaciones) no son resolubles: se registran y se reportan con la advertencia unresolved-dynamic-imports.
  • El descubrimiento de fuentes recorre todo el árbol omitiendo node_modules/dist/build, directorios ocultos y symlinks. Los directorios sin permiso de lectura (ej. el pg_data de Docker) se omiten; no abortan el análisis.

Desarrollo

npm test       # suite de tests (node:test)
npm run build  # compilación a dist/
npm run dev    # ejecutar en desarrollo

Los fixtures en test/fixtures/ validan el análisis contra proyectos artificiales: simple-project (cadena A→B→C), circular-dependencies (X↔Y), barrel-exports (re-exports por barrel), test-coverage y test-coverage-transitive (servicios con y sin tests, cadenas largas y ciclos), dynamic-imports (cargas dinámicas resolubles y no resolubles) e import-shapes (imports y re-exports multilínea).

Para contribuir: abre un issue o envía un PR. Las prioridades y mejoras candidatas están documentadas en docs/ROADMAP.md; el historial de versiones, en CHANGELOG.md.

Licencia

ISC