uniwrtc 1.0.0
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/.env.example +8 -0
- package/CLOUDFLARE_DEPLOYMENT.md +127 -0
- package/LICENSE +21 -0
- package/QUICKSTART_CLOUDFLARE.md +55 -0
- package/README.md +384 -0
- package/client-browser.js +249 -0
- package/client.js +231 -0
- package/demo.html +731 -0
- package/deploy-cloudflare.bat +113 -0
- package/deploy-cloudflare.sh +100 -0
- package/package-cf.json +16 -0
- package/package.json +26 -0
- package/server.js +350 -0
- package/src/client-cloudflare.js +247 -0
- package/src/index.js +35 -0
- package/src/room.js +211 -0
- package/test.js +62 -0
- package/wrangler.toml +23 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UniWRTC Client - Updated for Cloudflare deployment
|
|
3
|
+
* WebRTC Signaling Client Library
|
|
4
|
+
* Browser-only version
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
class UniWRTCClient {
|
|
8
|
+
constructor(serverUrl, options = {}) {
|
|
9
|
+
// Support both direct URLs and room parameter format
|
|
10
|
+
if (!serverUrl.includes('?')) {
|
|
11
|
+
const roomId = options.roomId || 'default';
|
|
12
|
+
this.serverUrl = serverUrl + (serverUrl.endsWith('/') ? '' : '/') + `signaling?room=${roomId}`;
|
|
13
|
+
} else {
|
|
14
|
+
this.serverUrl = serverUrl;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
this.ws = null;
|
|
18
|
+
this.clientId = null;
|
|
19
|
+
this.roomId = options.roomId || 'default';
|
|
20
|
+
this.peers = new Map();
|
|
21
|
+
this._connectedOnce = false;
|
|
22
|
+
this.options = {
|
|
23
|
+
autoReconnect: true,
|
|
24
|
+
reconnectDelay: 3000,
|
|
25
|
+
...options
|
|
26
|
+
};
|
|
27
|
+
this.eventHandlers = {
|
|
28
|
+
'connected': [],
|
|
29
|
+
'disconnected': [],
|
|
30
|
+
'joined': [],
|
|
31
|
+
'peer-joined': [],
|
|
32
|
+
'peer-left': [],
|
|
33
|
+
'offer': [],
|
|
34
|
+
'answer': [],
|
|
35
|
+
'ice-candidate': [],
|
|
36
|
+
'room-list': [],
|
|
37
|
+
'error': []
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
connect() {
|
|
42
|
+
return new Promise((resolve, reject) => {
|
|
43
|
+
try {
|
|
44
|
+
// Convert https to wss, http to ws
|
|
45
|
+
let wsUrl = this.serverUrl;
|
|
46
|
+
if (wsUrl.startsWith('https://')) {
|
|
47
|
+
wsUrl = 'wss://' + wsUrl.substring(8);
|
|
48
|
+
} else if (wsUrl.startsWith('http://')) {
|
|
49
|
+
wsUrl = 'ws://' + wsUrl.substring(7);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
this.ws = new WebSocket(wsUrl);
|
|
53
|
+
|
|
54
|
+
this.ws.onopen = () => {
|
|
55
|
+
console.log('Connected to signaling server');
|
|
56
|
+
|
|
57
|
+
// Send custom peer ID if provided
|
|
58
|
+
if (this.options.customPeerId) {
|
|
59
|
+
this.send({
|
|
60
|
+
type: 'set-id',
|
|
61
|
+
customId: this.options.customPeerId
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
this.ws.onmessage = (event) => {
|
|
67
|
+
try {
|
|
68
|
+
const message = JSON.parse(event.data);
|
|
69
|
+
this.handleMessage(message);
|
|
70
|
+
|
|
71
|
+
if (message.type === 'welcome' && !this._connectedOnce) {
|
|
72
|
+
this.clientId = message.clientId;
|
|
73
|
+
this._connectedOnce = true;
|
|
74
|
+
this.emit('connected', { clientId: this.clientId });
|
|
75
|
+
resolve(this.clientId);
|
|
76
|
+
}
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.error('Error parsing message:', error);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
this.ws.onclose = () => {
|
|
83
|
+
console.log('Disconnected from signaling server');
|
|
84
|
+
this.emit('disconnected');
|
|
85
|
+
|
|
86
|
+
if (this.options.autoReconnect) {
|
|
87
|
+
setTimeout(() => {
|
|
88
|
+
console.log('Attempting to reconnect...');
|
|
89
|
+
this.connect();
|
|
90
|
+
}, this.options.reconnectDelay);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
this.ws.onerror = (error) => {
|
|
95
|
+
console.error('WebSocket error:', error);
|
|
96
|
+
reject(error);
|
|
97
|
+
};
|
|
98
|
+
} catch (error) {
|
|
99
|
+
reject(error);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
disconnect() {
|
|
105
|
+
if (this.ws) {
|
|
106
|
+
this.options.autoReconnect = false;
|
|
107
|
+
this.ws.close();
|
|
108
|
+
this.ws = null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
joinRoom(roomId) {
|
|
113
|
+
this.roomId = roomId;
|
|
114
|
+
// Durable Objects handle room joining automatically via room parameter
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
leaveRoom() {
|
|
118
|
+
if (this.roomId) {
|
|
119
|
+
this.roomId = null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
send(message) {
|
|
124
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
125
|
+
this.ws.send(JSON.stringify(message));
|
|
126
|
+
} else {
|
|
127
|
+
console.warn('WebSocket is not connected');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
sendOffer(targetId, offer) {
|
|
132
|
+
this.send({
|
|
133
|
+
type: 'offer',
|
|
134
|
+
offer: offer,
|
|
135
|
+
targetId: targetId
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
sendAnswer(targetId, answer) {
|
|
140
|
+
this.send({
|
|
141
|
+
type: 'answer',
|
|
142
|
+
answer: answer,
|
|
143
|
+
targetId: targetId
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
sendIceCandidate(targetId, candidate) {
|
|
148
|
+
this.send({
|
|
149
|
+
type: 'ice-candidate',
|
|
150
|
+
candidate: candidate,
|
|
151
|
+
targetId: targetId
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
listRooms() {
|
|
156
|
+
// Durable Objects don't expose room listing
|
|
157
|
+
console.log('Room listing not available with Durable Objects');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
on(event, handler) {
|
|
161
|
+
if (this.eventHandlers[event]) {
|
|
162
|
+
this.eventHandlers[event].push(handler);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
off(event, handler) {
|
|
167
|
+
if (this.eventHandlers[event]) {
|
|
168
|
+
this.eventHandlers[event] = this.eventHandlers[event].filter(h => h !== handler);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
emit(event, data) {
|
|
173
|
+
if (this.eventHandlers[event]) {
|
|
174
|
+
this.eventHandlers[event].forEach(handler => {
|
|
175
|
+
try {
|
|
176
|
+
handler(data);
|
|
177
|
+
} catch (error) {
|
|
178
|
+
console.error(`Error in ${event} handler:`, error);
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
handleMessage(message) {
|
|
185
|
+
switch (message.type) {
|
|
186
|
+
case 'welcome':
|
|
187
|
+
this.clientId = message.clientId;
|
|
188
|
+
console.log('[UniWRTC] If this helps, consider donating ā¤ļø ā https://coff.ee/draederg');
|
|
189
|
+
break;
|
|
190
|
+
case 'joined':
|
|
191
|
+
this.roomId = message.roomId;
|
|
192
|
+
this.emit('joined', {
|
|
193
|
+
roomId: message.roomId,
|
|
194
|
+
clientId: message.clientId,
|
|
195
|
+
clients: message.clients
|
|
196
|
+
});
|
|
197
|
+
break;
|
|
198
|
+
case 'peer-joined':
|
|
199
|
+
this.emit('peer-joined', {
|
|
200
|
+
clientId: message.clientId
|
|
201
|
+
});
|
|
202
|
+
break;
|
|
203
|
+
case 'peer-left':
|
|
204
|
+
this.emit('peer-left', {
|
|
205
|
+
clientId: message.clientId
|
|
206
|
+
});
|
|
207
|
+
break;
|
|
208
|
+
case 'offer':
|
|
209
|
+
this.emit('offer', {
|
|
210
|
+
peerId: message.peerId,
|
|
211
|
+
offer: message.offer
|
|
212
|
+
});
|
|
213
|
+
break;
|
|
214
|
+
case 'answer':
|
|
215
|
+
this.emit('answer', {
|
|
216
|
+
peerId: message.peerId,
|
|
217
|
+
answer: message.answer
|
|
218
|
+
});
|
|
219
|
+
break;
|
|
220
|
+
case 'ice-candidate':
|
|
221
|
+
this.emit('ice-candidate', {
|
|
222
|
+
peerId: message.peerId,
|
|
223
|
+
candidate: message.candidate
|
|
224
|
+
});
|
|
225
|
+
break;
|
|
226
|
+
case 'room-list':
|
|
227
|
+
this.emit('room-list', {
|
|
228
|
+
rooms: message.rooms
|
|
229
|
+
});
|
|
230
|
+
break;
|
|
231
|
+
case 'error':
|
|
232
|
+
this.emit('error', {
|
|
233
|
+
message: message.message
|
|
234
|
+
});
|
|
235
|
+
break;
|
|
236
|
+
case 'chat':
|
|
237
|
+
this.emit('chat', {
|
|
238
|
+
text: message.text,
|
|
239
|
+
senderId: message.senderId,
|
|
240
|
+
roomId: message.roomId
|
|
241
|
+
});
|
|
242
|
+
break;
|
|
243
|
+
default:
|
|
244
|
+
console.log('Unknown message type:', message.type);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UniWRTC Cloudflare Worker
|
|
3
|
+
* WebRTC Signaling Service using Durable Objects
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { Room } from './room.js';
|
|
7
|
+
|
|
8
|
+
export { Room };
|
|
9
|
+
|
|
10
|
+
export default {
|
|
11
|
+
async fetch(request, env) {
|
|
12
|
+
const url = new URL(request.url);
|
|
13
|
+
|
|
14
|
+
// Health check
|
|
15
|
+
if (url.pathname === '/health') {
|
|
16
|
+
return new Response(JSON.stringify({ status: 'ok' }), {
|
|
17
|
+
headers: { 'Content-Type': 'application/json' }
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// WebSocket signaling
|
|
22
|
+
if (url.pathname === '/signaling' || url.pathname === '/') {
|
|
23
|
+
const roomId = url.searchParams.get('room') || 'default';
|
|
24
|
+
|
|
25
|
+
// Get Durable Object stub for this room
|
|
26
|
+
const id = env.ROOMS.idFromName(roomId);
|
|
27
|
+
const roomStub = env.ROOMS.get(id);
|
|
28
|
+
|
|
29
|
+
// Forward request to Durable Object
|
|
30
|
+
return roomStub.fetch(request);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return new Response('Not Found', { status: 404 });
|
|
34
|
+
}
|
|
35
|
+
};
|
package/src/room.js
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Durable Object for WebRTC Signaling Room
|
|
3
|
+
* Manages peers in a room and routes signaling messages
|
|
4
|
+
*/
|
|
5
|
+
export class Room {
|
|
6
|
+
constructor(state, env) {
|
|
7
|
+
this.state = state;
|
|
8
|
+
this.env = env;
|
|
9
|
+
this.clients = new Map(); // Map of clientId -> WebSocket
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async fetch(request) {
|
|
13
|
+
if (request.headers.get('Upgrade') === 'websocket') {
|
|
14
|
+
const [client, server] = Object.values(new WebSocketPair());
|
|
15
|
+
server.accept();
|
|
16
|
+
|
|
17
|
+
const clientId = crypto.randomUUID().substring(0, 9);
|
|
18
|
+
this.clients.set(clientId, server);
|
|
19
|
+
|
|
20
|
+
console.log(`[Room] Client ${clientId} joined (total: ${this.clients.size})`);
|
|
21
|
+
|
|
22
|
+
// Send welcome message
|
|
23
|
+
server.send(JSON.stringify({
|
|
24
|
+
type: 'welcome',
|
|
25
|
+
clientId: clientId,
|
|
26
|
+
message: 'Connected to UniWRTC signaling room'
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
// Notify existing clients
|
|
30
|
+
this.broadcast({
|
|
31
|
+
type: 'peer-joined',
|
|
32
|
+
clientId: clientId
|
|
33
|
+
}, clientId);
|
|
34
|
+
|
|
35
|
+
server.onmessage = async (event) => {
|
|
36
|
+
try {
|
|
37
|
+
const message = JSON.parse(event.data);
|
|
38
|
+
await this.handleMessage(clientId, message);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error('[Room] Message error:', error);
|
|
41
|
+
server.send(JSON.stringify({ type: 'error', message: error.message }));
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
server.onclose = () => {
|
|
46
|
+
console.log(`[Room] Client ${clientId} left`);
|
|
47
|
+
this.clients.delete(clientId);
|
|
48
|
+
this.broadcast({
|
|
49
|
+
type: 'peer-left',
|
|
50
|
+
peerId: clientId,
|
|
51
|
+
clientId: clientId
|
|
52
|
+
});
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
server.onerror = (error) => {
|
|
56
|
+
console.error('[Room] WebSocket error:', error);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return new Response(null, { status: 101, webSocket: client });
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return new Response('Not a WebSocket request', { status: 400 });
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async handleMessage(clientId, message) {
|
|
66
|
+
switch (message.type) {
|
|
67
|
+
case 'join':
|
|
68
|
+
await this.handleJoin(clientId, message);
|
|
69
|
+
break;
|
|
70
|
+
case 'set-id':
|
|
71
|
+
await this.handleSetId(clientId, message);
|
|
72
|
+
break;
|
|
73
|
+
case 'offer':
|
|
74
|
+
case 'answer':
|
|
75
|
+
case 'ice-candidate':
|
|
76
|
+
await this.handleSignaling(clientId, message);
|
|
77
|
+
break;
|
|
78
|
+
default:
|
|
79
|
+
console.log(`[Room] Unknown message type: ${message.type}`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async handleJoin(clientId, message) {
|
|
84
|
+
const { roomId, peerId } = message;
|
|
85
|
+
|
|
86
|
+
// Get list of other peers
|
|
87
|
+
const peers = Array.from(this.clients.keys())
|
|
88
|
+
.filter(id => id !== clientId);
|
|
89
|
+
|
|
90
|
+
const client = this.clients.get(clientId);
|
|
91
|
+
if (client && client.readyState === WebSocket.OPEN) {
|
|
92
|
+
// Send joined confirmation
|
|
93
|
+
client.send(JSON.stringify({
|
|
94
|
+
type: 'joined',
|
|
95
|
+
roomId: roomId,
|
|
96
|
+
peerId: peerId || clientId,
|
|
97
|
+
clients: peers
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Notify other peers
|
|
102
|
+
this.broadcast({
|
|
103
|
+
type: 'peer-joined',
|
|
104
|
+
peerId: peerId || clientId,
|
|
105
|
+
clientId: clientId
|
|
106
|
+
}, clientId);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async handleSetId(clientId, message) {
|
|
110
|
+
const { customId } = message;
|
|
111
|
+
|
|
112
|
+
if (!customId || customId.length < 3 || customId.length > 20) {
|
|
113
|
+
const client = this.clients.get(clientId);
|
|
114
|
+
if (client && client.readyState === WebSocket.OPEN) {
|
|
115
|
+
client.send(JSON.stringify({
|
|
116
|
+
type: 'error',
|
|
117
|
+
message: 'Custom ID must be 3-20 characters'
|
|
118
|
+
}));
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Check if ID is already taken
|
|
124
|
+
let idTaken = false;
|
|
125
|
+
for (const [id, ws] of this.clients) {
|
|
126
|
+
if (id !== clientId && id === customId) {
|
|
127
|
+
idTaken = true;
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (idTaken) {
|
|
133
|
+
const client = this.clients.get(clientId);
|
|
134
|
+
if (client && client.readyState === WebSocket.OPEN) {
|
|
135
|
+
client.send(JSON.stringify({
|
|
136
|
+
type: 'error',
|
|
137
|
+
message: 'Peer ID already taken'
|
|
138
|
+
}));
|
|
139
|
+
}
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Update client ID
|
|
144
|
+
const ws = this.clients.get(clientId);
|
|
145
|
+
this.clients.delete(clientId);
|
|
146
|
+
this.clients.set(customId, ws);
|
|
147
|
+
|
|
148
|
+
console.log(`[Room] Client changed ID from ${clientId} to ${customId}`);
|
|
149
|
+
|
|
150
|
+
// Send confirmation
|
|
151
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
152
|
+
ws.send(JSON.stringify({
|
|
153
|
+
type: 'welcome',
|
|
154
|
+
clientId: customId,
|
|
155
|
+
message: 'Custom peer ID set'
|
|
156
|
+
}));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Notify others of ID change
|
|
160
|
+
this.broadcast({
|
|
161
|
+
type: 'peer-id-changed',
|
|
162
|
+
oldId: clientId,
|
|
163
|
+
newId: customId
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
async handleSignaling(clientId, message) {
|
|
168
|
+
const { targetId, type, offer, answer, candidate } = message;
|
|
169
|
+
|
|
170
|
+
if (!targetId) {
|
|
171
|
+
console.log(`[Room] Signaling without target`);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const targetClient = this.clients.get(targetId);
|
|
176
|
+
if (!targetClient || targetClient.readyState !== WebSocket.OPEN) {
|
|
177
|
+
console.log(`[Room] Target client ${targetId} not found or closed`);
|
|
178
|
+
const client = this.clients.get(clientId);
|
|
179
|
+
if (client && client.readyState === WebSocket.OPEN) {
|
|
180
|
+
client.send(JSON.stringify({
|
|
181
|
+
type: 'error',
|
|
182
|
+
message: `Target peer ${targetId} not found`
|
|
183
|
+
}));
|
|
184
|
+
}
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
console.log(`[Room] Routing ${type} from ${clientId} to ${targetId}`);
|
|
189
|
+
|
|
190
|
+
// Route signaling message to target
|
|
191
|
+
const forwardMessage = {
|
|
192
|
+
type: type,
|
|
193
|
+
peerId: clientId
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
if (offer) forwardMessage.offer = offer;
|
|
197
|
+
if (answer) forwardMessage.answer = answer;
|
|
198
|
+
if (candidate) forwardMessage.candidate = candidate;
|
|
199
|
+
|
|
200
|
+
targetClient.send(JSON.stringify(forwardMessage));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
broadcast(message, excludeClientId = null) {
|
|
204
|
+
const payload = JSON.stringify(message);
|
|
205
|
+
for (const [clientId, client] of this.clients) {
|
|
206
|
+
if (clientId !== excludeClientId && client.readyState === WebSocket.OPEN) {
|
|
207
|
+
client.send(payload);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
package/test.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Simple test script for UniWRTC
|
|
5
|
+
* Runs two clients and tests peer connections
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { UniWRTCClient } = require('./client.js');
|
|
9
|
+
|
|
10
|
+
async function sleep(ms) {
|
|
11
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function runTest() {
|
|
15
|
+
console.log('š Starting UniWRTC Test...\n');
|
|
16
|
+
|
|
17
|
+
// Create two clients
|
|
18
|
+
const client1 = new UniWRTCClient('ws://localhost:8080');
|
|
19
|
+
const client2 = new UniWRTCClient('ws://localhost:8080');
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
// Connect first client
|
|
23
|
+
console.log('š± Connecting Client 1...');
|
|
24
|
+
const id1 = await client1.connect();
|
|
25
|
+
console.log(`ā
Client 1 connected with ID: ${id1}\n`);
|
|
26
|
+
|
|
27
|
+
await sleep(500);
|
|
28
|
+
|
|
29
|
+
// Connect second client
|
|
30
|
+
console.log('š± Connecting Client 2...');
|
|
31
|
+
const id2 = await client2.connect();
|
|
32
|
+
console.log(`ā
Client 2 connected with ID: ${id2}\n`);
|
|
33
|
+
|
|
34
|
+
await sleep(500);
|
|
35
|
+
|
|
36
|
+
// Join same room
|
|
37
|
+
console.log('š Client 1 joining room: test-room');
|
|
38
|
+
client1.joinRoom('test-room');
|
|
39
|
+
|
|
40
|
+
await sleep(500);
|
|
41
|
+
|
|
42
|
+
console.log('š Client 2 joining room: test-room');
|
|
43
|
+
client2.joinRoom('test-room');
|
|
44
|
+
|
|
45
|
+
await sleep(1000);
|
|
46
|
+
|
|
47
|
+
console.log('\n⨠Test complete! Both clients joined the room.');
|
|
48
|
+
console.log('š Peers in Client 1:', Array.from(client1.peers.keys()));
|
|
49
|
+
console.log('š Peers in Client 2:', Array.from(client2.peers.keys()));
|
|
50
|
+
|
|
51
|
+
// Cleanup
|
|
52
|
+
client1.disconnect();
|
|
53
|
+
client2.disconnect();
|
|
54
|
+
|
|
55
|
+
process.exit(0);
|
|
56
|
+
} catch (error) {
|
|
57
|
+
console.error('ā Test failed:', error.message);
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
runTest();
|
package/wrangler.toml
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
name = "uniwrtc"
|
|
2
|
+
main = "src/index.js"
|
|
3
|
+
compatibility_date = "2024-12-20"
|
|
4
|
+
|
|
5
|
+
[[migrations]]
|
|
6
|
+
tag = "v1"
|
|
7
|
+
new_sqlite_classes = ["Room"]
|
|
8
|
+
|
|
9
|
+
[[durable_objects.bindings]]
|
|
10
|
+
name = "ROOMS"
|
|
11
|
+
class_name = "Room"
|
|
12
|
+
|
|
13
|
+
[env.production]
|
|
14
|
+
routes = [
|
|
15
|
+
{ pattern = "signal.peer.ooo/*", zone_name = "peer.ooo" }
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[[env.production.durable_objects.bindings]]
|
|
19
|
+
name = "ROOMS"
|
|
20
|
+
class_name = "Room"
|
|
21
|
+
|
|
22
|
+
[build]
|
|
23
|
+
command = "npm install"
|