mirror of
https://github.com/xtr-dev/rondevu-client.git
synced 2025-12-10 10:53:24 +00:00
Add Node.js support via WebRTC polyfill injection
- Added WebRTCPolyfill interface for injecting WebRTC implementations - Added wrtc option to RondevuOptions and RondevuConnectionParams - Updated Rondevu and RondevuConnection to use injected APIs - Added helpful error message when RTCPeerConnection is not available - Updated README with Node.js usage examples - Version bumped to 0.3.0 Fixes: RTCPeerConnection not defined error in Node.js 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
40
README.md
40
README.md
@@ -24,6 +24,8 @@ npm install @xtr-dev/rondevu-client
|
|||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
|
#### Browser
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { Rondevu } from '@xtr-dev/rondevu-client';
|
import { Rondevu } from '@xtr-dev/rondevu-client';
|
||||||
|
|
||||||
@@ -56,6 +58,44 @@ conn.on('connect', () => {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
#### Node.js
|
||||||
|
|
||||||
|
In Node.js, you need to provide a WebRTC polyfill since WebRTC APIs are not natively available:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install @roamhq/wrtc
|
||||||
|
# or
|
||||||
|
npm install wrtc
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { Rondevu } from '@xtr-dev/rondevu-client';
|
||||||
|
import wrtc from '@roamhq/wrtc';
|
||||||
|
import fetch from 'node-fetch';
|
||||||
|
|
||||||
|
const rdv = new Rondevu({
|
||||||
|
baseUrl: 'https://server.com',
|
||||||
|
fetch: fetch as any,
|
||||||
|
wrtc: {
|
||||||
|
RTCPeerConnection: wrtc.RTCPeerConnection,
|
||||||
|
RTCSessionDescription: wrtc.RTCSessionDescription,
|
||||||
|
RTCIceCandidate: wrtc.RTCIceCandidate,
|
||||||
|
},
|
||||||
|
rtcConfig: {
|
||||||
|
iceServers: [
|
||||||
|
{ urls: 'stun:stun.l.google.com:19302' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Rest is the same as browser usage
|
||||||
|
const conn = await rdv.join('room');
|
||||||
|
conn.on('connect', () => {
|
||||||
|
const channel = conn.dataChannel('chat');
|
||||||
|
channel.send('Hello from Node.js!');
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
### API
|
### API
|
||||||
|
|
||||||
**Main Methods:**
|
**Main Methods:**
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@xtr-dev/rondevu-client",
|
"name": "@xtr-dev/rondevu-client",
|
||||||
"version": "0.2.2",
|
"version": "0.3.0",
|
||||||
"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",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { EventEmitter } from './event-emitter.js';
|
import { EventEmitter } from './event-emitter.js';
|
||||||
import { RondevuAPI } from './client.js';
|
import { RondevuAPI } from './client.js';
|
||||||
import { RondevuConnectionParams } from './types.js';
|
import { RondevuConnectionParams, WebRTCPolyfill } from './types.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a WebRTC connection with automatic signaling and ICE exchange
|
* Represents a WebRTC connection with automatic signaling and ICE exchange
|
||||||
@@ -21,6 +21,8 @@ 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 wrtc?: WebRTCPolyfill;
|
||||||
|
private RTCIceCandidate: typeof RTCIceCandidate;
|
||||||
|
|
||||||
constructor(params: RondevuConnectionParams, client: RondevuAPI) {
|
constructor(params: RondevuConnectionParams, client: RondevuAPI) {
|
||||||
super();
|
super();
|
||||||
@@ -34,6 +36,10 @@ export class RondevuConnection extends EventEmitter {
|
|||||||
this.dataChannels = new Map();
|
this.dataChannels = new Map();
|
||||||
this.pollingIntervalMs = params.pollingInterval;
|
this.pollingIntervalMs = params.pollingInterval;
|
||||||
this.connectionTimeoutMs = params.connectionTimeout;
|
this.connectionTimeoutMs = params.connectionTimeout;
|
||||||
|
this.wrtc = params.wrtc;
|
||||||
|
|
||||||
|
// Use injected WebRTC polyfill or fall back to global
|
||||||
|
this.RTCIceCandidate = params.wrtc?.RTCIceCandidate || globalThis.RTCIceCandidate;
|
||||||
|
|
||||||
this.setupEventHandlers();
|
this.setupEventHandlers();
|
||||||
this.startConnectionTimeout();
|
this.startConnectionTimeout();
|
||||||
@@ -187,7 +193,7 @@ export class RondevuConnection extends EventEmitter {
|
|||||||
for (const candidateStr of offererResponse.answerCandidates) {
|
for (const candidateStr of offererResponse.answerCandidates) {
|
||||||
try {
|
try {
|
||||||
const candidate = JSON.parse(candidateStr);
|
const candidate = JSON.parse(candidateStr);
|
||||||
await this.pc.addIceCandidate(new RTCIceCandidate(candidate));
|
await this.pc.addIceCandidate(new this.RTCIceCandidate(candidate));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn('Failed to add ICE candidate:', err);
|
console.warn('Failed to add ICE candidate:', err);
|
||||||
}
|
}
|
||||||
@@ -202,7 +208,7 @@ export class RondevuConnection extends EventEmitter {
|
|||||||
for (const candidateStr of answererResponse.offerCandidates) {
|
for (const candidateStr of answererResponse.offerCandidates) {
|
||||||
try {
|
try {
|
||||||
const candidate = JSON.parse(candidateStr);
|
const candidate = JSON.parse(candidateStr);
|
||||||
await this.pc.addIceCandidate(new RTCIceCandidate(candidate));
|
await this.pc.addIceCandidate(new this.RTCIceCandidate(candidate));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn('Failed to add ICE candidate:', err);
|
console.warn('Failed to add ICE candidate:', err);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export type {
|
|||||||
ConnectionRole,
|
ConnectionRole,
|
||||||
RondevuConnectionParams,
|
RondevuConnectionParams,
|
||||||
RondevuConnectionEvents,
|
RondevuConnectionEvents,
|
||||||
|
WebRTCPolyfill,
|
||||||
// Signaling types
|
// Signaling types
|
||||||
Side,
|
Side,
|
||||||
Session,
|
Session,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { RondevuAPI } from './client.js';
|
import { RondevuAPI } from './client.js';
|
||||||
import { RondevuConnection } from './connection.js';
|
import { RondevuConnection } from './connection.js';
|
||||||
import { RondevuOptions, JoinOptions, RondevuConnectionParams } from './types.js';
|
import { RondevuOptions, JoinOptions, RondevuConnectionParams, WebRTCPolyfill } from './types.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main Rondevu WebRTC client with automatic connection management
|
* Main Rondevu WebRTC client with automatic connection management
|
||||||
@@ -14,6 +14,9 @@ export class Rondevu {
|
|||||||
private rtcConfig?: RTCConfiguration;
|
private rtcConfig?: RTCConfiguration;
|
||||||
private pollingInterval: number;
|
private pollingInterval: number;
|
||||||
private connectionTimeout: number;
|
private connectionTimeout: number;
|
||||||
|
private wrtc?: WebRTCPolyfill;
|
||||||
|
private RTCPeerConnection: typeof RTCPeerConnection;
|
||||||
|
private RTCIceCandidate: typeof RTCIceCandidate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Rondevu client instance
|
* Creates a new Rondevu client instance
|
||||||
@@ -22,6 +25,7 @@ export class Rondevu {
|
|||||||
constructor(options: RondevuOptions = {}) {
|
constructor(options: RondevuOptions = {}) {
|
||||||
this.baseUrl = options.baseUrl || 'https://rondevu.xtrdev.workers.dev';
|
this.baseUrl = options.baseUrl || 'https://rondevu.xtrdev.workers.dev';
|
||||||
this.fetchImpl = options.fetch;
|
this.fetchImpl = options.fetch;
|
||||||
|
this.wrtc = options.wrtc;
|
||||||
|
|
||||||
this.api = new RondevuAPI({
|
this.api = new RondevuAPI({
|
||||||
baseUrl: this.baseUrl,
|
baseUrl: this.baseUrl,
|
||||||
@@ -33,6 +37,18 @@ export class Rondevu {
|
|||||||
this.rtcConfig = options.rtcConfig;
|
this.rtcConfig = options.rtcConfig;
|
||||||
this.pollingInterval = options.pollingInterval || 1000;
|
this.pollingInterval = options.pollingInterval || 1000;
|
||||||
this.connectionTimeout = options.connectionTimeout || 30000;
|
this.connectionTimeout = options.connectionTimeout || 30000;
|
||||||
|
|
||||||
|
// Use injected WebRTC polyfill or fall back to global
|
||||||
|
this.RTCPeerConnection = options.wrtc?.RTCPeerConnection || globalThis.RTCPeerConnection;
|
||||||
|
this.RTCIceCandidate = options.wrtc?.RTCIceCandidate || globalThis.RTCIceCandidate;
|
||||||
|
|
||||||
|
if (!this.RTCPeerConnection) {
|
||||||
|
throw new Error(
|
||||||
|
'RTCPeerConnection not available. ' +
|
||||||
|
'In Node.js, provide a WebRTC polyfill via the wrtc option. ' +
|
||||||
|
'Install: npm install @roamhq/wrtc or npm install wrtc'
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,7 +73,7 @@ export class Rondevu {
|
|||||||
*/
|
*/
|
||||||
async create(id: string, topic: string): Promise<RondevuConnection> {
|
async create(id: string, topic: string): Promise<RondevuConnection> {
|
||||||
// Create peer connection
|
// Create peer connection
|
||||||
const pc = new RTCPeerConnection(this.rtcConfig);
|
const pc = new this.RTCPeerConnection(this.rtcConfig);
|
||||||
|
|
||||||
// Create initial data channel for negotiation (required for offer creation)
|
// Create initial data channel for negotiation (required for offer creation)
|
||||||
pc.createDataChannel('_negotiation');
|
pc.createDataChannel('_negotiation');
|
||||||
@@ -86,6 +102,7 @@ export class Rondevu {
|
|||||||
remotePeerId: '', // Will be populated when answer is received
|
remotePeerId: '', // Will be populated when answer is received
|
||||||
pollingInterval: this.pollingInterval,
|
pollingInterval: this.pollingInterval,
|
||||||
connectionTimeout: this.connectionTimeout,
|
connectionTimeout: this.connectionTimeout,
|
||||||
|
wrtc: this.wrtc,
|
||||||
};
|
};
|
||||||
|
|
||||||
const connection = new RondevuConnection(connectionParams, this.api);
|
const connection = new RondevuConnection(connectionParams, this.api);
|
||||||
@@ -110,7 +127,7 @@ export class Rondevu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create peer connection
|
// Create peer connection
|
||||||
const pc = new RTCPeerConnection(this.rtcConfig);
|
const pc = new this.RTCPeerConnection(this.rtcConfig);
|
||||||
|
|
||||||
// Set remote offer
|
// Set remote offer
|
||||||
await pc.setRemoteDescription({
|
await pc.setRemoteDescription({
|
||||||
@@ -142,6 +159,7 @@ export class Rondevu {
|
|||||||
remotePeerId: sessionData.peerId,
|
remotePeerId: sessionData.peerId,
|
||||||
pollingInterval: this.pollingInterval,
|
pollingInterval: this.pollingInterval,
|
||||||
connectionTimeout: this.connectionTimeout,
|
connectionTimeout: this.connectionTimeout,
|
||||||
|
wrtc: this.wrtc,
|
||||||
};
|
};
|
||||||
|
|
||||||
const connection = new RondevuConnection(connectionParams, this.api);
|
const connection = new RondevuConnection(connectionParams, this.api);
|
||||||
|
|||||||
12
src/types.ts
12
src/types.ts
@@ -177,6 +177,15 @@ export interface RondevuClientOptions {
|
|||||||
// WebRTC Types
|
// WebRTC Types
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebRTC polyfill for Node.js and other non-browser platforms
|
||||||
|
*/
|
||||||
|
export interface WebRTCPolyfill {
|
||||||
|
RTCPeerConnection: typeof RTCPeerConnection;
|
||||||
|
RTCSessionDescription: typeof RTCSessionDescription;
|
||||||
|
RTCIceCandidate: typeof RTCIceCandidate;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration options for Rondevu WebRTC client
|
* Configuration options for Rondevu WebRTC client
|
||||||
*/
|
*/
|
||||||
@@ -193,6 +202,8 @@ export interface RondevuOptions {
|
|||||||
pollingInterval?: number;
|
pollingInterval?: number;
|
||||||
/** Connection timeout in milliseconds (default: 30000) */
|
/** Connection timeout in milliseconds (default: 30000) */
|
||||||
connectionTimeout?: number;
|
connectionTimeout?: number;
|
||||||
|
/** WebRTC polyfill for Node.js (e.g., wrtc or @roamhq/wrtc) */
|
||||||
|
wrtc?: WebRTCPolyfill;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -222,6 +233,7 @@ export interface RondevuConnectionParams {
|
|||||||
remotePeerId: string;
|
remotePeerId: string;
|
||||||
pollingInterval: number;
|
pollingInterval: number;
|
||||||
connectionTimeout: number;
|
connectionTimeout: number;
|
||||||
|
wrtc?: WebRTCPolyfill;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user