-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathdata-transforms.ts
More file actions
90 lines (75 loc) · 2.67 KB
/
data-transforms.ts
File metadata and controls
90 lines (75 loc) · 2.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
// src/lib/data-transforms.ts
// Functions: computeDelta, compareBigIntDesc, computePctChange,
// floatBytesToBigInt, computeBufferBytes, isUnixTimestampOnDate,
// sanitizeHost, normalizeUrl
import { localDateStr } from "@/lib/formatters"
import type { Snapshot } from "@/types/api"
/**
* Computes the 24-hour upload/download delta from a snapshot array.
* Sorts snapshots ascending by polledAt before processing, so the result
* is correct regardless of the order snapshots arrive in.
* Returns null if fewer than 2 snapshots exist or no snapshot falls within
* the 24-hour window.
*/
export function computeDelta(snaps: Snapshot[]): { uploaded: string; downloaded: string } | null {
if (snaps.length < 2) return null
const sorted = [...snaps].sort(
(a, b) => new Date(a.polledAt).getTime() - new Date(b.polledAt).getTime()
)
const latest = sorted[sorted.length - 1]
const cutoff = Date.now() - 24 * 60 * 60 * 1000
let earliest: Snapshot | null = null
for (const s of sorted) {
if (new Date(s.polledAt).getTime() >= cutoff) {
earliest = s
break
}
}
if (!earliest || earliest === latest) return null
if (
!latest.uploadedBytes ||
!earliest.uploadedBytes ||
!latest.downloadedBytes ||
!earliest.downloadedBytes
)
return null
try {
const uploadDelta = BigInt(latest.uploadedBytes) - BigInt(earliest.uploadedBytes)
const downloadDelta = BigInt(latest.downloadedBytes) - BigInt(earliest.downloadedBytes)
return { uploaded: uploadDelta.toString(), downloaded: downloadDelta.toString() }
} catch {
return null
}
}
export function compareBigIntDesc(a: bigint, b: bigint): number {
if (b > a) return 1
if (b < a) return -1
return 0
}
export function computePctChange(today: string, yesterday: string | null): number | null {
if (yesterday === null) return null
try {
const y = Number(BigInt(yesterday))
if (y === 0) return null
const t = Number(BigInt(today))
return ((t - y) / y) * 100
} catch {
return null
}
}
export function floatBytesToBigInt(n: number | null | undefined): bigint {
return BigInt(Math.max(0, Math.floor(n ?? 0)))
}
export function computeBufferBytes(uploaded: bigint, downloaded: bigint): bigint {
return uploaded > downloaded ? uploaded - downloaded : 0n
}
export function isUnixTimestampOnDate(unixSeconds: number, dateStr: string): boolean {
if (unixSeconds <= 0) return false
return localDateStr(new Date(unixSeconds * 1000)) === dateStr
}
export function sanitizeHost(host: string): string {
return host.trim().replace(/^https?:\/\//, "")
}
export function normalizeUrl(url: string): string {
return url.replace(/\/+$/, "").toLowerCase()
}