uniwrtc 1.0.7 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLOUDFLARE_DEPLOYMENT.md +2 -2
- package/README.md +62 -22
- package/client-browser.js +2 -254
- package/client.js +178 -136
- package/demo.html +1 -1
- package/deploy-cloudflare.sh +13 -6
- package/package.json +2 -5
- package/server.js +264 -282
- package/simple-peer-adapter.js +124 -0
- package/src/client-cloudflare.js +138 -119
- package/src/index.js +11 -11
- package/src/main.js +55 -15
- package/src/room.js +137 -169
- package/tests/e2e.spec.js +77 -58
- package/wrangler.toml +8 -13
package/CLOUDFLARE_DEPLOYMENT.md
CHANGED
|
@@ -70,7 +70,7 @@ await client.connect();
|
|
|
70
70
|
## How It Works
|
|
71
71
|
|
|
72
72
|
1. **Durable Objects** - One per room, manages peer discovery
|
|
73
|
-
2. **
|
|
73
|
+
2. **HTTP polling** - Browsers connect for signaling
|
|
74
74
|
3. **Signaling Only** - Offers/answers/ICE via Worker
|
|
75
75
|
4. **P2P Data** - WebRTC data channels bypass Cloudflare
|
|
76
76
|
5. **Free Tier** - Plenty of capacity for small deployments
|
|
@@ -109,7 +109,7 @@ const serverUrl = 'http://localhost:8787/';
|
|
|
109
109
|
|
|
110
110
|
## Troubleshooting
|
|
111
111
|
|
|
112
|
-
**
|
|
112
|
+
**Connection errors**: Ensure your domain is on Cloudflare with SSL enabled
|
|
113
113
|
|
|
114
114
|
**Connection refused**: Check the Worker route pattern in `wrangler.toml`
|
|
115
115
|
|
package/README.md
CHANGED
|
@@ -1,21 +1,53 @@
|
|
|
1
1
|
# UniWRTC
|
|
2
2
|
|
|
3
|
-
A universal WebRTC signaling service that provides a simple and flexible
|
|
3
|
+
A universal WebRTC signaling service that provides a simple and flexible **HTTP polling** signaling server for WebRTC applications.
|
|
4
4
|
|
|
5
5
|
Available on npm: https://www.npmjs.com/package/uniwrtc
|
|
6
6
|
|
|
7
7
|
## Features
|
|
8
8
|
|
|
9
|
-
- 🚀 **Simple
|
|
9
|
+
- 🚀 **Simple signaling** - HTTP polling (works locally and on Cloudflare Durable Objects)
|
|
10
10
|
- 🏠 **Session-based architecture** - Support for multiple sessions with isolated peer groups
|
|
11
11
|
- 🔌 **Flexible client library** - Ready-to-use JavaScript client for browser and Node.js
|
|
12
12
|
- 📡 **Real-time messaging** - Efficient message routing between peers
|
|
13
13
|
- 🔄 **Auto-reconnection** - Built-in reconnection logic for reliable connections
|
|
14
14
|
- 📊 **Health monitoring** - HTTP health check endpoint for monitoring
|
|
15
|
-
- 🎯 **Minimal dependencies** - Lightweight implementation
|
|
15
|
+
- 🎯 **Minimal dependencies** - Lightweight implementation with minimal runtime deps
|
|
16
16
|
|
|
17
17
|
## Quick Start
|
|
18
18
|
|
|
19
|
+
## Using with `simple-peer` (SDP text-only)
|
|
20
|
+
|
|
21
|
+
This repo's signaling format sends **SDP as plain text** for offers/answers.
|
|
22
|
+
`simple-peer` uses `{ type, sdp }` objects, so use the adapter in [simple-peer-adapter.js](simple-peer-adapter.js).
|
|
23
|
+
|
|
24
|
+
Example (browser):
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
import Peer from 'simple-peer';
|
|
28
|
+
import UniWRTCClient from './client-browser.js';
|
|
29
|
+
import { sendSimplePeerSignal, attachUniWRTCToSimplePeer, chooseDeterministicInitiator } from './simple-peer-adapter.js';
|
|
30
|
+
|
|
31
|
+
const client = new UniWRTCClient('https://your-signal-server', { roomId: 'my-room' });
|
|
32
|
+
await client.connect();
|
|
33
|
+
|
|
34
|
+
// Join a session (peers in the same session can connect)
|
|
35
|
+
await client.joinSession('my-room');
|
|
36
|
+
|
|
37
|
+
// Ensure exactly ONE side initiates for a given pair
|
|
38
|
+
const initiator = chooseDeterministicInitiator(client.clientId, targetId);
|
|
39
|
+
const peer = new Peer({ initiator, trickle: true });
|
|
40
|
+
const cleanup = attachUniWRTCToSimplePeer(client, peer);
|
|
41
|
+
|
|
42
|
+
peer.on('signal', (signal) => {
|
|
43
|
+
// targetId must be the other peer's UniWRTC client id
|
|
44
|
+
sendSimplePeerSignal(client, signal, targetId);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// When done:
|
|
48
|
+
// cleanup();
|
|
49
|
+
```
|
|
50
|
+
|
|
19
51
|
### Installation
|
|
20
52
|
|
|
21
53
|
#### From npm (recommended)
|
|
@@ -63,7 +95,7 @@ The interactive demo is available live at **https://signal.peer.ooo/** (Cloudfla
|
|
|
63
95
|
5. Open the P2P chat and send messages between tabs
|
|
64
96
|
|
|
65
97
|
**Or run locally:**
|
|
66
|
-
1. Start the server: `npm start` (signaling at `
|
|
98
|
+
1. Start the server: `npm start` (signaling at `http://localhost:8080`)
|
|
67
99
|
2. Start the Vite dev server: `npm run dev` (demo at `http://localhost:5173/`)
|
|
68
100
|
3. Open the demo in two browser tabs
|
|
69
101
|
4. Enter the same session ID in both, then Connect
|
|
@@ -73,7 +105,8 @@ The interactive demo is available live at **https://signal.peer.ooo/** (Cloudfla
|
|
|
73
105
|
|
|
74
106
|
### Server API
|
|
75
107
|
|
|
76
|
-
The signaling server
|
|
108
|
+
The signaling server supports:
|
|
109
|
+
- HTTP polling signaling (no WebSockets)
|
|
77
110
|
|
|
78
111
|
#### Client → Server Messages
|
|
79
112
|
|
|
@@ -97,7 +130,7 @@ The signaling server accepts WebSocket connections and supports the following me
|
|
|
97
130
|
```json
|
|
98
131
|
{
|
|
99
132
|
"type": "offer",
|
|
100
|
-
"offer":
|
|
133
|
+
"offer": "v=0\r\n...",
|
|
101
134
|
"targetId": "peer-client-id",
|
|
102
135
|
"sessionId": "session-123"
|
|
103
136
|
}
|
|
@@ -107,7 +140,7 @@ The signaling server accepts WebSocket connections and supports the following me
|
|
|
107
140
|
```json
|
|
108
141
|
{
|
|
109
142
|
"type": "answer",
|
|
110
|
-
"answer":
|
|
143
|
+
"answer": "v=0\r\n...",
|
|
111
144
|
"targetId": "peer-client-id",
|
|
112
145
|
"sessionId": "session-123"
|
|
113
146
|
}
|
|
@@ -117,7 +150,7 @@ The signaling server accepts WebSocket connections and supports the following me
|
|
|
117
150
|
```json
|
|
118
151
|
{
|
|
119
152
|
"type": "ice-candidate",
|
|
120
|
-
"candidate":
|
|
153
|
+
"candidate": "candidate:...|0|0",
|
|
121
154
|
"targetId": "peer-client-id",
|
|
122
155
|
"sessionId": "session-123"
|
|
123
156
|
}
|
|
@@ -175,16 +208,13 @@ Use directly from npm:
|
|
|
175
208
|
```javascript
|
|
176
209
|
// ESM (browser)
|
|
177
210
|
import UniWRTCClient from 'uniwrtc/client-browser.js';
|
|
178
|
-
|
|
179
|
-
// CommonJS (Node.js)
|
|
180
|
-
const UniWRTCClient = require('uniwrtc/client.js');
|
|
181
211
|
```
|
|
182
212
|
|
|
183
213
|
The `client.js` library provides a convenient wrapper for the signaling protocol:
|
|
184
214
|
|
|
185
215
|
```javascript
|
|
186
216
|
// Create a client instance
|
|
187
|
-
const client = new UniWRTCClient('
|
|
217
|
+
const client = new UniWRTCClient('http://localhost:8080', { roomId: 'my-room' });
|
|
188
218
|
|
|
189
219
|
// Set up event handlers
|
|
190
220
|
client.on('connected', (data) => {
|
|
@@ -220,7 +250,7 @@ client.on('ice-candidate', (data) => {
|
|
|
220
250
|
await client.connect();
|
|
221
251
|
|
|
222
252
|
// Join a session
|
|
223
|
-
client.joinSession('my-session');
|
|
253
|
+
await client.joinSession('my-session');
|
|
224
254
|
|
|
225
255
|
// Send WebRTC signaling messages
|
|
226
256
|
client.sendOffer(offerObject, targetPeerId);
|
|
@@ -233,7 +263,7 @@ client.sendIceCandidate(candidateObject, targetPeerId);
|
|
|
233
263
|
Here's a complete example of creating a WebRTC peer connection:
|
|
234
264
|
|
|
235
265
|
```javascript
|
|
236
|
-
const client = new UniWRTCClient('
|
|
266
|
+
const client = new UniWRTCClient('http://localhost:8080', { roomId: 'my-room' });
|
|
237
267
|
const peerConnections = new Map();
|
|
238
268
|
|
|
239
269
|
// ICE server configuration
|
|
@@ -281,7 +311,7 @@ client.on('peer-joined', async (data) => {
|
|
|
281
311
|
client.on('offer', async (data) => {
|
|
282
312
|
const pc = createPeerConnection(data.peerId);
|
|
283
313
|
|
|
284
|
-
await pc.setRemoteDescription(data.offer);
|
|
314
|
+
await pc.setRemoteDescription({ type: 'offer', sdp: data.offer });
|
|
285
315
|
const answer = await pc.createAnswer();
|
|
286
316
|
await pc.setLocalDescription(answer);
|
|
287
317
|
client.sendAnswer(answer, data.peerId);
|
|
@@ -291,7 +321,7 @@ client.on('offer', async (data) => {
|
|
|
291
321
|
client.on('answer', async (data) => {
|
|
292
322
|
const pc = peerConnections.get(data.peerId);
|
|
293
323
|
if (pc) {
|
|
294
|
-
await pc.setRemoteDescription(data.answer);
|
|
324
|
+
await pc.setRemoteDescription({ type: 'answer', sdp: data.answer });
|
|
295
325
|
}
|
|
296
326
|
});
|
|
297
327
|
|
|
@@ -299,7 +329,12 @@ client.on('answer', async (data) => {
|
|
|
299
329
|
client.on('ice-candidate', async (data) => {
|
|
300
330
|
const pc = peerConnections.get(data.peerId);
|
|
301
331
|
if (pc) {
|
|
302
|
-
|
|
332
|
+
const [candidate, sdpMidRaw, sdpMLineIndexRaw] = String(data.candidate).split('|');
|
|
333
|
+
await pc.addIceCandidate(new RTCIceCandidate({
|
|
334
|
+
candidate,
|
|
335
|
+
sdpMid: sdpMidRaw || undefined,
|
|
336
|
+
sdpMLineIndex: sdpMLineIndexRaw !== undefined && sdpMLineIndexRaw !== '' ? Number(sdpMLineIndexRaw) : undefined
|
|
337
|
+
}));
|
|
303
338
|
}
|
|
304
339
|
});
|
|
305
340
|
|
|
@@ -307,8 +342,8 @@ client.on('ice-candidate', async (data) => {
|
|
|
307
342
|
await client.connect();
|
|
308
343
|
client.joinSession('my-video-session');
|
|
309
344
|
|
|
310
|
-
// Or use Cloudflare Durable Objects deployment
|
|
311
|
-
const cfClient = new UniWRTCClient('
|
|
345
|
+
// Or use Cloudflare Durable Objects deployment (HTTP polling; no WebSockets)
|
|
346
|
+
const cfClient = new UniWRTCClient('https://signal.peer.ooo');
|
|
312
347
|
await cfClient.connect();
|
|
313
348
|
cfClient.joinSession('my-session');
|
|
314
349
|
```
|
|
@@ -323,7 +358,7 @@ new UniWRTCClient(serverUrl, options)
|
|
|
323
358
|
```
|
|
324
359
|
|
|
325
360
|
**Parameters:**
|
|
326
|
-
- `serverUrl` (string):
|
|
361
|
+
- `serverUrl` (string): HTTP(S) URL of the signaling server
|
|
327
362
|
- `options` (object, optional):
|
|
328
363
|
- `autoReconnect` (boolean): Enable automatic reconnection (default: true)
|
|
329
364
|
- `reconnectDelay` (number): Delay between reconnection attempts in ms (default: 3000)
|
|
@@ -382,20 +417,25 @@ Response:
|
|
|
382
417
|
|
|
383
418
|
### Message Flow
|
|
384
419
|
|
|
385
|
-
1. Client connects via
|
|
420
|
+
1. Client connects via HTTPS (Cloudflare DO HTTP polling)
|
|
386
421
|
2. Server/Durable Object assigns a unique client ID
|
|
387
422
|
3. Client sends join message with session ID
|
|
388
423
|
4. Server broadcasts `peer-joined` to other peers in the same session only
|
|
389
424
|
5. Peers exchange WebRTC offers/answers/ICE candidates via the server
|
|
390
425
|
6. Server routes signaling messages to specific peers by target ID (unicast, not broadcast)
|
|
391
426
|
|
|
427
|
+
Notes:
|
|
428
|
+
- Cloudflare signaling uses JSON over HTTPS requests to `/api` (polling).
|
|
429
|
+
- Offers/answers are transmitted as SDP strings (text-only) in the `offer`/`answer` fields.
|
|
430
|
+
- ICE candidates are transmitted as a compact text string: `candidate|sdpMid|sdpMLineIndex`.
|
|
431
|
+
|
|
392
432
|
## Security Considerations
|
|
393
433
|
|
|
394
434
|
This is a basic signaling server suitable for development and testing. For production use, consider:
|
|
395
435
|
|
|
396
436
|
- Adding authentication and authorization
|
|
397
437
|
- Implementing rate limiting
|
|
398
|
-
- Using TLS/
|
|
438
|
+
- Using TLS/HTTPS for encrypted connections
|
|
399
439
|
- Adding room access controls
|
|
400
440
|
- Implementing message validation
|
|
401
441
|
- Monitoring and logging
|
package/client-browser.js
CHANGED
|
@@ -1,265 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* UniWRTC Client - WebRTC Signaling Client Library
|
|
3
|
-
* Browser
|
|
3
|
+
* Browser version (HTTP polling; no WebSockets)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
constructor(serverUrl, options = {}) {
|
|
8
|
-
this.serverUrl = serverUrl;
|
|
9
|
-
this.ws = null;
|
|
10
|
-
this.clientId = null;
|
|
11
|
-
this.sessionId = null;
|
|
12
|
-
this.peers = new Map();
|
|
13
|
-
this._connectedOnce = false;
|
|
14
|
-
this.options = {
|
|
15
|
-
autoReconnect: true,
|
|
16
|
-
reconnectDelay: 3000,
|
|
17
|
-
...options
|
|
18
|
-
};
|
|
19
|
-
this.eventHandlers = {
|
|
20
|
-
'connected': [],
|
|
21
|
-
'disconnected': [],
|
|
22
|
-
'joined': [],
|
|
23
|
-
'peer-joined': [],
|
|
24
|
-
'peer-left': [],
|
|
25
|
-
'offer': [],
|
|
26
|
-
'answer': [],
|
|
27
|
-
'ice-candidate': [],
|
|
28
|
-
'room-list': [],
|
|
29
|
-
'error': []
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
connect() {
|
|
34
|
-
return new Promise((resolve, reject) => {
|
|
35
|
-
try {
|
|
36
|
-
this.ws = new WebSocket(this.serverUrl);
|
|
37
|
-
|
|
38
|
-
this.ws.onopen = () => {
|
|
39
|
-
console.log('Connected to signaling server');
|
|
40
|
-
|
|
41
|
-
// Send custom peer ID if provided
|
|
42
|
-
if (this.options.customPeerId) {
|
|
43
|
-
this.send({
|
|
44
|
-
type: 'set-id',
|
|
45
|
-
customId: this.options.customPeerId
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
this.ws.onmessage = (event) => {
|
|
51
|
-
try {
|
|
52
|
-
const message = JSON.parse(event.data);
|
|
53
|
-
this.handleMessage(message);
|
|
54
|
-
|
|
55
|
-
if (message.type === 'welcome' && !this._connectedOnce) {
|
|
56
|
-
this.clientId = message.clientId;
|
|
57
|
-
this._connectedOnce = true;
|
|
58
|
-
this.emit('connected', { clientId: this.clientId });
|
|
59
|
-
resolve(this.clientId);
|
|
60
|
-
}
|
|
61
|
-
} catch (error) {
|
|
62
|
-
console.error('Error parsing message:', error);
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
this.ws.onclose = () => {
|
|
67
|
-
console.log('Disconnected from signaling server');
|
|
68
|
-
this.emit('disconnected');
|
|
69
|
-
|
|
70
|
-
if (this.options.autoReconnect) {
|
|
71
|
-
setTimeout(() => {
|
|
72
|
-
console.log('Attempting to reconnect...');
|
|
73
|
-
this.connect();
|
|
74
|
-
}, this.options.reconnectDelay);
|
|
75
|
-
}
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
this.ws.onerror = (error) => {
|
|
79
|
-
console.error('WebSocket error:', error);
|
|
80
|
-
reject(error);
|
|
81
|
-
};
|
|
82
|
-
} catch (error) {
|
|
83
|
-
reject(error);
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
disconnect() {
|
|
89
|
-
if (this.ws) {
|
|
90
|
-
this.options.autoReconnect = false;
|
|
91
|
-
this.ws.close();
|
|
92
|
-
this.ws = null;
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
joinSession(sessionId) {
|
|
97
|
-
// Prevent duplicate join calls for the same session
|
|
98
|
-
if (this.sessionId === sessionId) return;
|
|
99
|
-
this.sessionId = sessionId;
|
|
100
|
-
|
|
101
|
-
// Send join message
|
|
102
|
-
this.send({
|
|
103
|
-
type: 'join',
|
|
104
|
-
sessionId: sessionId,
|
|
105
|
-
peerId: this.clientId
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
leaveSession() {
|
|
110
|
-
if (this.sessionId) {
|
|
111
|
-
this.send({
|
|
112
|
-
type: 'leave',
|
|
113
|
-
sessionId: this.sessionId
|
|
114
|
-
});
|
|
115
|
-
this.sessionId = null;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
send(message) {
|
|
120
|
-
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
121
|
-
this.ws.send(JSON.stringify(message));
|
|
122
|
-
} else {
|
|
123
|
-
console.warn('WebSocket is not connected');
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
sendOffer(offer, targetId) {
|
|
128
|
-
console.log(`[Client] Sending offer to ${targetId}`);
|
|
129
|
-
this.send({
|
|
130
|
-
type: 'offer',
|
|
131
|
-
offer: offer,
|
|
132
|
-
targetId: targetId,
|
|
133
|
-
sessionId: this.sessionId
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
sendAnswer(answer, targetId) {
|
|
138
|
-
console.log(`[Client] Sending answer to ${targetId}`);
|
|
139
|
-
this.send({
|
|
140
|
-
type: 'answer',
|
|
141
|
-
answer: answer,
|
|
142
|
-
targetId: targetId,
|
|
143
|
-
sessionId: this.sessionId
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
sendIceCandidate(candidate, targetId) {
|
|
148
|
-
console.log(`[Client] Sending ICE candidate to ${targetId}`);
|
|
149
|
-
this.send({
|
|
150
|
-
type: 'ice-candidate',
|
|
151
|
-
candidate: candidate,
|
|
152
|
-
targetId: targetId,
|
|
153
|
-
sessionId: this.sessionId
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
listRooms() {
|
|
158
|
-
this.send({
|
|
159
|
-
type: 'list-rooms'
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
on(event, handler) {
|
|
164
|
-
if (this.eventHandlers[event]) {
|
|
165
|
-
this.eventHandlers[event].push(handler);
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
off(event, handler) {
|
|
170
|
-
if (this.eventHandlers[event]) {
|
|
171
|
-
this.eventHandlers[event] = this.eventHandlers[event].filter(h => h !== handler);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
emit(event, data) {
|
|
176
|
-
if (this.eventHandlers[event]) {
|
|
177
|
-
this.eventHandlers[event].forEach(handler => {
|
|
178
|
-
try {
|
|
179
|
-
handler(data);
|
|
180
|
-
} catch (error) {
|
|
181
|
-
console.error(`Error in ${event} handler:`, error);
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
handleMessage(message) {
|
|
188
|
-
switch (message.type) {
|
|
189
|
-
case 'welcome':
|
|
190
|
-
// Only set clientId here; 'connected' is emitted in connect() with a guard
|
|
191
|
-
this.clientId = message.clientId;
|
|
192
|
-
console.log('[UniWRTC] If this helps, consider donating ❤️ → https://coff.ee/draederg');
|
|
193
|
-
break;
|
|
194
|
-
case 'joined':
|
|
195
|
-
this.sessionId = message.sessionId;
|
|
196
|
-
this.emit('joined', {
|
|
197
|
-
sessionId: message.sessionId,
|
|
198
|
-
peerId: message.peerId,
|
|
199
|
-
clientId: message.clientId,
|
|
200
|
-
clients: message.clients
|
|
201
|
-
});
|
|
202
|
-
break;
|
|
203
|
-
case 'peer-joined':
|
|
204
|
-
this.emit('peer-joined', {
|
|
205
|
-
sessionId: message.sessionId,
|
|
206
|
-
peerId: message.peerId
|
|
207
|
-
});
|
|
208
|
-
break;
|
|
209
|
-
case 'peer-left':
|
|
210
|
-
this.emit('peer-left', {
|
|
211
|
-
sessionId: message.sessionId,
|
|
212
|
-
peerId: message.peerId
|
|
213
|
-
});
|
|
214
|
-
break;
|
|
215
|
-
case 'offer':
|
|
216
|
-
console.log(`[Client] Received offer from ${message.peerId}`);
|
|
217
|
-
this.emit('offer', {
|
|
218
|
-
peerId: message.peerId,
|
|
219
|
-
offer: message.offer
|
|
220
|
-
});
|
|
221
|
-
break;
|
|
222
|
-
case 'answer':
|
|
223
|
-
console.log(`[Client] Received answer from ${message.peerId}`);
|
|
224
|
-
this.emit('answer', {
|
|
225
|
-
peerId: message.peerId,
|
|
226
|
-
answer: message.answer
|
|
227
|
-
});
|
|
228
|
-
break;
|
|
229
|
-
case 'ice-candidate':
|
|
230
|
-
console.log(`[Client] Received ICE candidate from ${message.peerId}`);
|
|
231
|
-
this.emit('ice-candidate', {
|
|
232
|
-
peerId: message.peerId,
|
|
233
|
-
candidate: message.candidate
|
|
234
|
-
});
|
|
235
|
-
break;
|
|
236
|
-
case 'room-list':
|
|
237
|
-
this.emit('room-list', {
|
|
238
|
-
rooms: message.rooms
|
|
239
|
-
});
|
|
240
|
-
break;
|
|
241
|
-
case 'error':
|
|
242
|
-
this.emit('error', {
|
|
243
|
-
message: message.message
|
|
244
|
-
});
|
|
245
|
-
break;
|
|
246
|
-
case 'chat':
|
|
247
|
-
this.emit('chat', {
|
|
248
|
-
text: message.text,
|
|
249
|
-
peerId: message.peerId,
|
|
250
|
-
sessionId: message.sessionId
|
|
251
|
-
});
|
|
252
|
-
break;
|
|
253
|
-
default:
|
|
254
|
-
console.log('Unknown message type:', message.type);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}
|
|
6
|
+
import UniWRTCClient from './src/client-cloudflare.js';
|
|
258
7
|
|
|
259
8
|
// Attach to window for non-module script usage
|
|
260
9
|
if (typeof window !== 'undefined') {
|
|
261
10
|
window.UniWRTCClient = UniWRTCClient;
|
|
262
11
|
}
|
|
263
12
|
|
|
264
|
-
// ESM default export for bundlers like Vite
|
|
265
13
|
export default UniWRTCClient;
|