# `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` 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 ```ts 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 ```ts 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`. ## 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`.