const THOUSAND = 1000; const MILLION = 1_000_000; const MINUTE_MS = 60_000; const HOUR_MS = 3_600_000; const DAY_MS = 86_400_000; const KB = 1024; const WHITESPACE = /\s+/g; export function fmtTokens(n: number | null | undefined): string { if (n === null || n === undefined) { return "–"; } if (n >= MILLION) { return `${(n / MILLION).toFixed(n >= 10 * MILLION ? 0 : 1)}M`; } if (n >= THOUSAND) { return `${(n / THOUSAND).toFixed(n >= 10 * THOUSAND ? 0 : 1)}k`; } return String(n); } export function fmtMoney(usd: number | null | undefined): string { if (usd === null || usd === undefined) { return "–"; } if (usd === 0) { return "$0"; } if (usd < 0.01) { return `$${usd.toFixed(4)}`; } if (usd < 1) { return `$${usd.toFixed(3)}`; } return `$${usd.toFixed(2)}`; } export function fmtPct(fraction: number | null | undefined): string { if (fraction === null || fraction === undefined) { return "–"; } return `${Math.round(fraction * 100)}%`; } export function fmtDuration(ms: number | null | undefined): string { if (ms === null || ms === undefined) { return "–"; } if (ms < THOUSAND) { return `${Math.round(ms)}ms`; } if (ms < MINUTE_MS) { return `${(ms / THOUSAND).toFixed(1)}s`; } if (ms < HOUR_MS) { const minutes = Math.floor(ms / MINUTE_MS); const seconds = Math.round((ms % MINUTE_MS) / THOUSAND); return `${minutes}m${String(seconds).padStart(2, "0")}s`; } const hours = Math.floor(ms / HOUR_MS); const minutes = Math.round((ms % HOUR_MS) / MINUTE_MS); return `${hours}h${String(minutes).padStart(2, "0")}m`; } export function fmtSeconds(seconds: number | null | undefined): string { return seconds === null || seconds === undefined ? "–" : fmtDuration(seconds * THOUSAND); } export function fmtBytes(bytes: number | null | undefined): string { if (bytes === null || bytes === undefined) { return "–"; } if (bytes >= KB * KB * KB) { return `${(bytes / (KB * KB * KB)).toFixed(2)} GB`; } if (bytes >= KB * KB) { return `${(bytes / (KB * KB)).toFixed(0)} MB`; } if (bytes >= KB) { return `${(bytes / KB).toFixed(0)} kB`; } return `${bytes} B`; } export function parseDate(iso: string | null | undefined): Date | null { if (!iso) { return null; } const date = new Date(iso); return Number.isNaN(date.getTime()) ? null : date; } export function fmtTime(iso: string | null | undefined): string { const date = parseDate(iso); return date ? date.toLocaleTimeString(undefined, { hour: "2-digit", minute: "2-digit", second: "2-digit", }) : "–"; } export function fmtDateTime(iso: string | null | undefined): string { const date = parseDate(iso); return date ? date.toLocaleString(undefined, { day: "numeric", hour: "2-digit", minute: "2-digit", month: "short", }) : "–"; } export function fmtRelative( iso: string | null | undefined, now = Date.now() ): string { const date = parseDate(iso); if (!date) { return "–"; } const diff = now - date.getTime(); const abs = Math.abs(diff); const suffix = diff >= 0 ? "ago" : "from now"; if (abs < MINUTE_MS) { return diff >= 0 ? "just now" : "in <1m"; } if (abs < HOUR_MS) { return `${Math.round(abs / MINUTE_MS)}m ${suffix}`; } if (abs < DAY_MS) { return `${Math.round(abs / HOUR_MS)}h ${suffix}`; } return `${Math.round(abs / DAY_MS)}d ${suffix}`; } export function fmtCountdown(iso: string | null | undefined, now = Date.now()) { const date = parseDate(iso); if (!date) { return "–"; } const left = date.getTime() - now; if (left <= 0) { return "resets now"; } if (left < HOUR_MS) { return `resets in ${Math.ceil(left / MINUTE_MS)}m`; } if (left < DAY_MS) { const hours = Math.floor(left / HOUR_MS); const minutes = Math.round((left % HOUR_MS) / MINUTE_MS); return `resets in ${hours}h ${String(minutes).padStart(2, "0")}m`; } const days = Math.floor(left / DAY_MS); const hours = Math.round((left % DAY_MS) / HOUR_MS); return `resets in ${days}d ${hours}h`; } export function elapsedMs(from: string, to: string | null, now = Date.now()) { const a = parseDate(from)?.getTime(); const b = to ? parseDate(to)?.getTime() : now; if (a === undefined || b === undefined) { return 0; } return Math.max(0, b - a); } export function clip(text: string, max: number): string { const one = text.replace(WHITESPACE, " ").trim(); return one.length > max ? `${one.slice(0, max - 1)}…` : one; } export function shortId(id: string | null | undefined): string { return id ? id.slice(0, 8) : "–"; } export function cacheShare(input: number, cacheRead: number): number | null { const total = input + cacheRead; return total > 0 ? cacheRead / total : null; }