import {z} from "zod" const rate=z.number().finite().nonnegative() const observations=z.object({rxNow:z.boolean(),txNow:z.boolean(),rxSeries:z.array(z.boolean()),txSeries:z.array(z.boolean()),sampledAt:z.string().refine(v=>Number.isFinite(Date.parse(v))).nullable()}) const row=z.object({id:z.string().trim().min(1),rxNow:rate,txNow:rate,rxSeries:z.array(rate),txSeries:z.array(rate),observations:observations.optional()}).refine(r=>!r.observations||(r.observations.rxSeries.length===r.rxSeries.length&&r.observations.txSeries.length===r.txSeries.length&&(!(r.observations.rxNow||r.observations.txNow||r.observations.rxSeries.some(Boolean)||r.observations.txSeries.some(Boolean))||r.observations.sampledAt!==null))) const payload=z.object({servers:z.array(row)}).refine(data=>new Set(data.servers.map(r=>r.id)).size===data.servers.length&&data.servers.every(r=>r.rxSeries.length===r.txSeries.length&&r.rxSeries.length===data.servers[0].rxSeries.length)) export interface DashboardTraffic {rx:(number|null)[];tx:(number|null)[];rxNow:number|null;txNow:number|null;observationsKnown:boolean;sampledAt:string|null} /** Numeric legacy zero alone cannot confirm that the collector observed an interval. */ export function parseDashboardTraffic(raw:unknown):DashboardTraffic { const parsed=payload.safeParse(raw) if(!parsed.success)throw Error("Некорректные данные трафика: проверьте счётчики и согласованность серий") const rows=parsed.data.servers,length=rows[0]?.rxSeries.length??0 const sum=(key:"rxNow"|"txNow")=>rows.every(r=>r.observations?.[key])?rows.reduce((sum,r)=>sum+r[key],0):null const series=(key:"rxSeries"|"txSeries")=>Array.from({length},(_,i)=>rows.every(r=>r.observations?.[key][i])?rows.reduce((sum,r)=>sum+r[key][i],0):null) const timestamps=rows.map(r=>r.observations?.sampledAt).filter((v):v is string=>!!v) const total={rx:series("rxSeries"),tx:series("txSeries"),rxNow:sum("rxNow"),txNow:sum("txNow"),observationsKnown:rows.every(r=>!!r.observations),sampledAt:timestamps.length===rows.length&×tamps.length?new Date(timestamps.reduce((oldest,v)=>Math.min(oldest,Date.parse(v)),Infinity)).toISOString():null} if(![total.rxNow,total.txNow,...total.rx,...total.tx].every(v=>v===null||Number.isFinite(v)))throw Error("Суммарный трафик превышает допустимый диапазон") return total }