Skip to content
39 linesCodeBlameRaw
1/** How big an artifact is, as people read sizes. */
2export function formatBytes(bytes: number): string {
3 if (bytes < 1024 * 1024) return `${Math.max(1, Math.round(bytes / 1024))} KB`;
4 if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
5 return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
6}
7
8/** When an artifact goes, from now: "expires today", "expires in 13 days". */
9export function expiresIn(at: string | null, now: number = Date.now()): string {
10 if (!at) return "";
11 const ms = Date.parse(at) - now;
12 if (Number.isNaN(ms)) return "";
13 if (ms <= 0) return "expired";
14 const days = Math.floor(ms / 86_400_000);
15 if (days === 0) return "expires today";
16 if (days === 1) return "expires tomorrow";
17 return `expires in ${days} days`;
18}
19
20/**
21 * Artifacts older runners kept in Workers KV. None has been made there
22 * since artifacts moved to R2 on 2026-10-08, and KV expires each 14 days
23 * after it was made: from 2026-10-22T00:00Z every one is gone. Delete the
24 * KV artifact code (here, artifacts.server.ts, and apps/api/src/blobs.rs)
25 * after that date.
26 */
27export const LEGACY_KV_UNTIL = Date.parse("2026-10-22T00:00:00Z");
28/** Runs made from this time on kept their artifacts in R2 only. */
29export const LEGACY_KV_BEFORE = Date.parse("2026-10-09T00:00:00Z");
30
31/**
32 * Whether a run's page asks KV for artifacts an older runner kept there:
33 * only for a finished run made before the move, and only until they have
34 * all expired. A KV list is the dearest thing KV does, and a run still
35 * going refreshes its page every few seconds.
36 */
37export function legacyArtifactsWorthAsking(run: { createdAt: string; status: string }, now: number = Date.now()): boolean {
38 return now < LEGACY_KV_UNTIL && run.status === "completed" && Date.parse(run.createdAt) < LEGACY_KV_BEFORE;
39}