Fix multiple connect events and add leave method

- Add hasConnected flag to prevent duplicate connect events
- Add leave() method to RondevuConnection to end sessions
- Add leave() API method to call /leave endpoint
- Version 0.3.5

The connect event will now only fire once per connection,
fixing the issue where it could fire multiple times as the
WebRTC connectionState transitions.

The leave() method allows either peer to end the session
by deleting the offer from the server.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-13 00:05:29 +01:00
parent 2f47107018
commit e1ca8e1c16
4 changed files with 42 additions and 5 deletions

View File

@@ -1,6 +1,6 @@
{ {
"name": "@xtr-dev/rondevu-client", "name": "@xtr-dev/rondevu-client",
"version": "0.3.4", "version": "0.3.5",
"description": "TypeScript client for Rondevu peer signaling and discovery server", "description": "TypeScript client for Rondevu peer signaling and discovery server",
"type": "module", "type": "module",
"main": "dist/index.js", "main": "dist/index.js",

View File

@@ -186,4 +186,23 @@ export class RondevuAPI {
method: 'GET', method: 'GET',
}); });
} }
/**
* Ends a session by deleting the offer from the server
*
* @param code - The offer code
* @returns Success confirmation
*
* @example
* ```typescript
* const api = new RondevuAPI({ baseUrl: 'https://example.com' });
* await api.leave('my-offer-code');
* ```
*/
async leave(code: string): Promise<{ success: boolean }> {
return this.request<{ success: boolean }>('/leave', {
method: 'POST',
body: JSON.stringify({ code }),
});
}
} }

View File

@@ -20,6 +20,7 @@ export class RondevuConnection extends EventEmitter {
private connectionTimer?: ReturnType<typeof setTimeout>; private connectionTimer?: ReturnType<typeof setTimeout>;
private isPolling: boolean = false; private isPolling: boolean = false;
private isClosed: boolean = false; private isClosed: boolean = false;
private hasConnected: boolean = false;
private wrtc?: WebRTCPolyfill; private wrtc?: WebRTCPolyfill;
private RTCIceCandidate: typeof RTCIceCandidate; private RTCIceCandidate: typeof RTCIceCandidate;
@@ -94,9 +95,12 @@ export class RondevuConnection extends EventEmitter {
switch (state) { switch (state) {
case 'connected': case 'connected':
this.clearConnectionTimeout(); if (!this.hasConnected) {
this.stopPolling(); this.hasConnected = true;
this.emit('connect'); this.clearConnectionTimeout();
this.stopPolling();
this.emit('connect');
}
break; break;
case 'disconnected': case 'disconnected':
@@ -283,6 +287,20 @@ export class RondevuConnection extends EventEmitter {
} }
} }
/**
* Leave the session by deleting the offer on the server and closing the connection
* This ends the session for all connected peers
*/
async leave(): Promise<void> {
try {
await this.client.leave(this.id);
} catch (err) {
// Ignore errors - session might already be expired
console.debug('Leave error (ignored):', err);
}
this.close();
}
/** /**
* Close the connection and cleanup resources * Close the connection and cleanup resources
*/ */

View File

@@ -62,7 +62,7 @@ export class Rondevu {
private async checkServerVersion(): Promise<void> { private async checkServerVersion(): Promise<void> {
try { try {
const { version: serverVersion } = await this.api.health(); const { version: serverVersion } = await this.api.health();
const clientVersion = '0.3.4'; // Should match package.json const clientVersion = '0.3.5'; // Should match package.json
if (!this.isVersionCompatible(clientVersion, serverVersion)) { if (!this.isVersionCompatible(clientVersion, serverVersion)) {
console.warn( console.warn(