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
DependencyCheckConfigwith per-checkcritical,timeout, andcacheTtlMsoverrides. - Result aggregation that returns
healthy/degraded/unhealthy: critical failures short-circuit, non-critical or degraded results downgrade todegraded. - Typed
EventBus<HealthEventMap>emittingcheck:passed,check:failed, andstatus:changedevents. - Zod-validated boundaries — every option object is parsed before use; failures throw
HealthCheckErrorwithINVALID_OPTIONS. - OTel instrumentation: every check run records on
health_check_runs_total+health_check_duration_ms, and every aggregate evaluation incrementshealth_check_aggregate_status_total. - Optional
LoggerLikeinjection fordebugregistration logs andwarnon 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.namerequired;version,defaultTimeout,defaultCacheTtlMs,checks,clock,timers,loggeroptional.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.