Functional Weave
Code in Rust

monitor.incidents@1.0.0

impl/typescript.ts

2,253 bytes · the TypeScript implementation · view raw

import { type Check, type CheckStatus } from "./monitor_check_status_types.ts";
import { type Incident } from "./monitor_incidents_types.ts";

/**
 * The incidents in a check series. A run of consecutive bad checks is one
 * incident, from its first bad check to the first good check after it, but
 * only when it holds at least confirmChecks checks: a single failed probe is
 * noise, not an outage, and would otherwise page and skew MTTR.
 */
export function findIncidents(checks: readonly Check[], incidentStatus: CheckStatus, confirmChecks: number, now: number): readonly Incident[] {
  if (!Number.isSafeInteger(now)) throw new RangeError("now must be a whole number of seconds");
  if (incidentStatus !== "down" && incidentStatus !== "degraded") {
    throw new RangeError(`incidentStatus must be down or degraded, received ${String(incidentStatus)}`);
  }
  if (!Number.isSafeInteger(confirmChecks) || confirmChecks < 1) {
    throw new RangeError(`confirmChecks must be at least 1, received ${confirmChecks}`);
  }
  for (let i = 0; i < checks.length; i++) {
    const s = checks[i].status;
    if (s !== "up" && s !== "degraded" && s !== "down") throw new RangeError(`unknown check status: ${String(s)}`);
    if (i > 0 && checks[i].at <= checks[i - 1].at) {
      throw new RangeError(`checks must be in strictly ascending time order: ${checks[i].at} follows ${checks[i - 1].at}`);
    }
  }
  if (checks.length > 0 && now < checks[checks.length - 1].at) {
    throw new RangeError(`now ${now} is before the last check at ${checks[checks.length - 1].at}`);
  }
  const bad = (s: CheckStatus): boolean => s === "down" || (incidentStatus === "degraded" && s === "degraded");
  const out: Incident[] = [];
  let i = 0;
  while (i < checks.length) {
    if (!bad(checks[i].status)) { i++; continue; }
    const start = checks[i].at;
    let count = 0;
    let worst: CheckStatus = "degraded";
    while (i < checks.length && bad(checks[i].status)) {
      if (checks[i].status === "down") worst = "down";
      count++;
      i++;
    }
    const end = i < checks.length ? checks[i].at : null;
    if (count >= confirmChecks) {
      out.push({ start, end, durationSeconds: (end ?? now) - start, worst, checks: count });
    }
  }
  return out;
}