From 800f6eaa9469aa332dde8fec19f96c605c77e175 Mon Sep 17 00:00:00 2001 From: Bas van den Aakster Date: Fri, 12 Dec 2025 23:07:49 +0100 Subject: [PATCH] Refactor connectToService() method for better maintainability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Break down 129-line method into focused helper methods: New private methods: - resolveServiceFqn(): Determines full FQN from various input options Handles direct FQN, service+username, or discovery mode - startIcePolling(): Manages remote ICE candidate polling Encapsulates polling logic and interval management Benefits: - connectToService() reduced from 129 to ~98 lines - Each method has single responsibility - Easier to test and maintain - Better code readability with clear method names - Reusable components for future features 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- src/rondevu.ts | 93 +++++++++++++++++++++++++++++++------------------- 1 file changed, 57 insertions(+), 36 deletions(-) diff --git a/src/rondevu.ts b/src/rondevu.ts index 606b7c4..8a00043 100644 --- a/src/rondevu.ts +++ b/src/rondevu.ts @@ -551,6 +551,57 @@ export class Rondevu { this.activeOffers.clear() } + /** + * Resolve the full service FQN from various input options + * Supports direct FQN, service+username, or service discovery + */ + private async resolveServiceFqn(options: ConnectToServiceOptions): Promise { + const { serviceFqn, service, username } = options + + if (serviceFqn) { + return serviceFqn + } else if (service && username) { + return `${service}@${username}` + } else if (service) { + // Discovery mode - get random service + this.debug(`Discovering service: ${service}`) + const discovered = await this.discoverService(service) + return discovered.serviceFqn + } else { + throw new Error('Either serviceFqn or service must be provided') + } + } + + /** + * Start polling for remote ICE candidates + * Returns the polling interval ID + */ + private startIcePolling( + pc: RTCPeerConnection, + serviceFqn: string, + offerId: string + ): ReturnType { + let lastIceTimestamp = 0 + + return setInterval(async () => { + try { + const result = await this.api.getOfferIceCandidates( + serviceFqn, + offerId, + lastIceTimestamp + ) + for (const item of result.candidates) { + if (item.candidate) { + await pc.addIceCandidate(new RTCIceCandidate(item.candidate)) + lastIceTimestamp = item.createdAt + } + } + } catch (err) { + console.error('[Rondevu] Failed to poll ICE candidates:', err) + } + }, Rondevu.POLLING_INTERVAL_MS) + } + /** * Automatically connect to a service (answerer side) * Handles the entire connection flow: discovery, WebRTC setup, answer exchange, ICE candidates @@ -577,34 +628,21 @@ export class Rondevu { * ``` */ async connectToService(options: ConnectToServiceOptions): Promise { - const { serviceFqn, service, username, onConnection, rtcConfig } = options + const { onConnection, rtcConfig } = options // Validate inputs - if (serviceFqn !== undefined && typeof serviceFqn === 'string' && !serviceFqn.trim()) { + if (options.serviceFqn !== undefined && typeof options.serviceFqn === 'string' && !options.serviceFqn.trim()) { throw new Error('serviceFqn cannot be empty') } - if (service !== undefined && typeof service === 'string' && !service.trim()) { + if (options.service !== undefined && typeof options.service === 'string' && !options.service.trim()) { throw new Error('service cannot be empty') } - if (username !== undefined && typeof username === 'string' && !username.trim()) { + if (options.username !== undefined && typeof options.username === 'string' && !options.username.trim()) { throw new Error('username cannot be empty') } // Determine the full service FQN - let fqn: string - if (serviceFqn) { - fqn = serviceFqn - } else if (service && username) { - fqn = `${service}@${username}` - } else if (service) { - // Discovery mode - get random service - this.debug(`Discovering service: ${service}`) - const discovered = await this.discoverService(service) - fqn = discovered.serviceFqn - } else { - throw new Error('Either serviceFqn or service must be provided') - } - + const fqn = await this.resolveServiceFqn(options) this.debug(`Connecting to service: ${fqn}`) // 1. Get service offer @@ -631,24 +669,7 @@ export class Rondevu { this.setupIceCandidateHandler(pc, serviceData.serviceFqn, serviceData.offerId) // 5. Poll for remote ICE candidates - let lastIceTimestamp = 0 - const icePollInterval = setInterval(async () => { - try { - const result = await this.api.getOfferIceCandidates( - serviceData.serviceFqn, - serviceData.offerId, - lastIceTimestamp - ) - for (const item of result.candidates) { - if (item.candidate) { - await pc.addIceCandidate(new RTCIceCandidate(item.candidate)) - lastIceTimestamp = item.createdAt - } - } - } catch (err) { - console.error('[Rondevu] Failed to poll ICE candidates:', err) - } - }, Rondevu.POLLING_INTERVAL_MS) + const icePollInterval = this.startIcePolling(pc, serviceData.serviceFqn, serviceData.offerId) // 6. Set remote description await pc.setRemoteDescription({