mirror of
https://github.com/xtr-dev/rondevu-client.git
synced 2025-12-10 10:53:24 +00:00
Refactor peer connection state machine into separate files
Split the monolithic peer.ts file into a modular state-based architecture: - Created separate files for each state class (idle, creating-offer, waiting-for-answer, answering, exchanging-ice, connected, failed, closed) - Extracted shared types into types.ts - Extracted base PeerState class into state.ts - Updated peer/index.ts to import state classes instead of defining them inline - Made close() method async to support dynamic imports and avoid circular dependencies - Used dynamic imports in state transitions to prevent circular dependency issues This improves code organization, maintainability, and makes each state's logic easier to understand and test. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
74
src/peer/exchanging-ice-state.ts
Normal file
74
src/peer/exchanging-ice-state.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { PeerState } from './state.js';
|
||||
import type { PeerOptions } from './types.js';
|
||||
import type RondevuPeer from './index.js';
|
||||
|
||||
/**
|
||||
* Exchanging ICE candidates and waiting for connection
|
||||
*/
|
||||
export class ExchangingIceState extends PeerState {
|
||||
private pollingInterval?: ReturnType<typeof setInterval>;
|
||||
private timeout?: ReturnType<typeof setTimeout>;
|
||||
private lastIceTimestamp = 0;
|
||||
|
||||
constructor(
|
||||
peer: RondevuPeer,
|
||||
private offerId: string,
|
||||
private options: PeerOptions
|
||||
) {
|
||||
super(peer);
|
||||
this.startPolling();
|
||||
}
|
||||
|
||||
get name() { return 'exchanging-ice'; }
|
||||
|
||||
private startPolling(): void {
|
||||
const connectionTimeout = this.options.timeouts?.iceConnection || 30000;
|
||||
|
||||
this.timeout = setTimeout(async () => {
|
||||
this.cleanup();
|
||||
const { FailedState } = await import('./failed-state.js');
|
||||
this.peer.setState(new FailedState(
|
||||
this.peer,
|
||||
new Error('ICE connection timeout')
|
||||
));
|
||||
}, connectionTimeout);
|
||||
|
||||
this.pollingInterval = setInterval(async () => {
|
||||
try {
|
||||
const candidates = await this.peer.offersApi.getIceCandidates(
|
||||
this.offerId,
|
||||
this.lastIceTimestamp
|
||||
);
|
||||
|
||||
for (const cand of candidates) {
|
||||
if (cand.candidate && cand.candidate.candidate && cand.candidate.candidate !== '') {
|
||||
try {
|
||||
await this.peer.pc.addIceCandidate(new RTCIceCandidate(cand.candidate));
|
||||
this.lastIceTimestamp = cand.createdAt;
|
||||
} catch (err) {
|
||||
console.warn('Failed to add ICE candidate:', err);
|
||||
this.lastIceTimestamp = cand.createdAt;
|
||||
}
|
||||
} else {
|
||||
this.lastIceTimestamp = cand.createdAt;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error polling for ICE candidates:', err);
|
||||
if (err instanceof Error && err.message.includes('not found')) {
|
||||
this.cleanup();
|
||||
const { FailedState } = await import('./failed-state.js');
|
||||
this.peer.setState(new FailedState(
|
||||
this.peer,
|
||||
new Error('Offer expired or not found')
|
||||
));
|
||||
}
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
cleanup(): void {
|
||||
if (this.pollingInterval) clearInterval(this.pollingInterval);
|
||||
if (this.timeout) clearTimeout(this.timeout);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user