/** * Formatting helpers. API timestamps are UTC ISO strings; dates render in UTC. All helpers accept `Num` * (Postgres aggregates can arrive as strings) and return the em-dash for missing values — never a fake number. */ import type { Num } from './types'; const nf0 = new Intl.NumberFormat('en-US', { maximumFractionDigits: 0 }); const nf1 = new Intl.NumberFormat('en-US', { maximumFractionDigits: 1 }); const nf2 = new Intl.NumberFormat('en-US', { maximumFractionDigits: 2 }); const nfCompact = new Intl.NumberFormat('en-US', { notation: 'compact', maximumFractionDigits: 1 }); export const DASH = '—'; /** Coerce `Num`/unknown to a finite number or null. */ export function num(v: unknown): number | null { if (v === null || v === undefined || v === '') return null; const n = typeof v === 'number' ? v : Number(v); return Number.isFinite(n) ? n : null; } export function fmtInt(v: Num | undefined | unknown): string { const n = num(v); return n === null ? DASH : nf0.format(n); } export function fmt1(v: Num | undefined | unknown): string { const n = num(v); return n === null ? DASH : nf1.format(n); } export function fmt2(v: Num | undefined | unknown): string { const n = num(v); return n === null ? DASH : nf2.format(n); } export function fmtCompact(v: Num | undefined | unknown): string { const n = num(v); return n === null ? DASH : nfCompact.format(n); } /** Parameter counts: 7e9 → "7B", 1.8e12 → "1.8T", 350e6 → "350M". */ export function fmtParams(v: Num | undefined | unknown): string { const n = num(v); if (n === null) return DASH; if (n >= 1e12) return `${trim(n / 1e12)}T`; if (n >= 1e9) return `${trim(n / 1e9)}B`; if (n >= 1e6) return `${trim(n / 1e6)}M`; if (n >= 1e3) return `${trim(n / 1e3)}K`; return nf0.format(n); } /** Token windows: 128000 → "128K", 1000000 → "1M", 200000 → "200K". */ export function fmtTokens(v: Num | undefined | unknown): string { const n = num(v); if (n === null) return DASH; if (n >= 1e6) return `${trim(n / 1e6)}M`; if (n >= 1e3) return `${trim(n / 1e3)}K`; return nf0.format(n); } function trim(x: number): string { const r = Math.round(x * 100) / 100; return r % 1 === 0 ? String(r) : String(Number(r.toFixed(r < 10 ? 2 : 1))); } /** USD per 1M tokens: 3 → "$3.00", 0.075 → "$0.075", 15 → "$15.00". */ export function fmtUsdPerM(v: Num | undefined | unknown, withUnit = false): string { const n = num(v); if (n === null) return DASH; let s: string; if (n === 0) s = '$0'; else if (n < 0.01) s = `$${n.toFixed(4).replace(/0+$/, '')}`; else if (n < 1) s = `$${n.toFixed(3).replace(/0$/, '')}`; else s = `$${nf2.format(n)}`; return withUnit ? `${s} / 1M` : s; } export function fmtUsd(v: Num | undefined | unknown): string { const n = num(v); return n === null ? DASH : `$${nf0.format(n)}`; } export function fmtPct(v: Num | undefined | unknown, digits = 1): string { const n = num(v); return n === null ? DASH : `${n.toFixed(digits)}%`; } export function fmtGb(v: Num | undefined | unknown, digits = 0): string { const n = num(v); return n === null ? DASH : `${digits ? n.toFixed(digits) : nf0.format(n)} GB`; } export function fmtBytes(v: Num | undefined | unknown): string { const n = num(v); if (n === null) return DASH; if (n >= 1e12) return `${(n / 1e12).toFixed(2)} TB`; if (n >= 1e9) return `${(n / 1e9).toFixed(1)} GB`; if (n >= 1e6) return `${(n / 1e6).toFixed(0)} MB`; if (n >= 1e3) return `${(n / 1e3).toFixed(0)} kB`; return `${n} B`; } export function fmtScore(v: Num | undefined | unknown): string { const n = num(v); if (n === null) return DASH; return Number.isInteger(n) ? nf0.format(n) : nf2.format(n); } /** ISO date (YYYY-MM-DD, YYYY-MM or full timestamp) → "11 Sept 2026" / "Sept 2026" / "2026" (UTC). */ export function fmtDate(v: string | null | undefined): string { if (!v) return DASH; if (/^\d{4}$/.test(v)) return v; if (/^\d{4}-\d{2}$/.test(v)) return fmtMonth(v); const d = new Date(v.length === 10 ? `${v}T00:00:00Z` : v); if (Number.isNaN(d.getTime())) return v; return d.toLocaleDateString('en-GB', { year: 'numeric', month: 'short', day: 'numeric', timeZone: 'UTC' }); } export function fmtMonth(v: string | null | undefined): string { if (!v) return DASH; const m = /^(\d{4})-(\d{2})/.exec(v); if (!m) return fmtDate(v); const d = new Date(Date.UTC(Number(m[1]), Number(m[2]) - 1, 1)); return d.toLocaleDateString('en-GB', { year: 'numeric', month: 'short', timeZone: 'UTC' }); } export function fmtDateTime(v: string | null | undefined): string { if (!v) return DASH; const d = new Date(v); if (Number.isNaN(d.getTime())) return DASH; return `${d.toLocaleDateString('en-GB', { year: 'numeric', month: 'short', day: 'numeric', timeZone: 'UTC' })} ${d.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', timeZone: 'UTC' })} UTC`; } export function fmtAgo(v: string | null | undefined, now: number = Date.now()): string { if (!v) return 'unavailable'; const t = new Date(v).getTime(); if (Number.isNaN(t)) return 'unavailable'; const s = Math.max(0, Math.round((now - t) / 1000)); if (s < 60) return 'just now'; const m = Math.round(s / 60); if (m < 60) return `${m} min ago`; const h = Math.round(m / 60); if (h < 48) return `${h} h ago`; const d = Math.round(h / 24); if (d < 60) return `${d} d ago`; return fmtDate(v); } export function fmtYear(v: string | null | undefined): string { return v && /^\d{4}/.test(v) ? v.slice(0, 4) : DASH; } export function fmtDuration(seconds: Num | undefined | unknown): string { const n = num(seconds); if (n === null) return DASH; if (n < 60) return `${n}s`; if (n < 3600) return `${Math.round(n / 60)} min`; if (n < 86400) return `${Math.round(n / 3600)} h`; return `${Math.round(n / 86400)} d`; } /** Signed delta: +3.2% / −1.8% (relative change between two values). Null when either side is missing or the base is 0. */ export function fmtDeltaPct(from: Num | undefined | unknown, to: Num | undefined | unknown, digits = 1): string | null { const a = num(from); const b = num(to); if (a === null || b === null || a === 0) return null; const d = ((b - a) / Math.abs(a)) * 100; const sign = d > 0 ? '+' : d < 0 ? '−' : ''; return `${sign}${Math.abs(d).toFixed(digits)}%`; } /** Signed integer: +12 / −3 / 0. */ export function fmtSigned(v: Num | undefined | unknown): string { const n = num(v); if (n === null) return DASH; return n > 0 ? `+${nf0.format(n)}` : n < 0 ? `−${nf0.format(Math.abs(n))}` : '0'; } /** Host of a URL without `www.` — for compact source labels. */ export function hostOf(url: string | null | undefined): string | null { if (!url) return null; try { return new URL(url).hostname.replace(/^www\./, ''); } catch { return null; } } export function titleCase(s: string | null | undefined): string { if (!s) return DASH; return s.replace(/[_-]+/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()); } /** Event type / predicate constant → words: NEW_MODEL → "New model", fine_tuned_from → "fine-tuned from". */ export function humanize(s: string | null | undefined): string { if (!s) return DASH; const w = s.toLowerCase().replace(/_/g, ' '); return w.charAt(0).toUpperCase() + w.slice(1); } export function plural(n: number, one: string, many = `${one}s`): string { return n === 1 ? one : many; } /** Render any attribute value for a spec table (never JSON-dumps a primitive). */ export function fmtValue(v: unknown, key?: string): string { if (v === null || v === undefined || v === '') return DASH; if (typeof v === 'boolean') return v ? 'Yes' : 'No'; if (Array.isArray(v)) return v.length ? v.map((x) => (typeof x === 'object' && x ? JSON.stringify(x) : String(x))).join(', ') : DASH; if (typeof v === 'number') { if (key && /parameter_count/.test(key)) return fmtParams(v); if (key && /(context_length|max_output_tokens)/.test(key)) return `${fmtTokens(v)} tokens`; if (key && /memory_gb|file_size_gb/.test(key)) return fmtGb(v, v % 1 ? 1 : 0); if (key && /bandwidth_gbs/.test(key)) return `${fmtInt(v)} GB/s`; if (key && /tdp_watts/.test(key)) return `${fmtInt(v)} W`; if (key && /tflops/.test(key)) return `${fmt1(v)} TFLOPS`; if (key && /price_usd/.test(key)) return fmtUsd(v); return Number.isInteger(v) ? nf0.format(v) : nf2.format(v); } if (typeof v === 'string') { if (key && /(_date|_at|cutoff)$/.test(key) && /^\d{4}(-\d{2})?(-\d{2})?/.test(v)) return fmtDate(v); return v; } return JSON.stringify(v); }