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

pbip-killer

v0.5.0

Published

Audit Power BI PBIP / PBIR reports from the command line or CI/CD, with fully customizable rules.

Readme

pbip-killer

Audit Power BI PBIP / PBIR reports from the command line or CI/CD — and fail the build when a report breaks your team's standards.

This is the report-side sibling of dataflow-killer. Where the dataflow CLI gates your data layer, this gates your reports: visuals, accessibility, governance and consistency. It is audit-only — it never modifies your .pbip.

Status: v1 foundation. Report (PBIR) analysis with a fully declarative, customizable rule layer. The semantic-model (TMDL) side is intentionally out of scope for now.

Why PBIR

When you save a Power BI Desktop project as PBIP with the enhanced report format (PBIR) enabled, the report is stored as small per-page / per-visual JSON files. That makes it git-versionable and lintable at the visual level — which is exactly what this tool needs. Mandate "PBIP + PBIR on" for your team and you unlock CI gating without needing workspace Git integration.

Usage

# From the terminal
npx pbip-killer audit ./MyProject.pbip
npx pbip-killer audit ./MyProject.Report --format=md
npx pbip-killer audit ./repo --config=pbipkiller.config.json --fail-on=warning

Exit code is 1 when any finding meets the failOn gate — that is what reddens a pipeline.

Use in GitHub Actions

The action/ wrapper installs the CLI and runs the audit. Drop this in .github/workflows/report-audit.yml:

name: Report audit
on:
  pull_request:
    paths: ['**/*.Report/**']
jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: Jesusveiga/DataflowKiller/pbip/[email protected]
        with:
          path: .                 # a .pbip, a *.Report folder, or a repo root
          # config: pbipkiller.config.json   # optional; auto-detected if omitted
          # fail-on: warning                 # optional; overrides config's failOn
          format: md

| Input | Default | Notes | |-----------|---------|-------| | path | . | .pbip file, *.Report folder, or a repo folder containing one | | config | — | Path to pbipkiller.config.json (auto-detected in cwd if omitted) | | fail-on | — | Override the gate; if omitted, the config's failOn wins | | format | human | human · json · md |

The model rules see

Rules never touch raw PBIR JSON. The parser normalizes it into clean, stable fields, and those field names are the contract for both filtering and message templating:

| Scope | Fields available | |-----------|------------------| | report | name, theme, exportDataMode, pageCount, visualCount, bookmarkCount | | page | name, displayName, hidden, type, width, height, visualCount | | visual | page, name, type, isCustomVisual, title, hasTitle, altText, hasAltText, hidden, width, height, x, y, tabOrder, fieldCount, measureCount, filterCount |

Configuration — pbipkiller.config.json

Three levels of customization, all in one file:

1. Tune the built-in rules

{
  "failOn": "critical",
  "rules": {
    "PERF001": { "severity": "warning", "max": 12 },  // override a threshold
    "A11Y001": { "severity": "critical" },            // raise severity
    "CONS001": "off"                                   // disable
  }
}

Built-in rules:

| Id | Category | What it flags | Config keys | Default | |-----------|---------------|---------------|-------------|---------| | PERF001 | Performance | More than max data visuals on a page | max (15) | on | | PERF002 | Performance | More than max visuals report-wide | max (60) | on | | PERF003 | Performance | More than max slicers on a page | max (4) | on | | PERF004 | Performance | A visual binding more than max fields | max (10) | on | | PERF005 | Performance | Page total field load above max | max (60) | on | | PERF006 | Performance | A table/matrix with no visual-level filter (Top N) | — | on | | PERF009 | Performance | More than max pages | max (20) | off | | DATA001 | Data | A visual with no fields bound (renders blank) | — | on | | GOV001 | Governance | An uncertified / custom (third-party) visual | — | on | | GOV002 | Governance | A visual whose type is in your block-list | types ([]) | off | | GOV003 | Governance | A page still named "Page N" | — | on | | GOV005 | Governance | A visual reading from a restricted table | entities ([]) | off | | GOV006 | Governance | End-user data export is enabled | — | on | | GOV008 | Governance | Theme not in your approved list | allowed ([]) | off | | CONS001 | Consistency | A hidden page that still carries visuals | — | off | | CONS002 | Consistency | An empty page (0 visuals) | — | on | | CONS003 | Consistency | Mixed canvas sizes across the report | — | on | | CONS004 | Consistency | Data visuals that overlap or sit off-canvas | minOverlapPct (25) | on | | CONS005 | Consistency | Slicers placed inconsistently across pages | tolerancePx (10) | on | | CONS010 | Consistency | A bookmark group nothing navigates to | — | on | | A11Y001 | Accessibility | A data visual with no alt text | — | on | | A11Y002 | Accessibility | A visual with no explicit tab order | — | off | | A11Y003 | Accessibility | A decorative shape/image left in the tab order | — | on | | A11Y005 | Accessibility | A title containing banned jargon/acronyms | terms ([]) | off |

Rules marked "off" need configuration (a list/threshold) to do anything; enable them by adding their config entry. Rules PERF004/005/006, A11Y003/005, GOV006 and CONS005 implement official Microsoft Learn guidance (optimization and accessibility checklists).

2. The gate

"failOn"critical (default) · warning · optimization · none.

3. Write your own rules — no code

Declare rules as data. They run in CI exactly like a built-in:

{
  "customRules": [
    {
      "id": "team/no-image-visuals",
      "title": "No static images as visuals",
      "category": "Governance",
      "severity": "warning",
      "scope": "visual",
      "when": { "field": "type", "op": "eq", "value": "image" },
      "message": "Visual '{{name}}' on page '{{page}}' is a static image."
    },
    {
      "id": "team/dense-pages",
      "category": "Performance",
      "severity": "critical",
      "scope": "page",
      "when": [
        { "field": "visualCount", "op": "gt", "value": 20 },
        { "field": "hidden", "op": "eq", "value": false }
      ],
      "message": "Page '{{displayName}}' has {{visualCount}} visuals — split it."
    }
  ]
}
  • when is one predicate or an array (AND-ed).
  • Operators: eq ne gt gte lt lte in exists missing matches (regex).
  • message supports {{field}} placeholders from the table above.

Roadmap

  • ~~Vitest suite pinning fixture findings.~~ ✅ tests/report.test.ts
  • ~~A GitHub Action wrapper.~~ ✅ action/
  • Phase 2: extract a shared core engine so dataflow + pbip share one dispatcher.
  • Phase 3: TMDL semantic-model rules (reuses the M folding engine).