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

shadow-runner

v0.1.0

Published

Silent production code tester — run new code alongside old code, compare outputs, zero risk to users

Readme

shadow-runner 🏃‍♂️💨

Silent production code tester — run new code alongside old code in production, compare outputs, zero risk to users.

CI npm License: MIT


Why?

You write new logic. Tests pass. But you're scared to deploy because real production traffic is unpredictable.

| Existing Tools | What They Miss | |---|---| | A/B testing libs | Changes UI, affects users | | Feature flags | Still runs one path at a time | | Staging environments | Fake data, not real traffic | | Load testing | Volume only, not logic correctness |

shadow-runner is the only zero-risk production validator — it mirrors real requests, runs both old + new logic in parallel, and silently logs any differences.


Install

npm install shadow-runner

Quick Start

import { shadow } from 'shadow-runner';

// OLD logic (currently live)
function calculateDiscount(user) {
  return user.isPremium ? 0.2 : 0.1;
}

// NEW logic (you want to test safely)
function calculateDiscountV2(user) {
  return user.isPremium ? 0.25 : user.orders > 10 ? 0.15 : 0.1;
}

// Wrap with shadow — users ONLY get old result
// New logic runs silently and differences are logged
const safeDiscount = shadow(calculateDiscount, calculateDiscountV2, {
  sampleRate: 0.3,        // Run on 30% of traffic
  logDiffs: true,         // Log when outputs differ
  timeout: 100,           // Kill shadow if it takes > 100ms
  onDiff: (old, next, input) => {
    metrics.track('discount_diff', { old, next, user: input.id });
  },
});

// Use exactly like before — zero risk
const discount = safeDiscount(currentUser);

What It Captures

[shadow-runner] DIFF DETECTED
  Input:    {"id":"u_991","isPremium":false,"orders":14}
  Old:      0.1
  New:      0.15
  Duration: old=2ms, new=3ms
  Sample:   312 / 1040 calls (30%)
  Match:    94.2% identical so far ✅

API Reference

shadow(oldFn, newFn, options?)

Returns a drop-in replacement for oldFn that silently runs newFn in the background.

Options

| Option | Type | Default | Description | |---|---|---|---| | sampleRate | number | 1 | Fraction of calls to sample (0–1) | | logDiffs | boolean | true | Log diff details to console | | timeout | number | 5000 | Kill shadow execution after N ms | | onDiff | function | — | Callback when outputs differ | | when | function | — | Only shadow when predicate returns true | | autoCutover | object | — | Auto-promote when confidence is high | | async | boolean | auto | Force async mode | | serializer | function | — | Transform values before comparing | | reporter | Reporter | ConsoleReporter | Custom reporter instance |

Returned Function

The returned function has the same signature as oldFn, plus:

  • .stats — Live ShadowStats snapshot
  • .disable() — Stop sampling
  • .enable() — Resume sampling

Advanced Features

Conditional Sampling

shadow(oldFn, newFn, {
  when: (input) => input.country === 'PK',
});

Auto-Cutover

shadow(oldFn, newFn, {
  autoCutover: {
    threshold: 99.5,       // 99.5% match rate
    minSamples: 5000,      // After 5000 real calls
    onReady: () => deploy('v2'),
  },
});

Custom Serializer

shadow(oldFn, newFn, {
  serializer: (val) => JSON.stringify(val, null, 2),
});

Silent Reporter (for testing)

import { shadow, SilentReporter } from 'shadow-runner';

const reporter = new SilentReporter();
const wrapped = shadow(old, fresh, { reporter });

wrapped(input);

console.log(reporter.diffs);   // All diffs
console.log(reporter.samples); // All samples

Framework Adapters

Express

import { shadowMiddleware } from 'shadow-runner/express';

app.get('/api/price',
  shadowMiddleware(oldPriceHandler, newPriceHandler, {
    sampleRate: 0.2,
  })
);

Fastify

import { shadowHandler } from 'shadow-runner/fastify';

fastify.get('/api/price', shadowHandler(
  async (request, reply) => ({ price: 100 }),
  async (request, reply) => ({ price: 95 }),
  { sampleRate: 0.3 }
));

tRPC

import { shadowProcedure } from 'shadow-runner/trpc';

const getUser = shadowProcedure(
  async ({ input, ctx }) => db.users.findById(input.id),
  async ({ input, ctx }) => newDb.users.findById(input.id),
  { sampleRate: 0.5 }
);

Architecture

shadow-runner/
├── src/
│   ├── shadow.ts         # Core wrapper function
│   ├── sampler.ts        # Traffic sampling logic
│   ├── differ.ts         # Deep diff engine
│   ├── reporter.ts       # Log / metrics / webhook output
│   ├── async-guard.ts    # Isolates new code errors
│   ├── types.ts          # TypeScript types
│   └── index.ts          # Public API exports
├── adapters/
│   ├── express.ts        # Express middleware
│   ├── fastify.ts        # Fastify plugin
│   └── trpc.ts           # tRPC middleware
├── tests/                # Comprehensive vitest suite
├── tsup.config.ts        # Dual CJS + ESM build
└── package.json

Who Needs This

  • Fintech / e-commerce — refactoring pricing/discount logic
  • Auth systems — testing new permission checks
  • Recommendation engines — validating new algorithms
  • Any team afraid to deploy refactors to production

Contributing

See CONTRIBUTING.md for guidelines.

License

MIT © usama