refactor: Clean lint errors and organise files

This commit is contained in:
2026-04-17 01:06:01 +02:00
parent 2927a86fbb
commit 35b616fb77
60 changed files with 1161 additions and 728 deletions

View File

@@ -1,4 +1,3 @@
/* eslint-disable complexity */
import {
SIGNALING_TYPE_ANSWER,
SIGNALING_TYPE_OFFER,
@@ -11,6 +10,7 @@ import {
PeerConnectionManagerContext,
PeerConnectionManagerState
} from '../shared';
import type { PeerData } from '../../realtime.types';
/**
* Queue a negotiation task so SDP operations for a single peer never overlap.
@@ -95,6 +95,97 @@ function replaceUnusablePeer(
state.peerNegotiationQueue.delete(peerId);
}
function getOrCreatePeerForOffer(
state: PeerConnectionManagerState,
fromUserId: string,
handlers: NegotiationHandlers
): PeerData {
return state.activePeerConnections.get(fromUserId) ?? handlers.createPeerConnection(fromUserId, false);
}
async function resolveOfferCollision(
peerData: PeerData,
callbacks: PeerConnectionManagerContext['callbacks'],
logger: PeerConnectionManagerContext['logger'],
fromUserId: string
): Promise<boolean> {
const signalingState = peerData.connection.signalingState;
const hasCollision = signalingState === 'have-local-offer' || signalingState === 'have-local-pranswer';
if (!hasCollision)
return true;
const localOderId = callbacks.getIdentifyCredentials()?.oderId ?? null;
const isPolite = !localOderId || localOderId > fromUserId;
if (!isPolite) {
logger.info('Ignoring colliding offer (impolite side)', { fromUserId, localOderId });
return false;
}
logger.info('Rolling back local offer (polite side)', { fromUserId, localOderId });
await peerData.connection.setLocalDescription({
type: 'rollback'
} as RTCSessionDescriptionInit);
return true;
}
function syncPeerSendersFromTransceivers(peerData: PeerData): void {
const transceivers = peerData.connection.getTransceivers();
for (const transceiver of transceivers) {
const receiverKind = transceiver.receiver.track?.kind;
if (receiverKind === TRACK_KIND_AUDIO) {
if (!peerData.audioSender) {
peerData.audioSender = transceiver.sender;
}
transceiver.direction = TRANSCEIVER_SEND_RECV;
continue;
}
if (receiverKind === TRACK_KIND_VIDEO && !peerData.videoSender) {
peerData.videoSender = transceiver.sender;
}
}
}
async function attachAnswererLocalTracks(
peerData: PeerData,
localStream: MediaStream | null,
logger: PeerConnectionManagerContext['logger'],
fromUserId: string
): Promise<void> {
if (!localStream)
return;
logger.logStream(`localStream->${fromUserId} (answerer)`, localStream);
for (const track of localStream.getTracks()) {
if (track.kind === TRACK_KIND_AUDIO && peerData.audioSender) {
await peerData.audioSender.replaceTrack(track);
logger.info('audio replaceTrack (answerer) ok', { fromUserId });
continue;
}
if (track.kind === TRACK_KIND_VIDEO && peerData.videoSender) {
await peerData.videoSender.replaceTrack(track);
logger.info('video replaceTrack (answerer) ok', { fromUserId });
}
}
}
async function applyPendingIceCandidates(peerData: PeerData): Promise<void> {
for (const candidate of peerData.pendingIceCandidates) {
await peerData.connection.addIceCandidate(new RTCIceCandidate(candidate));
}
peerData.pendingIceCandidates = [];
}
export async function doHandleOffer(
context: PeerConnectionManagerContext,
fromUserId: string,
@@ -107,72 +198,18 @@ export async function doHandleOffer(
replaceUnusablePeer(context, fromUserId, 'incoming offer');
let peerData = state.activePeerConnections.get(fromUserId);
if (!peerData) {
peerData = handlers.createPeerConnection(fromUserId, false);
}
const peerData = getOrCreatePeerForOffer(state, fromUserId, handlers);
try {
const signalingState = peerData.connection.signalingState;
const hasCollision =
signalingState === 'have-local-offer' || signalingState === 'have-local-pranswer';
const shouldApplyOffer = await resolveOfferCollision(peerData, callbacks, logger, fromUserId);
if (hasCollision) {
const localOderId = callbacks.getIdentifyCredentials()?.oderId ?? null;
const isPolite = !localOderId || localOderId > fromUserId;
if (!isPolite) {
logger.info('Ignoring colliding offer (impolite side)', { fromUserId, localOderId });
return;
}
logger.info('Rolling back local offer (polite side)', { fromUserId, localOderId });
await peerData.connection.setLocalDescription({
type: 'rollback'
} as RTCSessionDescriptionInit);
}
if (!shouldApplyOffer)
return;
await peerData.connection.setRemoteDescription(new RTCSessionDescription(sdp));
const transceivers = peerData.connection.getTransceivers();
for (const transceiver of transceivers) {
const receiverKind = transceiver.receiver.track?.kind;
if (receiverKind === TRACK_KIND_AUDIO) {
if (!peerData.audioSender) {
peerData.audioSender = transceiver.sender;
}
transceiver.direction = TRANSCEIVER_SEND_RECV;
} else if (receiverKind === TRACK_KIND_VIDEO && !peerData.videoSender) {
peerData.videoSender = transceiver.sender;
}
}
const localStream = callbacks.getLocalMediaStream();
if (localStream) {
logger.logStream(`localStream->${fromUserId} (answerer)`, localStream);
for (const track of localStream.getTracks()) {
if (track.kind === TRACK_KIND_AUDIO && peerData.audioSender) {
await peerData.audioSender.replaceTrack(track);
logger.info('audio replaceTrack (answerer) ok', { fromUserId });
} else if (track.kind === TRACK_KIND_VIDEO && peerData.videoSender) {
await peerData.videoSender.replaceTrack(track);
logger.info('video replaceTrack (answerer) ok', { fromUserId });
}
}
}
for (const candidate of peerData.pendingIceCandidates) {
await peerData.connection.addIceCandidate(new RTCIceCandidate(candidate));
}
peerData.pendingIceCandidates = [];
syncPeerSendersFromTransceivers(peerData);
await attachAnswererLocalTracks(peerData, callbacks.getLocalMediaStream(), logger, fromUserId);
await applyPendingIceCandidates(peerData);
const answer = await peerData.connection.createAnswer();