Files
shre-embed/src/probe.js
T

68 lines
2.4 KiB
JavaScript
Raw Normal View History

// 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 });
}