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

@zakkster/lite-di-health

v1.0.0

Published

Fail-closed readiness/liveness aggregator: composes plain health probes (and any object exposing check()) into one verdict, with a 0-allocation readyz()/livez() poll and a drain() latch for graceful shutdown. Pairs with @zakkster/lite-di-supervisor but ne

Readme

@zakkster/lite-di-health

Fail-closed readiness/liveness aggregator. Composes N health SOURCES -- plain pull probes, and any object exposing check() -- into one verdict. readyz()/livez() return a NUMBER (count of not-ready / not-live; 0 == healthy) on a single loop that allocates 0 bytes/op, hard-gated. report() is the structured /healthz body. drain() bleeds traffic for graceful shutdown; livez() is never touched. A source is healthy IFF its probe returns === true or === 0 -- everything else, including a THROW, is UNHEALTHY.

npm version sponsor Zero-GC npm bundle size npm downloads npm total downloads Tree-Shakeable TypeScript Dependencies license

The readiness surface the DI line was missing

Your service has subsystems -- a pool, a cache, a supervisor healing them -- and an orchestrator above it that needs two yes/no answers on a hot loop: is this process ALIVE (GET /livez), and is it READY to take traffic (GET /readyz)? Wiring those by hand drifts: a probe returns undefined on a bad branch and a naive handler reads it as "not falsey enough to fail", a subsystem dies silently and nothing flips, a SIGTERM fails the liveness probe and earns a SIGKILL mid-shutdown. The answers a load balancer trusts are exactly the answers that must fail CLOSED.

lite-di-health is that readiness surface. It aggregates N lane-tagged SOURCES into one verdict under a single rule: a source is healthy IFF its probe returns === true or === 0 -- false, undefined, null, NaN, a non-zero number, a string, a boxed Number(0), or a THROW are all UNHEALTHY, and a throwing probe never blinds the sweep to the rest. readyz() and livez() return a count (0 == healthy) on a loop that allocates exactly zero bytes; report() is the human /healthz body; drain() bleeds readiness for a graceful shutdown while keeping liveness up. It is the GAP-4 surface that closes the loop under the supervisor: the supervisor HEALS, this package REPORTS.

npm install @zakkster/lite-di-health

No dependency -- not even a peer. A source is a plain probe.

import { Health, LANES } from '@zakkster/lite-di-health';

const health = new Health();
health.source('db',    () => pool.connected, LANES.READY);      // ready when the pool is up
health.source('proc',  () => true,           LANES.LIVE);       // the event loop is turning
health.source('cache', () => cache.ok ? 0 : 1, LANES.BOTH);     // 0 == healthy, non-zero == not

// Two 0-allocation polls the load balancer hits every few seconds:
health.readyz();   // 0 -> ready (200), non-zero -> not (503)
health.livez();    // 0 -> live  (200), non-zero -> not (503)

// Graceful shutdown: bleed traffic, keep liveness up so you are not SIGKILLed.
health.drain();
health.readyz();   // non-zero -- the load balancer stops sending new work
health.livez();    // unchanged -- the process is still alive, draining in-flight work

Table of contents

Why this exists

A readiness verdict is only as trustworthy as its worst branch. The failure mode that bites in production is not a probe that throws -- it is a probe that returns something ALMOST truthy (undefined, NaN, an empty string, a boxed number) that a hand-rolled if (probe()) waves through, or a subsystem that dies without anyone flipping a flag. A readiness surface has to treat every unverified state as NOT healthy, or it lies to the load balancer at the worst possible moment.

This package makes that rule the ONLY rule and pins it with torture assertions: a source is healthy IFF === true || === 0; everything else is unhealthy; a throw is caught, counted unhealthy, and never aborts the sweep. It adds the three things a raw if cannot: LANES (one source, tagged for readiness, liveness, or both, so one topology drives two verdicts), a fail-closed EMPTY-lane verdict (nothing has vouched -> not ready, never a vacuous pass), and a DRAIN latch (bleed readiness on SIGTERM without failing liveness). "null is not zero," applied to observability.

