export type LiveTrafficSourceState = | 'connecting' | 'live' | 'degraded' | 'stale' | 'stopped' | 'incompatible' | 'disabled'; export interface LiveTrafficConnection { id: string; startedAt: string; closedAt: string | null; inbound: { tag: string; type: string }; network: 'tcp' | 'udp' | 'unknown'; protocol: string | null; source: { ip: string; port: number | null }; destination: { domain: string | null; ip: string | null; port: number | null; provenance: 'sing-box' | 'unknown'; }; origin: { kind: 'this-mac' | 'device' | 'unknown'; id: string | null; label: string; provenance: 'client-runtime' | 'source-ip' | 'unknown'; }; route: { kind: 'vpn' | 'direct' | 'other'; scope: 'local-sing-box'; outbound: string | null; outboundType: string | null; chain: string[]; rule: string | null; }; traffic: { uploadBytes: string; downloadBytes: string; uploadBytesPerSecond: string; downloadBytesPerSecond: string; }; } export interface LiveTrafficSnapshot { apiVersion: 1; epoch: string | null; sequence: number; observedAt: string | null; capabilities: { lifecycle: true; deviceAttribution: boolean; applicationAttribution: false; }; source: { transport: 'native'; state: LiveTrafficSourceState; completeness: 'lifecycle'; singBoxVersion: string | null; singBoxApiVersion: number | null; error: string | null; unattributedUploadBytes: string; unattributedDownloadBytes: string; }; summary: { active: number; recent: number; visible: number; recognized: number; unresolved: number; unresolvedOrigin: number; truncated: boolean; }; connections: LiveTrafficConnection[]; } const sourceStates = new Set([ 'connecting', 'live', 'degraded', 'stale', 'stopped', 'incompatible', 'disabled', ]); const decimal = /^\d+$/; function isoTimestamp(value: unknown) { return typeof value === 'string' && !Number.isNaN(Date.parse(value)) && new Date(value).toISOString() === value; } function decimalString(value: unknown) { return typeof value === 'string' && decimal.test(value); } function record(value: unknown): Record { if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error('Expected object'); return value as Record; } function nullableString(value: unknown) { if (value !== null && typeof value !== 'string') throw new Error('Expected nullable string'); } function nonNegativeInteger(value: unknown) { if (!Number.isSafeInteger(value) || Number(value) < 0) throw new Error('Expected non-negative integer'); } function nullablePort(value: unknown) { if (value !== null && (!Number.isInteger(value) || Number(value) < 0 || Number(value) > 65_535)) { throw new Error('Expected nullable port'); } } export function assertLiveTrafficSnapshot(value: unknown): LiveTrafficSnapshot { const snapshot = record(value); if (snapshot.apiVersion !== 1) throw new Error('Expected live traffic apiVersion 1'); nullableString(snapshot.epoch); nullableString(snapshot.observedAt); nonNegativeInteger(snapshot.sequence); const capabilities = record(snapshot.capabilities); if (capabilities.lifecycle !== true || typeof capabilities.deviceAttribution !== 'boolean' || capabilities.applicationAttribution !== false) throw new Error('Invalid traffic capabilities'); const source = record(snapshot.source); if (source.transport !== 'native' || source.completeness !== 'lifecycle' || !sourceStates.has(source.state as LiveTrafficSourceState)) throw new Error('Invalid traffic source'); nullableString(source.singBoxVersion); nullableString(source.error); if (source.singBoxApiVersion !== null) nonNegativeInteger(source.singBoxApiVersion); if (!decimalString(source.unattributedUploadBytes) || !decimalString(source.unattributedDownloadBytes)) throw new Error('Invalid traffic gap'); const summary = record(snapshot.summary); for (const field of ['active', 'recent', 'visible', 'recognized', 'unresolved', 'unresolvedOrigin']) { nonNegativeInteger(summary[field]); } if (typeof summary.truncated !== 'boolean') throw new Error('Invalid traffic summary'); if (!Array.isArray(snapshot.connections) || snapshot.connections.length > 256) { throw new Error('Invalid traffic connection list'); } const activeTotal = Number(summary.active); const recentTotal = Number(summary.recent); const visibleTotal = Number(summary.visible); const expectedVisible = Math.min(256, activeTotal + recentTotal); if (visibleTotal !== snapshot.connections.length || visibleTotal !== expectedVisible || Number(summary.recognized) + Number(summary.unresolved) !== Number(summary.active) || Number(summary.unresolvedOrigin) > Number(summary.active) || summary.truncated !== (activeTotal + recentTotal > visibleTotal)) { throw new Error('Inconsistent traffic summary'); } const ids = new Set(); let visibleActive = 0; let visibleRecent = 0; let recentSeen = false; for (const rawConnection of snapshot.connections) { const connection = record(rawConnection); if (typeof connection.id !== 'string' || !connection.id || ids.has(connection.id) || !isoTimestamp(connection.startedAt) || (connection.closedAt !== null && !isoTimestamp(connection.closedAt))) { throw new Error('Invalid traffic connection identity'); } ids.add(connection.id); if (connection.closedAt === null) { if (recentSeen) throw new Error('Inconsistent traffic summary'); visibleActive += 1; } else { recentSeen = true; visibleRecent += 1; } const inbound = record(connection.inbound); const sourceAddress = record(connection.source); const destination = record(connection.destination); const origin = record(connection.origin); const route = record(connection.route); const traffic = record(connection.traffic); if (typeof inbound.tag !== 'string' || typeof inbound.type !== 'string' || !['tcp', 'udp', 'unknown'].includes(String(connection.network)) || (connection.protocol !== null && typeof connection.protocol !== 'string') || typeof sourceAddress.ip !== 'string' || (destination.domain !== null && typeof destination.domain !== 'string') || (destination.ip !== null && typeof destination.ip !== 'string') || !['sing-box', 'unknown'].includes(String(destination.provenance)) || !['this-mac', 'device', 'unknown'].includes(String(origin.kind)) || (origin.id !== null && typeof origin.id !== 'string') || typeof origin.label !== 'string' || !['client-runtime', 'source-ip', 'unknown'].includes(String(origin.provenance)) || !['vpn', 'direct', 'other'].includes(String(route.kind)) || route.scope !== 'local-sing-box' || (route.outbound !== null && typeof route.outbound !== 'string') || (route.outboundType !== null && typeof route.outboundType !== 'string') || (route.rule !== null && typeof route.rule !== 'string') || !Array.isArray(route.chain) || !route.chain.every((item) => typeof item === 'string')) { throw new Error('Invalid traffic connection'); } nullablePort(sourceAddress.port); nullablePort(destination.port); for (const field of ['uploadBytes', 'downloadBytes', 'uploadBytesPerSecond', 'downloadBytesPerSecond']) { if (!decimalString(traffic[field])) throw new Error('Invalid traffic byte value'); } if (connection.closedAt !== null && (traffic.uploadBytesPerSecond !== '0' || traffic.downloadBytesPerSecond !== '0')) { throw new Error('Invalid closed traffic rate'); } } if (visibleActive !== Math.min(activeTotal, visibleTotal) || visibleRecent !== visibleTotal - visibleActive || visibleRecent > recentTotal) { throw new Error('Inconsistent traffic summary'); } return value as LiveTrafficSnapshot; }