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

@dotworld/shadow-canary-core

v0.7.2

Published

Runtime primitives for shadow-canary deployment pattern on Vercel

Readme

@dotworld/shadow-canary-core

Runtime primitives for the shadow-canary deployment pattern on Vercel.

Implements permanent 1% shadow traffic (master branch) plus an SLO-gated canary ramp (0→100%) for Next.js projects, using Vercel Edge Config for real-time configuration and HMAC-based admin sessions.

Two entry points

| Import path | Runtime | What it exports | |---|---|---| | @dotworld/shadow-canary-core | Node.js only | Everything — Vercel REST wrappers, HMAC session helpers, types | | @dotworld/shadow-canary-core/edge | Edge + Node | getShadowConfig, shadowCanaryMiddleware, types |

Installation

pnpm add @dotworld/shadow-canary-core

Peer dependencies (already installed in a Next.js project):

pnpm add next @vercel/edge-config

Usage

Drop-in middleware (Next.js 15, or Next.js 16 Edge)

// middleware.ts
import { shadowCanaryMiddleware } from '@dotworld/shadow-canary-core/edge';
import { NextRequest, NextResponse } from 'next/server';

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico|admin|.*\\..*).*)'],
};

export async function middleware(req: NextRequest) {
  const res = await shadowCanaryMiddleware(req);
  return res ?? NextResponse.next();
}

Drop-in proxy (Next.js 16, Node runtime)

Next.js 16 renamed middleware.tsproxy.ts (and the exported function middleware()proxy()). proxy.ts runs on the Node.js runtime — for Edge runtime semantics, keep middleware.ts (still supported on v16, deprecated). The lib's function is identical across both — we just expose shadowCanaryProxy as an alias matching the v16 naming:

// proxy.ts
import { shadowCanaryProxy } from '@dotworld/shadow-canary-core';
import { NextRequest, NextResponse } from 'next/server';

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico|admin|.*\\..*).*)'],
};

export async function proxy(req: NextRequest) {
  const res = await shadowCanaryProxy(req);
  return res ?? NextResponse.next();
}

Migrating an existing v15 install: npx @next/codemod@canary middleware-to-proxy .

Composing with existing middleware

// middleware.ts
import { shadowCanaryMiddleware } from '@dotworld/shadow-canary-core/edge';
import { NextRequest, NextResponse } from 'next/server';

export async function middleware(req: NextRequest) {
  // Your existing logic first
  if (req.nextUrl.pathname.startsWith('/admin')) {
    return NextResponse.redirect(new URL('/login', req.url));
  }

  const res = await shadowCanaryMiddleware(req, {
    cookieName: 'my-bucket',    // default: 'shadow-bucket'
    cookieMaxAge: 3600,         // default: 86400 (24h)
  });
  return res ?? NextResponse.next();
}

Production branch

Both the shadow and current-prod slots are deployed with vercel deploy --prod, which means both get VERCEL_ENV=production baked in at build time. The middleware uses VERCEL_GIT_COMMIT_REF (git branch name) as the only runtime signal to distinguish them — only deploys built from the configured prod branch actually route traffic.

Default branch name: 'production' (matches the branch the reference deploy workflows push to). If your prod branch is main, master, or anything else, tell the middleware:

await shadowCanaryMiddleware(req, {
  productionBranch: 'master', // or 'main', etc.
});

Or set the SHADOW_CANARY_PRODUCTION_BRANCH env var on Vercel — the middleware picks it up automatically, no code change needed. Pass '' (empty string) to disable the branch filter entirely when you don't follow the 2-branch shadow-canary topology.

Vercel Deployment Protection

If your Vercel project has Deployment Protection enabled (password / SSO), the shadow and previous-prod deployment URLs would block the rewrite. Enable "Protection Bypass for Automation" in the project settings — Vercel auto-injects the VERCEL_AUTOMATION_BYPASS_SECRET env var, which the middleware picks up automatically and attaches to rewrites as x-vercel-protection-bypass + x-vercel-set-bypass-cookie: samesitenone.

Zero caller config required. Override with the bypassToken option if you need a different source, or pass '' to explicitly disable:

await shadowCanaryMiddleware(req, {
  bypassToken: process.env.MY_CUSTOM_BYPASS, // default: VERCEL_AUTOMATION_BYPASS_SECRET
});

Reading config from the edge

import { getShadowConfig } from '@dotworld/shadow-canary-core/edge';

const cfg = await getShadowConfig(); // 60s in-memory TTL cache
console.log(cfg?.trafficShadowPercent); // e.g. 1

Vercel REST API wrappers (node only)

import {
  readShadowConfig,
  patchShadowConfig,
  listDeployments,
  promoteDeployment,
} from '@dotworld/shadow-canary-core';

// Read current config
const cfg = await readShadowConfig();

// Patch — start a canary at 5%
await patchShadowConfig({
  trafficProdCanaryPercent: 5,
  canaryStartedAt: new Date().toISOString(),
});

// Promote a deployment
const deployments = await listDeployments();
await promoteDeployment(deployments[0].uid);

Runtime info (slot, commit, bucket)

Identify which deploy slot the running code lives in (shadow / prod-current / prod-previous / preview / dev) and stamp every Sentry error / PostHog event / log line with build metadata:

import { getBuildInfo, getRuntimeBucket } from '@dotworld/shadow-canary-core';

// Sync, env-var-only — safe in Sentry.init / module-level.
const info = getBuildInfo();
// { slot: 'production-track', commitShaShort: 'abc1234', branch: 'production', ... }

// Async, queries Edge Config — narrows production-track to prod-current vs prod-previous.
const runtime = await getRuntimeBucket();
// { bucket: 'prod-current', ... }

Critical for canary observability: when a release breaks, filtering errors by bucket=prod-current confirms whether the new deploy is the cause vs. something orthogonal. Full Sentry / PostHog integration recipes: docs/reference/runtime-info.

HMAC admin sessions (node only)

import {
  verifyCredentials,
  createSessionToken,
  verifySessionToken,
  SESSION_COOKIE,
  SESSION_MAX_AGE,
} from '@dotworld/shadow-canary-core';

// In a login route handler
if (verifyCredentials(user, pass)) {
  const token = createSessionToken();
  // Set SESSION_COOKIE = token with maxAge SESSION_MAX_AGE
}

// In a protected route
if (!verifySessionToken(token)) {
  return new Response('Unauthorized', { status: 401 });
}

Environment variables

| Variable | Used by | Required | |---|---|---| | EDGE_CONFIG | @vercel/edge-config SDK | Edge Config reads | | VERCEL_API_TOKEN | REST API calls | Admin operations | | VERCEL_ORG_ID | REST API calls | Admin operations | | VERCEL_PROJECT_ID | REST API calls | Deployments / promote | | VERCEL_EDGE_CONFIG_ID | REST API patching | Config writes | | VERCEL_AUTOMATION_BYPASS_SECRET | Edge middleware | Rewrites past Deployment Protection (auto-injected by Vercel) | | SHADOW_CANARY_PRODUCTION_BRANCH | Edge middleware | Git branch name of the current-prod slot (default: production) | | ADMIN_USER | Session auth | Admin login (default: admin) | | ADMIN_PASS | Session auth | Admin login (default: 12345) | | ADMIN_SESSION_SECRET | HMAC signing | Session tokens |

License

MIT