export function formatNumber(n: number | null | undefined, opts: Intl.NumberFormatOptions = {}): string { if (n === null || n === undefined || Number.isNaN(n)) return "—"; return new Intl.NumberFormat("en-US", { maximumFractionDigits: 0, ...opts }).format(n); } export function formatCompact(n: number | null | undefined): string { if (n === null || n === undefined) return "—"; return new Intl.NumberFormat("en-US", { notation: "compact", maximumFractionDigits: 1 }).format(n); } export function formatBytes(bytes: number | null | undefined): string { if (bytes === null || bytes === undefined) return "—"; if (bytes < 1024) return `${bytes} B`; const units = ["KB", "MB", "GB", "TB"]; let v = bytes / 1024; let i = 0; while (v >= 1024 && i < units.length - 1) { v /= 1024; i++; } return `${v.toFixed(v >= 100 ? 0 : v >= 10 ? 1 : 2)} ${units[i]}`; } export function formatMs(ms: number | null | undefined): string { if (ms === null || ms === undefined) return "—"; if (ms < 1000) return `${Math.round(ms)} ms`; return `${(ms / 1000).toFixed(2)} s`; } export function formatUsd(n: number | null | undefined, precise = false): string { if (n === null || n === undefined) return "—"; if (n === 0) return "$0.00"; if (precise || Math.abs(n) < 0.01) return `$${n.toFixed(n < 0.001 ? 6 : 4)}`; return new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(n); } export function formatPercent(n: number | null | undefined, digits = 1): string { if (n === null || n === undefined || Number.isNaN(n)) return "—"; return `${n.toFixed(digits)}%`; } export function formatDate(d: Date | string | null | undefined, opts: Intl.DateTimeFormatOptions = {}): string { if (!d) return "—"; const date = typeof d === "string" ? new Date(d) : d; return new Intl.DateTimeFormat("en-US", { dateStyle: "medium", timeStyle: "short", ...opts }).format(date); } export function formatDateOnly(d: Date | string | null | undefined): string { if (!d) return "—"; const date = typeof d === "string" ? new Date(d) : d; return new Intl.DateTimeFormat("en-US", { dateStyle: "medium" }).format(date); } export function timeAgo(d: Date | string | null | undefined): string { if (!d) return "never"; const date = typeof d === "string" ? new Date(d) : d; const diff = Date.now() - date.getTime(); const s = Math.round(diff / 1000); if (s < 5) return "just now"; if (s < 60) return `${s}s ago`; const m = Math.round(s / 60); if (m < 60) return `${m}m ago`; const h = Math.round(m / 60); if (h < 24) return `${h}h ago`; const days = Math.round(h / 24); if (days < 30) return `${days}d ago`; return formatDateOnly(date); } export function truncate(s: string, n = 48): string { return s.length > n ? `${s.slice(0, n - 1)}…` : s; } export function titleCase(s: string): string { return s.replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()); }