Functional Weave
Code in Python

monitor.uptime@1.0.0

impl/typescript.ts

2,320 bytes · the TypeScript implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

import { type Check } from "./monitor_check_status.ts";  ← from monitor.check-status ^1.0.0 · built alongside by fune
import { type UptimeReport } from "./monitor_uptime_types.ts";

/**
 * Seconds up, degraded, down and unknown over [from, to). Each check's status
 * holds from its `at` until the next check, but for at most maxGapSeconds: a
 * monitor that stopped reporting has not proved the target was up.
 */
export function uptime(checks: readonly Check[], from: number, to: number, maxGapSeconds: number): UptimeReport {
  if (!Number.isSafeInteger(from) || !Number.isSafeInteger(to)) throw new RangeError("from and to must be whole seconds");
  if (from > to) throw new RangeError(`from must not be after to: ${from} > ${to}`);
  if (!Number.isSafeInteger(maxGapSeconds) || maxGapSeconds < 1) {
    throw new RangeError(`maxGapSeconds must be a whole number of at least 1, received ${maxGapSeconds}`);
  }
  let up = 0;
  let degraded = 0;
  let down = 0;
  let count = 0;
  let downCount = 0;
  for (let i = 0; i < checks.length; i++) {
    const c = checks[i];
    if (c.status !== "up" && c.status !== "degraded" && c.status !== "down") {
      throw new RangeError(`unknown check status: ${String(c.status)}`);
    }
    if (i > 0 && c.at <= checks[i - 1].at) {
      throw new RangeError(`checks must be in strictly ascending time order: ${c.at} follows ${checks[i - 1].at}`);
    }
    if (c.at >= from && c.at < to) {
      count += 1;
      if (c.status === "down") downCount += 1;
    }
    // The span this check vouches for, clipped to the window.
    let end = c.at + maxGapSeconds;
    if (i + 1 < checks.length && checks[i + 1].at < end) end = checks[i + 1].at;
    const s = Math.max(c.at, from);
    const e = Math.min(end, to);
    if (e > s) {
      if (c.status === "up") up += e - s;
      else if (c.status === "degraded") degraded += e - s;
      else down += e - s;
    }
  }
  const known = up + degraded + down;
  // Floored in BigInt: never 100.00% while a down second exists, and exact
  // for windows whose seconds times 10000 pass 2^53.
  const uptimeBasisPoints = known === 0 ? null : Number((BigInt(up + degraded) * 10000n) / BigInt(known));
  return {
    upSeconds: up,
    degradedSeconds: degraded,
    downSeconds: down,
    unknownSeconds: to - from - known,
    uptimeBasisPoints,
    checks: count,
    downChecks: downCount,
  };
}