Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,65 @@
# `health-checker/health-checker`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## 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
```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<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`.