// Three-valued observability primitives. // // Every probe answer is one of THREE states, never two: // "ok" — the probe saw the thing and it is present/working // "absent" — the probe SAW the surface and the thing is genuinely missing // "unobservable" — the probe COULD NOT SEE (dependency down, timeout, auth // failure, endpoint missing). Never collapsed into ok/absent. // // Every result carries measurement_surface (exactly what was measured) and // observed_at (ISO timestamp). export const OK = "ok"; export const ABSENT = "absent"; export const UNOBSERVABLE = "unobservable"; export function probeResult(status, measurementSurface, extra = {}) { return { status, measurement_surface: measurementSurface, observed_at: new Date().toISOString(), ...extra, }; } /** * Perform an HTTP GET probe of `url`. Returns a three-valued probeResult: * - ok when the response status passes `okWhen` (default: 2xx) * - unobservable on network error, timeout, or non-passing HTTP status * (a 401/404/500 from the surface means we could not observe the truth, * not that the dependency is healthy or absent). * `classify(response, bodyText)` may optionally downgrade an HTTP-ok response * to ABSENT (e.g. endpoint reachable but required model not installed). */ export async function httpProbe(url, { fetchImpl = fetch, timeoutMs = 2500, classify } = {}) { const started = Date.now(); let res; try { res = await fetchImpl(url, { signal: AbortSignal.timeout(timeoutMs) }); } catch (err) { return probeResult(UNOBSERVABLE, url, { reason: `fetch failed: ${err?.cause?.code || err?.name || "error"}: ${err?.message}`, latency_ms: Date.now() - started, }); } const latency = Date.now() - started; if (!res.ok) { return probeResult(UNOBSERVABLE, url, { reason: `HTTP ${res.status} from probe surface`, http_status: res.status, latency_ms: latency, }); } if (classify) { let bodyText = ""; try { bodyText = await res.text(); } catch { return probeResult(UNOBSERVABLE, url, { reason: "response body unreadable", latency_ms: latency, }); } const classified = classify(res, bodyText); if (classified) return probeResult(classified.status, url, { latency_ms: latency, ...classified.extra }); } return probeResult(OK, url, { http_status: res.status, latency_ms: latency }); }