export const STORAGE_BUDGET_SCHEMA_VERSION = 1 as const; export interface StorageBudgetBreakdown { schemaVersion: typeof STORAGE_BUDGET_SCHEMA_VERSION; projectId: string; projectBytes: number; snapshotBytes: number; lodBytes: number; mediaBytes: number; vdbBytes: number; totalBytes: number; } export function createStorageBudget(projectId: string, values: Partial> = {}): StorageBudgetBreakdown { const fields = ["projectBytes", "snapshotBytes", "lodBytes", "mediaBytes", "vdbBytes"] as const; const normalized = Object.fromEntries(fields.map((field) => { const value = values[field] ?? 0; if (!Number.isSafeInteger(value) || value < 0) throw new Error(`STORAGE_BUDGET_INVALID: ${field}`); return [field, value]; })) as Pick; const totalBytes = fields.reduce((total, field) => { const next = total + normalized[field]; if (!Number.isSafeInteger(next)) throw new Error("STORAGE_BUDGET_INVALID: totalBytes"); return next; }, 0); return { schemaVersion: STORAGE_BUDGET_SCHEMA_VERSION, projectId, ...normalized, totalBytes }; } export function formatStorageBytes(bytes: number): string { if (!Number.isSafeInteger(bytes) || bytes < 0) throw new Error("STORAGE_BUDGET_INVALID: bytes"); if (bytes < 1024) return `${bytes} B`; const units = ["KiB", "MiB", "GiB", "TiB"]; let value = bytes; for (const unit of units) { value /= 1024; if (value < 1024 || unit === units[units.length - 1]) return `${value.toFixed(value >= 10 ? 0 : 1)} ${unit}`; } return `${bytes} B`; }