/
nayvok
/
livecoding
Обзор
Документация
Войти
/
nayvok
/
livecoding
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
frontend/src/lib/iceCandidateQueue.ts
85 строк
2 KB
nayvok
fix: harden sessions and WebRTC recovery
17 июн 2026, 22:02
17 июн 2026, 22:02
37b9a92
Код
Авторство
О чём код?
export type IceCandidateDisposition = 'ready' | 'pending' | 'stale' export interface IceCandidateQueue { add: ( candidate: RTCIceCandidateInit, disposition: IceCandidateDisposition, addCandidate: (candidate: RTCIceCandidateInit) => Promise<void>, ) => Promise<boolean> flush: ( getDisposition: ( candidate: RTCIceCandidateInit, ) => IceCandidateDisposition, addCandidate: (candidate: RTCIceCandidateInit) => Promise<void>, ) => Promise<unknown[]> clear: () => void } export function getIceCandidateDisposition( candidate: RTCIceCandidateInit, remoteDescription: RTCSessionDescription | null, signalingState: RTCSignalingState, ): IceCandidateDisposition { if (!remoteDescription) return 'pending' if (signalingState === 'closed') return 'stale' const candidateUfrag = candidate.usernameFragment if (candidateUfrag && remoteDescription.sdp) { const remoteUfrags = remoteDescription.sdp .split(/\r?\n/) .filter((line) => line.startsWith('a=ice-ufrag:')) .map((line) => line.slice('a=ice-ufrag:'.length)) if (remoteUfrags.length > 0) { const matchesCurrentDescription = remoteUfrags.includes(candidateUfrag) if (signalingState === 'have-local-offer') { return matchesCurrentDescription ? 'stale' : 'pending' } return matchesCurrentDescription ? 'ready' : 'stale' } } // ICE-restart, ждем новый answer. return signalingState === 'have-local-offer' ? 'pending' : 'ready' } export function createIceCandidateQueue(): IceCandidateQueue { const pending: RTCIceCandidateInit[] = [] return { async add(candidate, disposition, addCandidate) { if (disposition === 'stale') { return false } if (disposition === 'pending') { if (pending.length >= 256) { pending.shift() } pending.push(candidate) return false } await addCandidate(candidate) return true }, async flush(getDisposition, addCandidate) { const candidates = pending.splice(0) const errors: unknown[] = [] for (const candidate of candidates) { const disposition = getDisposition(candidate) if (disposition === 'pending') { pending.push(candidate) continue } if (disposition === 'stale') continue try { await addCandidate(candidate) } catch (err) { errors.push(err) } } return errors }, clear() { pending.length = 0 }, } }