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

@deploydoubles/checks

v0.1.1

Published

Generates the deploy report (/.well-known/deploy-report) for Node.js apps: a Next.js route handler and an in-process scheduler.

Readme

@deploydoubles/checks

Generates the deploy report — GET /.well-known/deploy-report — for Node.js apps, so a pipeline, an agent or a monitor can tell whether a deploy actually works. Today it ships what a Next.js (App Router) app needs: a route handler and an in-process scheduler.

This package is developed in the deploydoubles/doubles monorepo. Its own repository is a read-only mirror; open issues and pull requests in the monorepo.

Requires Node 24 or newer. No runtime dependencies.

Install (Next.js)

npm install @deploydoubles/checks

app/.well-known/deploy-report/route.ts:

export { GET } from '@deploydoubles/checks';

export const dynamic = 'force-dynamic';
export const runtime = 'nodejs';

instrumentation.ts — starts the checks when the server boots: one run now, then one every 60 seconds, in the same process:

export async function register(): Promise<void> {
  if (process.env.NEXT_RUNTIME !== 'nodejs') return;
  const { startDeployReport } = await import('@deploydoubles/checks');
  const { Client } = await import('pg');
  startDeployReport({ database: () => new Client({ connectionString: process.env.DATABASE_URL }) });
}

database is a factory for a fresh node-postgres-compatible client; it is called once per run, never per request. In next.config.ts, add the package to serverExternalPackages so it is loaded from node_modules rather than bundled.

The database check is bounded: connect and queries together get 10 seconds, after which the client is ended and the check fails with database_unreachable. A whole scheduled run gets 45 seconds; a run that outlives that no longer holds back the next one.

Where it runs

The checks need a long-running server process and a writable directory that outlives each deploy. Serverless functions, edge runtimes and read-only file systems are not supported: there the scheduler never runs on time, and a store the app cannot write to is reported as scheduler: fail with report_store_unwritable.

Keep storage/ (the result store and the storage marker, see below) in a directory that is shared between releases — the persistent storage your platform offers, or Deployer's shared_dirs. A store that is replaced on every deploy cannot see a rollback, and makes the storage check meaningless.

Configure

Commit deploy-report.config.json in the app root:

{
  "tier": "public",
  "checks": {
    "database": { "expected": "postgres" },
    "scheduler": {},
    "scheduler.release": {},
    "storage": {},
    "env": { "required": ["NODE_ENV"] }
  }
}

| Key | Default | Meaning | |---|---|---| | tier | "public" | Keep "public". Only reference apps commit "full". It is read from this file only — never from the environment. | | checks | scheduler, scheduler.release, storage | The checks this app declares, with what it expects | | store_path | storage/deploy-report | Where results are stored | | storage_marker_path | storage/app/deploy-report | Where the storage check writes its marker | | name, double | none | Shown in the full tier |

DEPLOY_REPORT_TOKEN (≥ 32 characters, sent as Authorization: Bearer <token>) unlocks the full tier; DEPLOY_RUN_ID lets the verifier's Deploy-Run-Id header be matched. The commit is read from platform variables, then a REVISION file, then .git — the same order as deploydoubles/checks-php.

What it checks

database (connects, engine, write then read through a temporary table — skipped with database_write_unsupported on a server without temporary tables), storage (writable marker), env (required names present), scheduler and scheduler.release (a fresh heartbeat from the in-process scheduler, one release per minute). Results older than three minutes turn the report into scheduler: fail; a store the app cannot write to turns it into scheduler: fail with report_store_unwritable.

An app with a queue worker can declare queue and queue.release: pass dispatchProbe to startDeployReport to enqueue a job carrying the probe name, and call answerDeployReportProbe(probe) from that job. A probe left unanswered for two minutes fails queue. Declaring queue or queue.release without passing dispatchProbe fails queue at once with queue_driver_mismatch (and skips queue.release), rather than leaving it pending.

Every checked_at dates the evidence (the scheduled run, the heartbeat, the answered probe), never the request. When a release comes back — a rollback, or a redeploy of a commit that ran before — its first scheduled run discards what its earlier life left in the store and starts fresh, so booted_at is when it was first seen running again. A rollback can restart the process less than 90 seconds after the release's last run, which is too soon to be recognised as a return; the minute the process started in is then recorded as a takeover minute, so the handover is not reported as two releases running in the same minute. Until that minute is over scheduler.release is pending, because only the next run shows whether the other release has stopped.

Security

The same rules as deploydoubles/checks-php, and the same report shape:

  • Without a valid token the report says only pass or fail per check.
  • The full tier never contains error messages, hostnames, IP addresses, usernames, paths, connection strings or environment values. Errors are mapped to fixed codes by their code (SQLSTATE, Node system error or mysql2 code) or driver error number, exactly as checks-php maps them; a connect that fails without any code counts as database_unreachable. Messages are never read.
  • Tokens and run IDs are compared in constant time (crypto.timingSafeEqual).
  • A request only reads stored results. It never runs a check or opens a connection.

Licence

MIT.