import { formatMs } from "@/lib/format"; import { EmptyState } from "@/components/ui/empty-state"; import { Timer } from "lucide-react"; const PHASES: Array<{ key: string; label: string; description: string }> = [ { key: "dns_ms", label: "DNS", description: "Resolving the target hostname" }, { key: "proxy_connect_ms", label: "Network connect", description: "Establishing the route through the selected network class" }, { key: "tls_ms", label: "TLS", description: "TLS handshake with the origin" }, { key: "origin_ms", label: "Origin", description: "Waiting for the origin server to respond and stream the body" }, { key: "processing_ms", label: "Processing", description: "Format conversion, redaction and metering" }, ]; function read(timing: Record, key: string): number | null { const v = timing[key] ?? timing[key.replace(/_ms$/, "")]; return typeof v === "number" && Number.isFinite(v) ? v : null; } /** Horizontal waterfall of request phases from the stored `timing` JSON. Server component. */ export function TimingWaterfall({ timing, totalMs }: { timing: Record | null; totalMs: number | null }) { if (!timing || Object.keys(timing).length === 0) { return ; } const phases = PHASES.map((p) => ({ ...p, value: read(timing, p.key) })).filter((p) => p.value !== null) as Array<(typeof PHASES)[number] & { value: number }>; const total = read(timing, "total_ms") ?? totalMs ?? phases.reduce((a, p) => a + p.value, 0); const scale = Math.max(total, phases.reduce((a, p) => a + p.value, 0), 1); // Each phase starts where the previous one ended (cumulative offsets computed up front). const rows = phases.map((p, i) => ({ ...p, start: phases.slice(0, i).reduce((a, q) => a + q.value, 0) })); return (
    {rows.map((p) => { const left = (p.start / scale) * 100; const width = Math.max((p.value / scale) * 100, 0.5); return (
  1. {p.label}
    {p.description}
    {formatMs(p.value)}
  2. ); })}
Total {formatMs(total)}
); }