mirror of
https://github.com/xtr-dev/rondevu-client.git
synced 2025-12-10 10:53:24 +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
|
||||
|
||||
```typescript
|
||||
@@ -500,28 +537,36 @@ import type {
|
||||
|
||||
The client library is designed to work across different JavaScript runtimes:
|
||||
|
||||
| Environment | Native Fetch | Custom Fetch Needed |
|
||||
|-------------|--------------|---------------------|
|
||||
| Modern Browsers | ✅ Yes | ❌ No |
|
||||
| Node.js 18+ | ✅ Yes | ❌ No |
|
||||
| Node.js < 18 | ❌ No | ✅ Yes (node-fetch) |
|
||||
| Deno | ✅ Yes | ❌ No |
|
||||
| Bun | ✅ Yes | ❌ No |
|
||||
| Cloudflare Workers | ✅ Yes | ❌ No |
|
||||
| Environment | Native Fetch | Native WebRTC | Polyfills Needed |
|
||||
|-------------|--------------|---------------|------------------|
|
||||
| Modern Browsers | ✅ Yes | ✅ Yes | ❌ None |
|
||||
| Node.js 18+ | ✅ Yes | ❌ No | ✅ WebRTC (wrtc) |
|
||||
| Node.js < 18 | ❌ No | ❌ No | ✅ Fetch + WebRTC |
|
||||
| Deno | ✅ Yes | ⚠️ Partial | ❌ None (signaling only) |
|
||||
| Bun | ✅ Yes | ❌ No | ✅ WebRTC (wrtc) |
|
||||
| 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
|
||||
npm install node-fetch
|
||||
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://rondevu.xtrdev.workers.dev',
|
||||
fetch: fetch as any
|
||||
baseUrl: 'https://api.ronde.vu',
|
||||
fetch: fetch as any,
|
||||
RTCPeerConnection,
|
||||
RTCSessionDescription,
|
||||
RTCIceCandidate
|
||||
});
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"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",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
|
||||
@@ -43,7 +43,7 @@ export class ExchangingIceState extends PeerState {
|
||||
for (const cand of candidates) {
|
||||
if (cand.candidate && cand.candidate.candidate && cand.candidate.candidate !== '') {
|
||||
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;
|
||||
} catch (err) {
|
||||
console.warn('Failed to add ICE candidate:', err);
|
||||
|
||||
@@ -24,6 +24,11 @@ export default class RondevuPeer extends EventEmitter<PeerEvents> {
|
||||
offerId?: string;
|
||||
role?: 'offerer' | 'answerer';
|
||||
|
||||
// WebRTC polyfills for Node.js compatibility
|
||||
RTCPeerConnection: typeof RTCPeerConnection;
|
||||
RTCSessionDescription: typeof RTCSessionDescription;
|
||||
RTCIceCandidate: typeof RTCIceCandidate;
|
||||
|
||||
private _state: PeerState;
|
||||
|
||||
// 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:stun1.l.google.com:19302' }
|
||||
]
|
||||
}
|
||||
},
|
||||
rtcPeerConnection?: typeof RTCPeerConnection,
|
||||
rtcSessionDescription?: typeof RTCSessionDescription,
|
||||
rtcIceCandidate?: typeof RTCIceCandidate
|
||||
) {
|
||||
super();
|
||||
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.setupPeerConnection();
|
||||
|
||||
@@ -27,7 +27,7 @@ export abstract class PeerState {
|
||||
async handleIceCandidate(candidate: any): Promise<void> {
|
||||
// ICE candidates can arrive in multiple states, so default is to add them
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
@@ -33,10 +69,16 @@ export class Rondevu {
|
||||
private credentials?: Credentials;
|
||||
private baseUrl: string;
|
||||
private fetchFn?: FetchFunction;
|
||||
private rtcPeerConnection?: typeof RTCPeerConnection;
|
||||
private rtcSessionDescription?: typeof RTCSessionDescription;
|
||||
private rtcIceCandidate?: typeof RTCIceCandidate;
|
||||
|
||||
constructor(options: RondevuOptions = {}) {
|
||||
this.baseUrl = options.baseUrl || 'https://api.ronde.vu';
|
||||
this.fetchFn = options.fetch;
|
||||
this.rtcPeerConnection = options.RTCPeerConnection;
|
||||
this.rtcSessionDescription = options.RTCSessionDescription;
|
||||
this.rtcIceCandidate = options.RTCIceCandidate;
|
||||
|
||||
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.');
|
||||
}
|
||||
|
||||
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