What you get

  • A Health aggregator over N lane-tagged pull SOURCES -- register a probe under a unique name in the LIVE, READY, or BOTH lane; readyz() sweeps READY, livez() sweeps LIVE. One topology, two verdicts.
  • The fail-closed coercion rule as the single source of truth: healthy IFF === true || === 0; false / undefined / null / NaN / any non-zero number / any string / a boxed Number(0) / a THROW are all UNHEALTHY. A throwing probe is counted unhealthy and the sweep continues -- one sick source cannot blind the readout to the rest.
  • Two 0-allocation poll lanes: readyz() and livez() each return a count of not-ready / not-live sources in a single indexed loop -- no closure, no array, no string, no allocating branch. Hard-gated at 0.000 B/op over 1e6 calls.
  • A fail-closed EMPTY-lane verdict: an empty lane returns 1 (NOT ready / NOT live). Nothing has vouched, so nothing is claimed -- pinned by a torture assertion so it can never regress to a vacuous 0.
  • A drain() / undrain() latch: drain() marks the WHOLE ready lane not-ready (a load balancer bleeds traffic) while livez() stays untouched; reversible for a rolling deploy, or left latched on the SIGTERM path.
  • watchSupervisor(name, sup) -- duck-type any object exposing check() (e.g. @zakkster/lite-di-supervisor) into a source, 0 B/op per poll. No dependency.
  • Fail-closed registration: an unknown option, an empty/duplicate name, a non-function probe, an unknown lane, or a watchSupervisor on a check-less object all throw.

Full types ship in Health.d.ts.

The coercion rule

A health readout is a security boundary in miniature: when in doubt, say NO. So the rule is deliberately narrow -- a source is healthy for EXACTLY two return values, and unhealthy for every other thing a probe can produce:

