SPB Git forge

spb/fetcha

Public
11commits 1branches 0releases
1.5 MBsize
maindefault branch
16 days agolast push
TypeScript 97.5% SQL 1.4% Python 0.8%
2.9 KB · 77 lines typescript
Raw Blame History
1export function formatNumber(n: number | null | undefined, opts: Intl.NumberFormatOptions = {}): string {2  if (n === null || n === undefined || Number.isNaN(n)) return "—";3  return new Intl.NumberFormat("en-US", { maximumFractionDigits: 0, ...opts }).format(n);4}56export function formatCompact(n: number | null | undefined): string {7  if (n === null || n === undefined) return "—";8  return new Intl.NumberFormat("en-US", { notation: "compact", maximumFractionDigits: 1 }).format(n);9}1011export function formatBytes(bytes: number | null | undefined): string {12  if (bytes === null || bytes === undefined) return "—";13  if (bytes < 1024) return `${bytes} B`;14  const units = ["KB", "MB", "GB", "TB"];15  let v = bytes / 1024;16  let i = 0;17  while (v >= 1024 && i < units.length - 1) {18    v /= 1024;19    i++;20  }21  return `${v.toFixed(v >= 100 ? 0 : v >= 10 ? 1 : 2)} ${units[i]}`;22}2324export function formatMs(ms: number | null | undefined): string {25  if (ms === null || ms === undefined) return "—";26  if (ms < 1000) return `${Math.round(ms)} ms`;27  return `${(ms / 1000).toFixed(2)} s`;28}2930export function formatUsd(n: number | null | undefined, precise = false): string {31  if (n === null || n === undefined) return "—";32  if (n === 0) return "$0.00";33  if (precise || Math.abs(n) < 0.01) return `$${n.toFixed(n < 0.001 ? 6 : 4)}`;34  return new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(n);35}3637export function formatPercent(n: number | null | undefined, digits = 1): string {38  if (n === null || n === undefined || Number.isNaN(n)) return "—";39  return `${n.toFixed(digits)}%`;40}4142export function formatDate(d: Date | string | null | undefined, opts: Intl.DateTimeFormatOptions = {}): string {43  if (!d) return "—";44  const date = typeof d === "string" ? new Date(d) : d;45  return new Intl.DateTimeFormat("en-US", { dateStyle: "medium", timeStyle: "short", ...opts }).format(date);46}4748export function formatDateOnly(d: Date | string | null | undefined): string {49  if (!d) return "—";50  const date = typeof d === "string" ? new Date(d) : d;51  return new Intl.DateTimeFormat("en-US", { dateStyle: "medium" }).format(date);52}5354export function timeAgo(d: Date | string | null | undefined): string {55  if (!d) return "never";56  const date = typeof d === "string" ? new Date(d) : d;57  const diff = Date.now() - date.getTime();58  const s = Math.round(diff / 1000);59  if (s < 5) return "just now";60  if (s < 60) return `${s}s ago`;61  const m = Math.round(s / 60);62  if (m < 60) return `${m}m ago`;63  const h = Math.round(m / 60);64  if (h < 24) return `${h}h ago`;65  const days = Math.round(h / 24);66  if (days < 30) return `${days}d ago`;67  return formatDateOnly(date);68}6970export function truncate(s: string, n = 48): string {71  return s.length > n ? `${s.slice(0, n - 1)}…` : s;72}7374export function titleCase(s: string): string {75  return s.replace(/[_-]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase());76}77