mirror of
https://github.com/xtr-dev/rondevu-client.git
synced 2025-12-10 02:43:25 +00:00
Add WebRTC polyfill support for Node.js environments
Added optional polyfill parameters to RondevuOptions to support Node.js: - RTCPeerConnection: Custom peer connection implementation - RTCSessionDescription: Custom session description implementation - RTCIceCandidate: Custom ICE candidate implementation This allows users to plug in wrtc or node-webrtc packages for full WebRTC support in Node.js environments. Updated documentation with usage examples and environment compatibility matrix. Version bumped to 0.7.4 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
69
README.md
69
README.md
@@ -280,6 +280,43 @@ const client = new Rondevu({
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Node.js with WebRTC (wrtc)
|
||||||
|
|
||||||
|
For WebRTC functionality in Node.js, you need to provide WebRTC polyfills since Node.js doesn't have native WebRTC support:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install wrtc node-fetch
|
||||||
|
```
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
import { Rondevu } from '@xtr-dev/rondevu-client';
|
||||||
|
import fetch from 'node-fetch';
|
||||||
|
import { RTCPeerConnection, RTCSessionDescription, RTCIceCandidate } from 'wrtc';
|
||||||
|
|
||||||
|
const client = new Rondevu({
|
||||||
|
baseUrl: 'https://api.ronde.vu',
|
||||||
|
fetch: fetch as any,
|
||||||
|
RTCPeerConnection,
|
||||||
|
RTCSessionDescription,
|
||||||
|
RTCIceCandidate
|
||||||
|
});
|
||||||
|
|
||||||
|
// Now you can use WebRTC features
|
||||||
|
await client.register();
|
||||||
|
const peer = client.createPeer({
|
||||||
|
iceServers: [
|
||||||
|
{ urls: 'stun:stun.l.google.com:19302' }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create offers, answer, etc.
|
||||||
|
const offerId = await peer.createOffer({
|
||||||
|
topics: ['my-topic']
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note:** The `wrtc` package provides WebRTC bindings for Node.js. Alternative packages like `node-webrtc` can also be used - just pass their implementations to the Rondevu constructor.
|
||||||
|
|
||||||
### Deno
|
### Deno
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
@@ -500,28 +537,36 @@ import type {
|
|||||||
|
|
||||||
The client library is designed to work across different JavaScript runtimes:
|
The client library is designed to work across different JavaScript runtimes:
|
||||||
|
|
||||||
| Environment | Native Fetch | Custom Fetch Needed |
|
| Environment | Native Fetch | Native WebRTC | Polyfills Needed |
|
||||||
|-------------|--------------|---------------------|
|
|-------------|--------------|---------------|------------------|
|
||||||
| Modern Browsers | ✅ Yes | ❌ No |
|
| Modern Browsers | ✅ Yes | ✅ Yes | ❌ None |
|
||||||
| Node.js 18+ | ✅ Yes | ❌ No |
|
| Node.js 18+ | ✅ Yes | ❌ No | ✅ WebRTC (wrtc) |
|
||||||
| Node.js < 18 | ❌ No | ✅ Yes (node-fetch) |
|
| Node.js < 18 | ❌ No | ❌ No | ✅ Fetch + WebRTC |
|
||||||
| Deno | ✅ Yes | ❌ No |
|
| Deno | ✅ Yes | ⚠️ Partial | ❌ None (signaling only) |
|
||||||
| Bun | ✅ Yes | ❌ No |
|
| Bun | ✅ Yes | ❌ No | ✅ WebRTC (wrtc) |
|
||||||
| Cloudflare Workers | ✅ Yes | ❌ No |
|
| Cloudflare Workers | ✅ Yes | ❌ No | ❌ None (signaling only) |
|
||||||
|
|
||||||
**If your environment doesn't have native fetch:**
|
**For signaling-only (no WebRTC peer connections):**
|
||||||
|
|
||||||
|
Use the low-level API with `client.offers` - no WebRTC polyfills needed.
|
||||||
|
|
||||||
|
**For full WebRTC support in Node.js:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install node-fetch
|
npm install wrtc node-fetch
|
||||||
```
|
```
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { Rondevu } from '@xtr-dev/rondevu-client';
|
import { Rondevu } from '@xtr-dev/rondevu-client';
|
||||||
import fetch from 'node-fetch';
|
import fetch from 'node-fetch';
|
||||||
|
import { RTCPeerConnection, RTCSessionDescription, RTCIceCandidate } from 'wrtc';
|
||||||
|
|
||||||
const client = new Rondevu({
|
const client = new Rondevu({
|
||||||
baseUrl: 'https://rondevu.xtrdev.workers.dev',
|
baseUrl: 'https://api.ronde.vu',
|
||||||
fetch: fetch as any
|
fetch: fetch as any,
|
||||||
|
RTCPeerConnection,
|
||||||
|
RTCSessionDescription,
|
||||||
|
RTCIceCandidate
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@xtr-dev/rondevu-client",
|
"name": "@xtr-dev/rondevu-client",
|
||||||
"version": "0.7.3",
|
"version": "0.7.4",
|
||||||
"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",
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export class ExchangingIceState extends PeerState {
|
|||||||
for (const cand of candidates) {
|
for (const cand of candidates) {
|
||||||
if (cand.candidate && cand.candidate.candidate && cand.candidate.candidate !== '') {
|
if (cand.candidate && cand.candidate.candidate && cand.candidate.candidate !== '') {
|
||||||
try {
|
try {
|
||||||
await this.peer.pc.addIceCandidate(new RTCIceCandidate(cand.candidate));
|
await this.peer.pc.addIceCandidate(new this.peer.RTCIceCandidate(cand.candidate));
|
||||||
this.lastIceTimestamp = cand.createdAt;
|
this.lastIceTimestamp = cand.createdAt;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn('Failed to add ICE candidate:', err);
|
console.warn('Failed to add ICE candidate:', err);
|
||||||
|
|||||||
@@ -24,6 +24,11 @@ export default class RondevuPeer extends EventEmitter<PeerEvents> {
|
|||||||
offerId?: string;
|
offerId?: string;
|
||||||
role?: 'offerer' | 'answerer';
|
role?: 'offerer' | 'answerer';
|
||||||
|
|
||||||
|
// WebRTC polyfills for Node.js compatibility
|
||||||
|
RTCPeerConnection: typeof RTCPeerConnection;
|
||||||
|
RTCSessionDescription: typeof RTCSessionDescription;
|
||||||
|
RTCIceCandidate: typeof RTCIceCandidate;
|
||||||
|
|
||||||
private _state: PeerState;
|
private _state: PeerState;
|
||||||
|
|
||||||
// Event handler references for cleanup
|
// Event handler references for cleanup
|
||||||
@@ -60,11 +65,34 @@ export default class RondevuPeer extends EventEmitter<PeerEvents> {
|
|||||||
{ urls: 'stun:stun.l.google.com:19302' },
|
{ urls: 'stun:stun.l.google.com:19302' },
|
||||||
{ urls: 'stun:stun1.l.google.com:19302' }
|
{ urls: 'stun:stun1.l.google.com:19302' }
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
rtcPeerConnection?: typeof RTCPeerConnection,
|
||||||
|
rtcSessionDescription?: typeof RTCSessionDescription,
|
||||||
|
rtcIceCandidate?: typeof RTCIceCandidate
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
this.offersApi = offersApi;
|
this.offersApi = offersApi;
|
||||||
this.pc = new RTCPeerConnection(rtcConfig);
|
|
||||||
|
// Use provided polyfills or fall back to globals
|
||||||
|
this.RTCPeerConnection = rtcPeerConnection || (typeof globalThis.RTCPeerConnection !== 'undefined'
|
||||||
|
? globalThis.RTCPeerConnection
|
||||||
|
: (() => {
|
||||||
|
throw new Error('RTCPeerConnection is not available. Please provide it in the Rondevu constructor options for Node.js environments.');
|
||||||
|
}) as any);
|
||||||
|
|
||||||
|
this.RTCSessionDescription = rtcSessionDescription || (typeof globalThis.RTCSessionDescription !== 'undefined'
|
||||||
|
? globalThis.RTCSessionDescription
|
||||||
|
: (() => {
|
||||||
|
throw new Error('RTCSessionDescription is not available. Please provide it in the Rondevu constructor options for Node.js environments.');
|
||||||
|
}) as any);
|
||||||
|
|
||||||
|
this.RTCIceCandidate = rtcIceCandidate || (typeof globalThis.RTCIceCandidate !== 'undefined'
|
||||||
|
? globalThis.RTCIceCandidate
|
||||||
|
: (() => {
|
||||||
|
throw new Error('RTCIceCandidate is not available. Please provide it in the Rondevu constructor options for Node.js environments.');
|
||||||
|
}) as any);
|
||||||
|
|
||||||
|
this.pc = new this.RTCPeerConnection(rtcConfig);
|
||||||
this._state = new IdleState(this);
|
this._state = new IdleState(this);
|
||||||
|
|
||||||
this.setupPeerConnection();
|
this.setupPeerConnection();
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export abstract class PeerState {
|
|||||||
async handleIceCandidate(candidate: any): Promise<void> {
|
async handleIceCandidate(candidate: any): Promise<void> {
|
||||||
// ICE candidates can arrive in multiple states, so default is to add them
|
// ICE candidates can arrive in multiple states, so default is to add them
|
||||||
if (this.peer.pc.remoteDescription) {
|
if (this.peer.pc.remoteDescription) {
|
||||||
await this.peer.pc.addIceCandidate(new RTCIceCandidate(candidate));
|
await this.peer.pc.addIceCandidate(new this.peer.RTCIceCandidate(candidate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,42 @@ export interface RondevuOptions {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
fetch?: FetchFunction;
|
fetch?: FetchFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom RTCPeerConnection implementation for Node.js environments
|
||||||
|
* Required when using in Node.js with wrtc or similar polyfills
|
||||||
|
*
|
||||||
|
* @example Node.js with wrtc
|
||||||
|
* ```typescript
|
||||||
|
* import { RTCPeerConnection } from 'wrtc';
|
||||||
|
* const client = new Rondevu({ RTCPeerConnection });
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
RTCPeerConnection?: typeof RTCPeerConnection;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom RTCSessionDescription implementation for Node.js environments
|
||||||
|
* Required when using in Node.js with wrtc or similar polyfills
|
||||||
|
*
|
||||||
|
* @example Node.js with wrtc
|
||||||
|
* ```typescript
|
||||||
|
* import { RTCSessionDescription } from 'wrtc';
|
||||||
|
* const client = new Rondevu({ RTCSessionDescription });
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
RTCSessionDescription?: typeof RTCSessionDescription;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Custom RTCIceCandidate implementation for Node.js environments
|
||||||
|
* Required when using in Node.js with wrtc or similar polyfills
|
||||||
|
*
|
||||||
|
* @example Node.js with wrtc
|
||||||
|
* ```typescript
|
||||||
|
* import { RTCIceCandidate } from 'wrtc';
|
||||||
|
* const client = new Rondevu({ RTCIceCandidate });
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
RTCIceCandidate?: typeof RTCIceCandidate;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Rondevu {
|
export class Rondevu {
|
||||||
@@ -33,10 +69,16 @@ export class Rondevu {
|
|||||||
private credentials?: Credentials;
|
private credentials?: Credentials;
|
||||||
private baseUrl: string;
|
private baseUrl: string;
|
||||||
private fetchFn?: FetchFunction;
|
private fetchFn?: FetchFunction;
|
||||||
|
private rtcPeerConnection?: typeof RTCPeerConnection;
|
||||||
|
private rtcSessionDescription?: typeof RTCSessionDescription;
|
||||||
|
private rtcIceCandidate?: typeof RTCIceCandidate;
|
||||||
|
|
||||||
constructor(options: RondevuOptions = {}) {
|
constructor(options: RondevuOptions = {}) {
|
||||||
this.baseUrl = options.baseUrl || 'https://api.ronde.vu';
|
this.baseUrl = options.baseUrl || 'https://api.ronde.vu';
|
||||||
this.fetchFn = options.fetch;
|
this.fetchFn = options.fetch;
|
||||||
|
this.rtcPeerConnection = options.RTCPeerConnection;
|
||||||
|
this.rtcSessionDescription = options.RTCSessionDescription;
|
||||||
|
this.rtcIceCandidate = options.RTCIceCandidate;
|
||||||
|
|
||||||
this.auth = new RondevuAuth(this.baseUrl, this.fetchFn);
|
this.auth = new RondevuAuth(this.baseUrl, this.fetchFn);
|
||||||
|
|
||||||
@@ -98,6 +140,12 @@ export class Rondevu {
|
|||||||
throw new Error('Not authenticated. Call register() first or provide credentials.');
|
throw new Error('Not authenticated. Call register() first or provide credentials.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return new RondevuPeer(this._offers, rtcConfig);
|
return new RondevuPeer(
|
||||||
|
this._offers,
|
||||||
|
rtcConfig,
|
||||||
|
this.rtcPeerConnection,
|
||||||
|
this.rtcSessionDescription,
|
||||||
|
this.rtcIceCandidate
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user