| Probe returns | Verdict | Why | | -------------------- | ----------- | ------------------------------------------------------ | | true | healthy | an explicit boolean yes | | 0 | healthy | the "zero problems" count convention (supervisor's check()) | | -0 | healthy | -0 === 0 is true -- numerically zero, not special-cased | | false | unhealthy | an explicit no | | undefined | unhealthy | a probe that fell off the end -- unverified | | null | unhealthy | "null is not zero" | | NaN | unhealthy | a computation that went wrong | | 'ok' (any string) | unhealthy | not a verdict this API accepts | | 1, 2, -1 (non-zero) | unhealthy | a non-zero problem count | | new Number(0) | unhealthy | a boxed object -- === 0 is false; fail closed | | a THROW | unhealthy | caught, counted, and the sweep CONTINUES |

The last row is the load-bearing one: a probe that throws does not crash readyz() and does not stop it from reaching the sources after it. It is counted unhealthy and the loop moves on, so a single sick subsystem can never blind the readout to a healthy majority. This is the supervisor's "escalate flush" lesson in a new coat: never coerce an unverified state toward healthy.

API reference

Constructor

new Health(options?: {
  sources?: { name: string | symbol; probe: () => boolean | number; lane?: 1 | 2 | 3 }[];
})
  • options -- optional. The ONLY known key is sources; any other key throws with a did-you-mean hint (unknown option 'source'. Did you mean 'sources'?).
  • sources -- optional initial sources, each an object { name, probe, lane? }, validated and appended via the SAME path as .source() (so every fail-closed rule below applies identically). lane defaults to BOTH.

Methods

source(name: string | symbol, probe: () => boolean | number, lane?: 1 | 2 | 3): void
watchSupervisor(name: string | symbol, sup: { check(): number }, lane?: 1 | 2 | 3): void
readyz(): number
livez(): number
report(): { ready: number; live: number; state: number; sources: { name; lane; status }[] }
drain(): void
undrain(): void
get state(): number
  • source(name, probe, lane?) -- COLD. Register a pull probe under a unique name in a lane (default BOTH). A non-empty-string / non-symbol name, a duplicate name, a non-function probe, or an unknown lane each throw. Append-only -- there is no remove in alpha (topology is boot-locked, like the sibling bricks).
  • watchSupervisor(name, sup, lane?) -- COLD. Duck-types sup.check: if sup is null/undefined or sup.check is not a function, it THROWS (a phantom source -- silently never-healthy or silently skipped -- is a fail-open readout). It registers a 0-allocation probe calling sup.check(), whose count composes directly (0 healthy, non-zero unhealthy).
  • readyz(): number -- HOT. The count of not-ready sources among READY-lane sources (0 == ready). A single indexed loop; no closure, no array, no allocating branch. An empty READY lane returns 1 (fail-closed). While DRAINING it returns the READY-source count (the whole lane reads not-ready). 0.000 B/op.
  • livez(): number -- HOT. The count of not-live sources among LIVE-lane sources (0 == live). An empty LIVE lane returns 1 (fail-closed, symmetric -- see the footgun below). NEVER affected by drain. 0.000 B/op.
  • report(): object -- COLD. { ready, live, state, sources } for the human /healthz body. ready/live come from readyz()/livez() (so ready reflects the drain latch); sources is [{ name, lane, status }] where status is the RAW coercion verdict ('healthy' | 'unhealthy'), NOT forced by drain -- an operator draining a still-passing node should see it passing. Re-probes each source; allocates freely.
  • drain(): void -- latch OK -> DRAINING. Idempotent. readyz() flips to all-not-ready; livez() is untouched. The seam an orchestrator pulls before teardown.
  • undrain(): void -- latch DRAINING -> OK. Idempotent. Reverses drain() for a rolling deploy / maintenance toggle; the SIGTERM path simply never calls it.
  • state (getter) -- the current drain state, one of STATES.

The empty-LIVE-lane footgun. The empty-lane rule is SYMMETRIC and fail- closed: an empty LIVE lane returns 1, so a process that exposes /livez yet registers NO LIVE (or BOTH) source reports NOT-live -- and a Kubernetes liveness probe would RESTART it. This is by design (nothing has vouched for liveness). Register at least one LIVE source if you wire /livez. The code stays fail-closed; this warning carries the nuance.

Constants

LANES  = { LIVE: 1, READY: 2, BOTH: 3 }   // frozen bitmask
STATES = { OK: 0, DRAINING: 1 }           // frozen int enum
VERSION                                   // version string, three-place-synced

| Export | Type | Value / meaning | | --------------- | --------------- | ------------------------------------------------------------ | | LANES.LIVE | number (1) | Source counts toward livez() only. | | LANES.READY | number (2) | Source counts toward readyz() only. | | LANES.BOTH | number (3) | Source counts toward both LIVE and READY (bit 1 + bit 2). The default. | | STATES.OK | number (0) | Normal operation. readyz() reflects the probes. | | STATES.DRAINING | number (1) | Draining. readyz() reports all-not-ready; livez() unchanged. | | VERSION | string | '1.0.0'. |

Composability with the DI line

A full readiness surface: subsystems wired in a container, a supervisor healing them, and a Health aggregator folding the supervisor's check() plus a couple of raw probes into the two answers the orchestrator polls -- with a drain() on SIGTERM that bleeds traffic before teardown.

import { Container } from '@zakkster/lite-di-container';
import { Supervisor, STRATEGIES } from '@zakkster/lite-di-supervisor';
import { Health, LANES } from '@zakkster/lite-di-health';

// --- Subsystems, wired + supervised. ---------------------------------------
const c = new Container();
c.singleton('pool', Pool);
c.singleton('repo', Repo, ['pool']);
c.singleton('worker', Worker, ['repo']);
c.boot();

const sup = new Supervisor(c, {
  children: ['pool', 'repo', 'worker'],
  strategy: STRATEGIES.REST_FOR_ONE,
});
await sup.start();

// --- The readiness surface. -------------------------------------------------
const health = new Health();
health.watchSupervisor('kernel', sup, LANES.READY);   // supervisor healthy -> ready
health.source('proc', () => true, LANES.LIVE);        // the process is alive
health.source('deps', () => pingUpstream() ? 0 : 1, LANES.READY);

// --- The k8s probe endpoints -- 0 B/op each. --------------------------------
function readyz() { return health.readyz() === 0 ? 200 : 503; }
function livez()  { return health.livez()  === 0 ? 200 : 503; }
function healthz() { return health.report(); }

// --- Graceful shutdown. Bleed readiness, keep liveness up. ------------------
process.on('SIGTERM', async () => {
  health.drain();          // readyz -> 503; livez stays 200 (no SIGKILL)
  await drainInFlight();   // let the load balancer stop sending, finish work
  await sup.shutdown();
  await c.shutdown();
  process.exit(0);
});

Every stage snaps onto the DI line, but nothing is REQUIRED: watchSupervisor duck-types check(), so any object with that method composes and this package carries no dependency. The container owns the instances, the supervisor owns the healing, and this package owns the verdict the orchestrator reads.

Zero-GC design notes

readyz() and livez() are the only hot lanes -- the readiness/liveness poll a long-running server hits every few seconds -- so they are gated at exactly zero, not "small". Each is a single indexed loop over the lane-tagged parallel arrays (_probes / _lane), masking the lane bit, calling the probe, and counting the not-healthy verdicts. It captures no closure, builds no message, allocates no array or object, and takes no allocating branch. The try/catch around each probe allocates NOTHING on the healthy path (no throw = no Error); a throw is the rare unhealthy path where allocation is fine. Everything else -- source, watchSupervisor, report -- is COLD: registration is rare and report() is the human path, so their allocation is fine.

| Path | Steady-state allocations | | --------------------------------- | ----------------------------------------------- | | readyz() (poll, N sources) | 0.000 B/op (HARD gate, 1e6 ops, 64 sources) | | livez() (poll, N sources) | 0.000 B/op (HARD gate, 1e6 ops, 64 sources) | | watchSupervisor-backed readyz() | 0.000 B/op (sup.check() is 0-alloc) | | source / watchSupervisor | cold by design (registration, rare) | | report() | cold by design (the human /healthz body) |

The torture gate (@zakkster/lite-leak + @zakkster/lite-gc-profiler, under --expose-gc) proves both the poll and the soak:

  • readyz() / livez(): 0.000 B/op over 1e6 calls on a 64-source fan-out, maxMajor 0.
  • T7 soak: 100000 source-set churn cycles (retention returns to 0) + 1e6 readyz/livez polls interleaved with drain/undrain cycles, major GCs = 0, the probe array never grows and the lane SoA is never reallocated.

Numbers reproduce with node --expose-gc test/torture.mjs. No gate output is a FAIL.

Design decisions worth knowing

The four load-bearing forks were ratified in decisions/0001-health-model.md.

  • Source contract is PULL (Fork 1). A source is () => boolean | number, swept on demand. WHY: the probe path stays 0-alloc and stateless, and a source can never go stale. PUSH would hold mutable last-known state and fail OPEN the instant one source forgot to report a transition.
  • One lane-tagged aggregator (Fork 2). Each source carries a lane mask (LIVE | READY | BOTH); one topology drives both readyz() and livez(). WHY: two separate aggregators drift -- a source on one but not the other is a silent gap. The empty-lane verdict is fail-closed (Fork 2a): an empty lane returns 1 (not-ready), pinned by a torture assertion so it can never regress to the vacuous 0 ("nothing has vouched, yet I claim ready"). Symmetric for both lanes -- with the LIVE footgun documented above, not softened in code.
  • Number-hot + object-cold (Fork 3). readyz()/livez() return a NUMBER (the 0-alloc poll lane); report() returns the structured object (the cold, human path). WHY: a single rich return would allocate a { ... } and a sources array on the poll lane, several times a second, forever. This mirrors the supervisor's check() (number) vs describe() (object) split exactly.
  • drain() flips readyz only, and is reversible (Fork 4). While DRAINING, readyz() reports the whole ready lane not-ready; livez() is UNAFFECTED, and undrain() reverses the latch. WHY: flipping livez() on drain would fail the k8s liveness probe mid-shutdown and invite a SIGKILL before graceful teardown finishes. The process is ALIVE during a drain, just not accepting new work.

Every unverified state fails closed: an unknown option throws with a did-you-mean hint, an empty/duplicate name or non-function probe throws, a watchSupervisor on a check-less object throws, an absent/throwing/undefined probe is UNHEALTHY (never skipped, never coerced toward 0), an empty lane is not-ready, and the drain latch marks the WHOLE ready lane not-ready, not one token.

Testing

  • npm test -- node:test cases across construction, registration, the coercion matrix, the lane routing, the empty-lane + drain pins, and watchSupervisor.
  • npm run torture -- node --expose-gc test/torture.mjs: the 0.000 B/op readyz() + livez() gate over 1e6 calls on a 64-source fan-out (maxMajor 0, plus a watchSupervisor-backed variant), the coercion differential vs a naive oracle aggregator, and the T7 soak (100000 churn cycles + 1e6 polls, major GCs 0, leak size 0).
  • npm run verify -- npm test then npm run torture, the publish gate.

What this is not

  • Not an HTTP server or router. You wire readyz() / livez() / report() into your own framework's routes (see the COOKBOOK); this produces the verdict, not the transport.
  • Not a process / container health manager. Kubernetes, systemd, and pm2 READ a readiness verdict; this package PRODUCES one. It is the surface their probes hit, not the orchestrator.
  • Not a metrics or tracing library. A source is a binary healthy/not verdict, not a gauge, a histogram, or a span.
  • Not a liveness prober with timers. Sources are PULLED on demand when you call readyz()/livez(); this package runs no timers and polls nothing on its own.
  • Not a supervisor. Healing subsystems on a fault is @zakkster/lite-di-supervisor; this package folds that supervisor's check() (and any other probe) into a readiness verdict, and needs no dependency to do it.

Ecosystem

Part of the @zakkster/lite-di-* line -- a self-healing zero-GC backend service kernel:

  • @zakkster/lite-di-container -- the SPINE: DI wiring, lifetimes, and the invalidate / rebind restart primitive.
  • @zakkster/lite-di-supervisor -- the GAP-1 self-healing keystone; its check() is what watchSupervisor folds in.
  • @zakkster/lite-di-event-bus -- zero-GC DI fan-out / pub-sub (sibling).
  • @zakkster/lite-di-cron -- wall-clock scheduling over the container (sibling).
  • @zakkster/lite-di-ticker -- the per-frame system loop, 0 B/frame (sibling).
  • @zakkster/lite-di-graph -- topology visualization / describe() formatting (sibling).
  • @zakkster/lite-di-health -- this package: the GAP-4 readiness/liveness surface that closes the loop under the supervisor.
  • @zakkster/lite-gc-profiler / @zakkster/lite-leak -- the allocation gate and retention witness that prove the 0 B/op poll and the soak.

License

MIT (c) Zahary Shinikchiev [email protected]