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

costguard

v0.3.1

Published

Detects expensive code patterns — runaway Firebase reads, missing real-time listener cleanup, polling abuse, and React render loops — before they hit production.

Readme

CostGuard

Catch expensive Firebase and React patterns before they hit production — and before they hit your bill.

CostGuard is a VS Code extension that detects runaway Firestore reads, missing listener cleanup, render loops, and other cost-heavy patterns as you write code. It adds inline squiggles, a per-file risk score, and optional gates that block bad code from being committed, merged, or deployed.


Features

Live diagnostics

Squiggles appear as you type (500ms debounce). No save required.

Risk scoring

Every flagged file gets a score and a breakdown by risk category, visible inline and in the status bar.

$(shield) Risk Score: 82/100   Cost: HIGH   Scalability: MEDIUM   Memory Leak: HIGH

Deployment gates

Three layers that stop risky code before it ships:

| Gate | When it runs | Blocks on | |---|---|---| | Pre-commit hook | git commit | HIGH risk | | GitHub Actions PR gate | Every pull request | HIGH risk | | Deploy gate | npm run predeploy | MEDIUM+ risk |


Installation

  1. Download costguard-0.2.0.vsix
  2. Open VS Code → Extensions··· menu → Install from VSIX
  3. Select the file and reload when prompted
  4. The setup wizard appears automatically — choose which protection layers to enable

Setup Wizard

On first install (and on every upgrade), a QuickPick appears ~1.5 seconds after VS Code loads:

CostGuard — Choose your protection layers

  ●  Pre-commit Hook         Block commits with HIGH risk violations
  ●  GitHub Actions PR Gate  Post risk card on PRs, block HIGH risk merges
  ○  Deploy Gate             Block firebase deploy on MEDIUM+ risk violations

Select what you want, press Enter. CostGuard writes all the necessary files automatically — no manual path setup.

To re-run the wizard at any time: Command PaletteCostGuard: Setup


What it detects

FCG001 — Unstable useEffect dependency error

Objects, arrays, functions, or call results used as useEffect dependencies without useMemo/useCallback. Every render creates a new reference, causing the effect to re-run infinitely — the root cause of most Firestore read runaway bills.

// Bad — query is a new object on every render
const query = { collection: 'invoices', status: 'open' };
useEffect(() => { fetchData(query); }, [query]); // ← FCG001

// Fix
const query = useMemo(() => ({ collection: 'invoices', status: 'open' }), []);

FCG002 — Unbounded Firestore read error

getDocs or collection calls without .limit(). On a large collection this can read thousands of documents in one call.

// Bad
const snap = await getDocs(collection(db, 'invoices')); // ← FCG002

// Fix
const snap = await getDocs(query(collection(db, 'invoices'), limit(50)));

FCG003 — Real-time listener on UI state warning

onSnapshot listeners that re-register whenever UI state like activeTab, selectedId, or showModal changes. Each re-registration opens a new listener and the old one may not be cleaned up.

// Bad — re-registers every time activeTab changes
useEffect(() => {
  return onSnapshot(collection(db, activeTab), ...);
}, [activeTab]); // ← FCG003

FCG004 — onSnapshot without cleanup error

Real-time listeners must return their unsubscribe function. Without it, every component mount adds a new listener that never closes.

// Bad — listener leaks on every remount
useEffect(() => {
  onSnapshot(collection(db, 'invoices'), handler); // ← FCG004
}, []);

// Fix
useEffect(() => {
  return onSnapshot(collection(db, 'invoices'), handler); // return the unsub
}, []);

FCG005 — Firestore read inside a loop error

getDoc or getDocs calls inside for, while, forEach, map, or similar constructs. Each iteration makes a separate network round-trip (N+1 reads).

// Bad — one read per invoice
for (const id of invoiceIds) {
  const doc = await getDoc(doc(db, 'invoices', id)); // ← FCG005
}

// Fix — use getAll / batched reads / a single query

FCG006 — setInterval without cleanup error

setInterval inside useEffect without a corresponding clearInterval return. Intervals stack up on every remount, polling Firebase endlessly.

// Bad
useEffect(() => {
  setInterval(() => fetchStats(), 5000); // ← FCG006
}, []);

// Fix
useEffect(() => {
  const id = setInterval(() => fetchStats(), 5000);
  return () => clearInterval(id);
}, []);

Risk scoring

Each violation carries a point weight based on its real-world cost impact. Scores are capped at 100.

| Rule | Risk categories | Points | |---|---|---| | FCG001 Unstable deps | Cost + Memory Leak | 10 | | FCG002 Unbounded read | Cost + Scalability | 18 | | FCG003 Listener UI dep | Cost | 12 | | FCG004 No snapshot cleanup | Memory Leak | 22 | | FCG005 Read in loop | Cost + Scalability | 20 | | FCG006 No interval cleanup | Memory Leak | 18 |

Risk levels per category

| Points | Level | |---|---| | 0 | LOW | | 1 – 24 | MEDIUM | | 25+ | HIGH |


Deployment gates

Pre-commit hook

Blocks git commit if staged files contain HIGH risk violations.

  CostGuard
  ────────────────────────────────────────────────────────────

  src/invoices.tsx
  Risk 58/100  |  Cost: HIGH  |  Scalability: LOW  |  Memory Leak: HIGH
    ✗  Line 42  [FCG002]  Unbounded Firestore read — add .limit()
    ✗  Line 71  [FCG004]  onSnapshot missing cleanup return

  ────────────────────────────────────────────────────────────
  2 violations in 1 file

  ✗  Blocked — fix HIGH risk violations before proceeding.

Installed automatically by the setup wizard into .git/hooks/pre-commit.

GitHub Actions PR gate

Runs on every pull request and posts a risk card comment. Fails the required check if HIGH risk violations are found, blocking the merge.

| File | Score | Cost | Scalability | Memory Leak | Violations | |---|---|---|---|---|---| | src/invoices.tsx | 🔴 58/100 | 🔴 HIGH | 🟢 LOW | 🔴 HIGH | 2 |

The workflow is written to .github/workflows/costguard.yml by the setup wizard. Requires costguard in devDependencies (added automatically).

Deploy gate

Runs before firebase deploy or any deploy script and blocks if MEDIUM+ risk violations are found.

npm run predeploy   # or wired via firebase.json predeploy hook

CLI

The analyzer is also available as a command-line tool for use in scripts and CI pipelines.

# Scan a directory
node out/cli.js src/

# Scan only staged files (for pre-commit hooks)
node out/cli.js --staged

# Output JSON for downstream tooling
node out/cli.js src/ --json

# GitHub Actions annotation format
node out/cli.js src/ --format=github

# Set the blocking threshold (default: HIGH)
node out/cli.js src/ --max-risk=MEDIUM

Exit codes: 0 = no violations above threshold · 1 = violations found


Configuration

| Setting | Default | Description | |---|---|---| | costGuard.enable | true | Enable / disable all diagnostics |

Toggle from Settings → search costGuard, or add to your workspace settings.json:

{
  "costGuard.enable": false
}

Commands

| Command | Description | |---|---| | CostGuard: Setup | Re-run the feature setup wizard | | CostGuard: Show Risk Score Details | Show full risk breakdown for the active file |