Files
2026-05-27 23:47:53 +01:00

3.4 KiB

health-checker/health-checker

(no submodules)

Purpose

HealthChecker is the orchestration core of @suaveplan/health-check. It owns a registry of dependency checks, runs them on demand, enforces per-check timeouts through an injected TimersLike, caches results for a configurable TTL, and produces standardised health, readiness, and liveness payloads suitable for the /health, /ready, and /live endpoints common to Kubernetes, ECS, Nomad, and reverse-proxy probes. Wall-clock readings come from the injected Clock, so tests can drive deterministic uptime + cache-expiry behaviour.

Features

  • Registry of DependencyCheckConfig with per-check critical, timeout, and cacheTtlMs overrides.
  • Result aggregation that returns healthy / degraded / unhealthy: critical failures short-circuit, non-critical or degraded results downgrade to degraded.
  • Typed EventBus<HealthEventMap> emitting check:passed, check:failed, and status:changed events.
  • Zod-validated boundaries — every option object is parsed before use; failures throw HealthCheckError with INVALID_OPTIONS.
  • OTel instrumentation: every check run records on health_check_runs_total + health_check_duration_ms, and every aggregate evaluation increments health_check_aggregate_status_total.
  • Optional LoggerLike injection for debug registration logs and warn on failed checks.

Usage: Basic

import { HealthChecker, createHttpCheck } from "@suaveplan/health-check";

const checker = new HealthChecker({ name: "api", version: "1.0.0" });
checker.addCheck({
    name: "upstream",
    check: createHttpCheck({ url: "https://up.example.com/health" }),
});
const response = await checker.health();

Usage: Advanced

import { HealthChecker, HealthEvent } from "@suaveplan/health-check";

const checker = new HealthChecker({
    name: "api",
    defaultTimeout: 2_000,
    defaultCacheTtlMs: 5_000,
    clock: () => performance.now() + epoch,
    timers: fakeTimers,
    logger,
});

checker.events.on(HealthEvent.STATUS_CHANGED, ({ previous, current }) => {
    pagerduty.notify({ previous, current });
});

API Reference

  • new HealthChecker(options) — options.name required; version, defaultTimeout, defaultCacheTtlMs, checks, clock, timers, logger optional.
  • addCheck(config) / removeCheck(name) / getCheckNames().
  • runCheck(name) → CheckResult (single check, honours cache).
  • health() → HealthResponse (runs all, aggregates).
  • ready() → ReadinessResponse (critical-only failures).
  • live() → LivenessResponse (process is responding).
  • clearCache() / getUptimeMs().
  • events: EventBus<HealthEventMap>.

Implementation Notes

Timeout enforcement is delegated to the injected TimersLike rather than setTimeout so tests can drive timeouts deterministically without vi.useFakeTimers(). The cache is keyed by check name and stores a frozen CheckResult with an absolute expiresAt computed against this.clock() — clock skew between writes and reads cannot leak negative TTLs because we re-read the clock on every check. Cached reads tag results with cached: true so consumers can distinguish them. Failures inside executeWithTimeout are wrapped in HealthCheckError(CHECK_EXECUTION_FAILED) so the outer runCheck always sees a typed error; the CHECK_TIMEOUT branch is preserved separately so it can be surfaced in details.timedOut.