cat / goq
clone

turns a linux box into a private steam streaming appliance: plug in, tap your key, play from another computer

fix/release-manifest-language
goq / portal / session-diagnostics.mjs
session-diagnostics.mjs 3.3K
1const MODES = new Set(['legacy_exclusive_v1', 'moq_multi_viewer_v2']);2const HANDLE_PATTERN = /^[A-Za-z0-9-]{1,32}$/;34function unsigned(value, label, { positive = false } = {}) {5 if (!Number.isSafeInteger(value) || value < (positive ? 1 : 0)) {6 throw new TypeError(`${label} must be an exact ${positive ? 'positive ' : ''}unsigned integer`);7 }8 return value;9}1011function optionalUnsigned(value, label, options) {12 return value === null ? null : unsigned(value, label, options);13}1415export function normalizeSessionDiagnostics(value) {16 if (!value || typeof value !== 'object' || Array.isArray(value)) {17 throw new TypeError('session diagnostics must be an object');18 }19 if (!MODES.has(value.mode)) throw new TypeError('session diagnostics mode is unsupported');20 const staleSnapshotTotal = unsigned(value.stale_snapshot_total, 'stale_snapshot_total');21 const invalidSnapshotTotal = unsigned(value.invalid_snapshot_total, 'invalid_snapshot_total');2223 if (value.mode === 'legacy_exclusive_v1') {24 for (const field of [25 'local_handle', 'focus_generation', 'roster_revision', 'roster_age_ms',26 'subscription_expires_at_unix', 'subscription_seconds_remaining',27 ]) {28 if (value[field] !== null) throw new TypeError(`legacy session diagnostics require null ${field}`);29 }30 return {31 mode: value.mode,32 label: 'legacy exclusive v1',33 localHandle: null,34 focusGeneration: null,35 rosterRevision: null,36 rosterAgeMs: null,37 subscriptionExpiresAtUnix: null,38 subscriptionSecondsRemaining: null,39 staleSnapshotTotal,40 invalidSnapshotTotal,41 };42 }4344 if (typeof value.local_handle !== 'string' || !HANDLE_PATTERN.test(value.local_handle)) {45 throw new TypeError('local_handle must be a bounded opaque viewer handle');46 }47 const rosterRevision = unsigned(value.roster_revision, 'roster_revision', { positive: true });48 const rosterAgeMs = unsigned(value.roster_age_ms, 'roster_age_ms');49 const subscriptionExpiresAtUnix = unsigned(50 value.subscription_expires_at_unix,51 'subscription_expires_at_unix',52 { positive: true },53 );54 const subscriptionSecondsRemaining = unsigned(55 value.subscription_seconds_remaining,56 'subscription_seconds_remaining',57 );58 return {59 mode: value.mode,60 label: 'native MoQ multi-viewer v2',61 localHandle: value.local_handle,62 focusGeneration: optionalUnsigned(value.focus_generation, 'focus_generation', { positive: true }),63 rosterRevision,64 rosterAgeMs,65 subscriptionExpiresAtUnix,66 subscriptionSecondsRemaining,67 staleSnapshotTotal,68 invalidSnapshotTotal,69 };70}7172export function sessionDiagnosticsPresentation(session) {73 if (session === null) return 'unavailable';74 if (session.mode === 'legacy_exclusive_v1') {75 return `${session.label} · implicit focus · no roster · stale/invalid ${session.staleSnapshotTotal}/${session.invalidSnapshotTotal}`;76 }77 const focus = session.focusGeneration === null ? 'spectator' : `focus generation ${session.focusGeneration}`;78 return `${session.label} · ${session.localHandle} · ${focus} · roster r${session.rosterRevision} age ${session.rosterAgeMs} ms · subscription ${session.subscriptionSecondsRemaining} s remaining · stale/invalid ${session.staleSnapshotTotal}/${session.invalidSnapshotTotal}`;79}