From 9d9aba2cf533ce26f0011b90bd4854c0efda1b19 Mon Sep 17 00:00:00 2001 From: Bas van den Aakster Date: Fri, 14 Nov 2025 19:38:41 +0100 Subject: [PATCH] Refactor: Send/receive ICE candidates as complete objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update to send full RTCIceCandidateInit objects instead of partial data - Simplify API by using JSON serialization - Include usernameFragment field - More maintainable and future-proof 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/connection.ts | 24 +++++++++--------------- src/offers.ts | 20 ++++++++++++-------- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/connection.ts b/src/connection.ts index f9012b3..bc1d0e8 100644 --- a/src/connection.ts +++ b/src/connection.ts @@ -1,4 +1,4 @@ -import { RondevuOffers } from './offers.js'; +import { RondevuOffers, RTCIceCandidateInit } from './offers.js'; /** * Events emitted by RondevuConnection @@ -60,11 +60,7 @@ export class RondevuConnection { private lastIceTimestamp: number = Date.now(); private eventListeners: Map> = new Map(); private dataChannel?: RTCDataChannel; - private pendingIceCandidates: Array<{ - candidate: string; - sdpMid: string | null; - sdpMLineIndex: number | null; - }> = []; + private pendingIceCandidates: RTCIceCandidateInit[] = []; /** * Current connection state @@ -107,10 +103,12 @@ export class RondevuConnection { private setupPeerConnection(): void { this.pc.onicecandidate = async (event) => { if (event.candidate) { - const candidateData = { + // Convert RTCIceCandidate to RTCIceCandidateInit (plain object) + const candidateData: RTCIceCandidateInit = { candidate: event.candidate.candidate, sdpMid: event.candidate.sdpMid, sdpMLineIndex: event.candidate.sdpMLineIndex, + usernameFragment: event.candidate.usernameFragment, }; if (this.offerId) { @@ -282,14 +280,10 @@ export class RondevuConnection { this.lastIceTimestamp ); - for (const candidate of candidates) { - // Create ICE candidate with all fields - await this.pc.addIceCandidate(new RTCIceCandidate({ - candidate: candidate.candidate, - sdpMid: candidate.sdpMid ?? undefined, - sdpMLineIndex: candidate.sdpMLineIndex ?? undefined, - })); - this.lastIceTimestamp = candidate.createdAt; + for (const cand of candidates) { + // Use the candidate object directly - it's already RTCIceCandidateInit + await this.pc.addIceCandidate(new RTCIceCandidate(cand.candidate)); + this.lastIceTimestamp = cand.createdAt; } } catch (err) { console.error('Error polling for ICE candidates:', err); diff --git a/src/offers.ts b/src/offers.ts index 52b65b8..921d862 100644 --- a/src/offers.ts +++ b/src/offers.ts @@ -24,10 +24,18 @@ export interface Offer { answeredAt?: number; } +/** + * RTCIceCandidateInit interface for environments without native WebRTC types + */ +export interface RTCIceCandidateInit { + candidate?: string; + sdpMid?: string | null; + sdpMLineIndex?: number | null; + usernameFragment?: string | null; +} + export interface IceCandidate { - candidate: string; - sdpMid: string | null; - sdpMLineIndex: number | null; + candidate: RTCIceCandidateInit; // Full candidate object peerId: string; role: 'offerer' | 'answerer'; createdAt: number; @@ -282,11 +290,7 @@ export class RondevuOffers { */ async addIceCandidates( offerId: string, - candidates: Array<{ - candidate: string; - sdpMid?: string | null; - sdpMLineIndex?: number | null; - }> + candidates: RTCIceCandidateInit[] ): Promise { const response = await this.fetchFn(`${this.baseUrl}/offers/${encodeURIComponent(offerId)}/ice-candidates`, { method: 'POST',