mirror of
https://github.com/xtr-dev/rondevu-client.git
synced 2025-12-11 11:23: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:
66
src/peer/creating-offer-state.ts
Normal file
66
src/peer/creating-offer-state.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { PeerState } from './state.js';
|
||||
import type { PeerOptions } from './types.js';
|
||||
import type RondevuPeer from './index.js';
|
||||
|
||||
/**
|
||||
* Creating offer and sending to server
|
||||
*/
|
||||
export class CreatingOfferState extends PeerState {
|
||||
constructor(peer: RondevuPeer, private options: PeerOptions) {
|
||||
super(peer);
|
||||
}
|
||||
|
||||
get name() { return 'creating-offer'; }
|
||||
|
||||
async createOffer(options: PeerOptions): Promise<string> {
|
||||
try {
|
||||
this.peer.role = 'offerer';
|
||||
|
||||
// Create data channel if requested
|
||||
if (options.createDataChannel !== false) {
|
||||
const channel = this.peer.pc.createDataChannel(
|
||||
options.dataChannelLabel || 'data'
|
||||
);
|
||||
this.peer.emitEvent('datachannel', channel);
|
||||
}
|
||||
|
||||
// Create WebRTC offer
|
||||
const offer = await this.peer.pc.createOffer();
|
||||
await this.peer.pc.setLocalDescription(offer);
|
||||
|
||||
// Send offer to server immediately (don't wait for ICE)
|
||||
const offers = await this.peer.offersApi.create([{
|
||||
sdp: offer.sdp!,
|
||||
topics: options.topics,
|
||||
ttl: options.ttl || 300000
|
||||
}]);
|
||||
|
||||
const offerId = offers[0].id;
|
||||
this.peer.offerId = offerId;
|
||||
|
||||
// Enable trickle ICE - send candidates as they arrive
|
||||
this.peer.pc.onicecandidate = async (event: RTCPeerConnectionIceEvent) => {
|
||||
if (event.candidate && offerId) {
|
||||
const candidateData = event.candidate.toJSON();
|
||||
if (candidateData.candidate && candidateData.candidate !== '') {
|
||||
try {
|
||||
await this.peer.offersApi.addIceCandidates(offerId, [candidateData]);
|
||||
} catch (err) {
|
||||
console.error('Error sending ICE candidate:', err);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Transition to waiting for answer
|
||||
const { WaitingForAnswerState } = await import('./waiting-for-answer-state.js');
|
||||
this.peer.setState(new WaitingForAnswerState(this.peer, offerId, options));
|
||||
|
||||
return offerId;
|
||||
} catch (error) {
|
||||
const { FailedState } = await import('./failed-state.js');
|
||||
this.peer.setState(new FailedState(this.peer, error as Error));
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user