mirror of
https://github.com/xtr-dev/rondevu-client.git
synced 2025-12-10 10:53:24 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 557cc0a838 | |||
| 6e661f69bc | |||
| 00f4da7250 |
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@xtr-dev/rondevu-client",
|
"name": "@xtr-dev/rondevu-client",
|
||||||
"version": "0.7.0",
|
"version": "0.7.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@xtr-dev/rondevu-client",
|
"name": "@xtr-dev/rondevu-client",
|
||||||
"version": "0.7.0",
|
"version": "0.7.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@xtr-dev/rondevu-client": "^0.5.1"
|
"@xtr-dev/rondevu-client": "^0.5.1"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@xtr-dev/rondevu-client",
|
"name": "@xtr-dev/rondevu-client",
|
||||||
"version": "0.7.0",
|
"version": "0.7.1",
|
||||||
"description": "TypeScript client for Rondevu topic-based peer discovery and signaling server",
|
"description": "TypeScript client for Rondevu topic-based peer discovery and signaling server",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|||||||
@@ -31,18 +31,7 @@ export class AnsweringState extends PeerState {
|
|||||||
await this.peer.offersApi.answer(offerId, answer.sdp!);
|
await this.peer.offersApi.answer(offerId, answer.sdp!);
|
||||||
|
|
||||||
// Enable trickle ICE - send candidates as they arrive
|
// Enable trickle ICE - send candidates as they arrive
|
||||||
this.peer.pc.onicecandidate = async (event: RTCPeerConnectionIceEvent) => {
|
this.setupIceCandidateHandler(offerId);
|
||||||
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 exchanging ICE
|
// Transition to exchanging ICE
|
||||||
const { ExchangingIceState } = await import('./exchanging-ice-state.js');
|
const { ExchangingIceState } = await import('./exchanging-ice-state.js');
|
||||||
|
|||||||
@@ -39,18 +39,7 @@ export class CreatingOfferState extends PeerState {
|
|||||||
this.peer.offerId = offerId;
|
this.peer.offerId = offerId;
|
||||||
|
|
||||||
// Enable trickle ICE - send candidates as they arrive
|
// Enable trickle ICE - send candidates as they arrive
|
||||||
this.peer.pc.onicecandidate = async (event: RTCPeerConnectionIceEvent) => {
|
this.setupIceCandidateHandler(offerId);
|
||||||
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
|
// Transition to waiting for answer
|
||||||
const { WaitingForAnswerState } = await import('./waiting-for-answer-state.js');
|
const { WaitingForAnswerState } = await import('./waiting-for-answer-state.js');
|
||||||
|
|||||||
@@ -26,6 +26,12 @@ export default class RondevuPeer extends EventEmitter<PeerEvents> {
|
|||||||
|
|
||||||
private _state: PeerState;
|
private _state: PeerState;
|
||||||
|
|
||||||
|
// Event handler references for cleanup
|
||||||
|
private connectionStateChangeHandler?: () => void;
|
||||||
|
private dataChannelHandler?: (event: RTCDataChannelEvent) => void;
|
||||||
|
private trackHandler?: (event: RTCTrackEvent) => void;
|
||||||
|
private iceCandidateErrorHandler?: (event: Event) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current connection state name
|
* Current connection state name
|
||||||
*/
|
*/
|
||||||
@@ -68,7 +74,7 @@ export default class RondevuPeer extends EventEmitter<PeerEvents> {
|
|||||||
* Set up peer connection event handlers
|
* Set up peer connection event handlers
|
||||||
*/
|
*/
|
||||||
private setupPeerConnection(): void {
|
private setupPeerConnection(): void {
|
||||||
this.pc.onconnectionstatechange = () => {
|
this.connectionStateChangeHandler = () => {
|
||||||
switch (this.pc.connectionState) {
|
switch (this.pc.connectionState) {
|
||||||
case 'connected':
|
case 'connected':
|
||||||
this.setState(new ConnectedState(this));
|
this.setState(new ConnectedState(this));
|
||||||
@@ -86,18 +92,22 @@ export default class RondevuPeer extends EventEmitter<PeerEvents> {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
this.pc.addEventListener('connectionstatechange', this.connectionStateChangeHandler);
|
||||||
|
|
||||||
this.pc.ondatachannel = (event) => {
|
this.dataChannelHandler = (event: RTCDataChannelEvent) => {
|
||||||
this.emitEvent('datachannel', event.channel);
|
this.emitEvent('datachannel', event.channel);
|
||||||
};
|
};
|
||||||
|
this.pc.addEventListener('datachannel', this.dataChannelHandler);
|
||||||
|
|
||||||
this.pc.ontrack = (event) => {
|
this.trackHandler = (event: RTCTrackEvent) => {
|
||||||
this.emitEvent('track', event);
|
this.emitEvent('track', event);
|
||||||
};
|
};
|
||||||
|
this.pc.addEventListener('track', this.trackHandler);
|
||||||
|
|
||||||
this.pc.onicecandidateerror = (event) => {
|
this.iceCandidateErrorHandler = (event: Event) => {
|
||||||
console.error('ICE candidate error:', event);
|
console.error('ICE candidate error:', event);
|
||||||
};
|
};
|
||||||
|
this.pc.addEventListener('icecandidateerror', this.iceCandidateErrorHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -145,6 +155,20 @@ export default class RondevuPeer extends EventEmitter<PeerEvents> {
|
|||||||
* Close the connection and clean up
|
* Close the connection and clean up
|
||||||
*/
|
*/
|
||||||
async close(): Promise<void> {
|
async close(): Promise<void> {
|
||||||
|
// Remove RTCPeerConnection event listeners
|
||||||
|
if (this.connectionStateChangeHandler) {
|
||||||
|
this.pc.removeEventListener('connectionstatechange', this.connectionStateChangeHandler);
|
||||||
|
}
|
||||||
|
if (this.dataChannelHandler) {
|
||||||
|
this.pc.removeEventListener('datachannel', this.dataChannelHandler);
|
||||||
|
}
|
||||||
|
if (this.trackHandler) {
|
||||||
|
this.pc.removeEventListener('track', this.trackHandler);
|
||||||
|
}
|
||||||
|
if (this.iceCandidateErrorHandler) {
|
||||||
|
this.pc.removeEventListener('icecandidateerror', this.iceCandidateErrorHandler);
|
||||||
|
}
|
||||||
|
|
||||||
await this._state.close();
|
await this._state.close();
|
||||||
this.removeAllListeners();
|
this.removeAllListeners();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ import type RondevuPeer from './index.js';
|
|||||||
* Implements the State pattern for managing WebRTC connection lifecycle
|
* Implements the State pattern for managing WebRTC connection lifecycle
|
||||||
*/
|
*/
|
||||||
export abstract class PeerState {
|
export abstract class PeerState {
|
||||||
|
protected iceCandidateHandler?: (event: RTCPeerConnectionIceEvent) => void;
|
||||||
|
|
||||||
constructor(protected peer: RondevuPeer) {}
|
constructor(protected peer: RondevuPeer) {}
|
||||||
|
|
||||||
abstract get name(): string;
|
abstract get name(): string;
|
||||||
@@ -29,8 +31,31 @@ export abstract class PeerState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup trickle ICE candidate handler
|
||||||
|
* Sends local ICE candidates to server as they are discovered
|
||||||
|
*/
|
||||||
|
protected setupIceCandidateHandler(offerId: string): void {
|
||||||
|
this.iceCandidateHandler = 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.peer.pc.addEventListener('icecandidate', this.iceCandidateHandler);
|
||||||
|
}
|
||||||
|
|
||||||
cleanup(): void {
|
cleanup(): void {
|
||||||
// Override in states that need cleanup
|
// Clean up ICE candidate handler if it exists
|
||||||
|
if (this.iceCandidateHandler) {
|
||||||
|
this.peer.pc.removeEventListener('icecandidate', this.iceCandidateHandler);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async close(): Promise<void> {
|
async close(): Promise<void> {
|
||||||
|
|||||||
Reference in New Issue
Block a user