uniwrtc 1.1.0 → 1.2.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.
@@ -1,126 +1,32 @@
1
1
  # UniWRTC Cloudflare Deployment Guide
2
2
 
3
- ## Prerequisites
3
+ ## Deploy Without Durable Objects (Cloudflare Pages)
4
4
 
5
- 1. **Cloudflare Account** - Free tier is sufficient
6
- 2. **Wrangler CLI** - Install: `npm install -g wrangler`
7
- 3. **Node.js** - v16 or higher
8
- 4. **Your Domain** - Domain must be on Cloudflare (e.g., `peer.ooo`)
9
-
10
- ## Setup Steps
11
-
12
- ### 1. Install Wrangler
13
-
14
- ```bash
15
- npm install -g wrangler
16
- ```
17
-
18
- ### 2. Authenticate with Cloudflare
19
-
20
- ```bash
21
- wrangler login
22
- ```
23
-
24
- This will open your browser to authorize the CLI.
25
-
26
- ### 3. Update wrangler.toml
27
-
28
- Replace the zone configuration with your domain:
29
-
30
- ```toml
31
- [env.production]
32
- routes = [
33
- { pattern = "signal.peer.ooo/*", zone_name = "peer.ooo" }
34
- ]
35
- ```
36
-
37
- ### 4. Deploy to Cloudflare
38
-
39
- ```bash
40
- # Deploy to production
41
- wrangler publish --env production
42
-
43
- # Or deploy to staging first
44
- wrangler publish
45
- ```
46
-
47
- ### 5. Access Your Signaling Server
48
-
49
- Your server will be available at:
50
- - **Production**: `https://signal.peer.ooo/`
51
- - **Development**: `https://uniwrtc.<subdomain>.workers.dev/`
5
+ If you only want to host the demo UI (static assets) and do NOT want Durable Objects, deploy the Vite build to Cloudflare Pages.
52
6
 
53
- ## Using with UniWRTC Demo
7
+ ### Prerequisites
54
8
 
55
- Update the demo to use your Cloudflare endpoint:
56
-
57
- ```javascript
58
- // In demo.html or client
59
- const serverUrl = 'https://signal.peer.ooo/';
60
- const roomId = 'my-room';
61
-
62
- const client = new UniWRTCClient(serverUrl, {
63
- roomId: roomId,
64
- customPeerId: 'optional-id'
65
- });
66
-
67
- await client.connect();
68
- ```
69
-
70
- ## How It Works
71
-
72
- 1. **Durable Objects** - One per room, manages peer discovery
73
- 2. **HTTP polling** - Browsers connect for signaling
74
- 3. **Signaling Only** - Offers/answers/ICE via Worker
75
- 4. **P2P Data** - WebRTC data channels bypass Cloudflare
76
- 5. **Free Tier** - Plenty of capacity for small deployments
77
-
78
- ## Cost
79
-
80
- - **Requests**: 100,000 free per day (signaling only)
81
- - **Compute**: Included in free tier
82
- - **Durable Objects**: ~$0.15/million operations (minimal for signaling)
83
- - **Total**: Free to very low cost
84
-
85
- ## Monitoring
86
-
87
- Check deployment status:
88
-
89
- ```bash
90
- wrangler tail --env production
91
- ```
92
-
93
- View real-time logs from your Worker.
94
-
95
- ## Local Development
9
+ 1. **Cloudflare Account** - Free tier is sufficient
10
+ 2. **Wrangler CLI** - Install: `npm install -g wrangler` (or use `npx`)
11
+ 3. **Node.js** - v16 or higher
96
12
 
97
- Test locally before deploying:
13
+ ### Deploy
98
14
 
99
15
  ```bash
100
- wrangler dev
16
+ npm install
17
+ npm run deploy:cf:no-do
101
18
  ```
102
19
 
103
- Your local server will run at `http://localhost:8787`
104
-
105
- Update demo to test:
106
- ```javascript
107
- const serverUrl = 'http://localhost:8787/';
108
- ```
109
-
110
- ## Troubleshooting
111
-
112
- **Connection errors**: Ensure your domain is on Cloudflare with SSL enabled
113
-
114
- **Connection refused**: Check the Worker route pattern in `wrangler.toml`
20
+ Notes:
21
+ - This deploys the `dist/` folder (static hosting).
22
+ - No server routes are deployed; Durable Objects are not used.
115
23
 
116
- **Durable Objects not found**: Run `wrangler publish` with migrations enabled
24
+ ## Custom Domain (signal.peer.ooo)
117
25
 
118
- ## Next Steps
26
+ To serve the Pages project at `https://signal.peer.ooo`:
119
27
 
120
- 1. Deploy the Worker
121
- 2. Update demo.html to use your Cloudflare endpoint
122
- 3. Test with multiple browsers
123
- 4. Scale up!
28
+ 1. Cloudflare Dashboard → Pages → your project → **Custom domains** → add `signal.peer.ooo`
29
+ 2. Cloudflare DNS set `signal` as a CNAME to `<your-pages-project>.pages.dev`
124
30
 
125
31
  ---
126
32
 
@@ -1,8 +1,24 @@
1
- # Quick Start - Deploy to Cloudflare in 30 seconds
1
+ # Quick Start - Deploy to Cloudflare
2
+
3
+ ## Option A (No Durable Objects): Cloudflare Pages (static)
4
+
5
+ This repo’s current demo works client-side (Nostr), so you can deploy just the static site to Cloudflare Pages.
6
+
7
+ ### Prerequisites
8
+ - Cloudflare account (free tier works)
9
+ - Node.js installed
10
+ - Wrangler CLI authenticated (`npx wrangler login`)
11
+
12
+ ### Deploy
13
+ ```bash
14
+ npm install
15
+ npm run deploy:cf:no-do
16
+ ```
17
+
18
+ Wrangler will prompt you to pick/create a Pages project the first time.
2
19
 
3
20
  ## Prerequisites
4
21
  - Cloudflare account (free tier works)
5
- - Your domain on Cloudflare
6
22
  - Node.js installed
7
23
 
8
24
  ## Deploy
@@ -18,38 +34,12 @@ chmod +x deploy-cloudflare.sh
18
34
  deploy-cloudflare.bat
19
35
  ```
20
36
 
21
- ## What the script does:
22
- 1. ✅ Checks Node.js and installs Wrangler
23
- 2. ✅ Authenticates with Cloudflare
24
- 3. ✅ Asks for your domain (e.g., `signal.peer.ooo`)
25
- 4. ✅ Updates `wrangler.toml`
26
- 5. ✅ Deploys to Cloudflare Workers
27
- 6. ✅ Gives you the live URL
37
+ ## What this does
38
+ 1. ✅ Builds the Vite site into `dist/`
39
+ 2. ✅ Deploys `dist/` to Cloudflare Pages
28
40
 
29
41
  ## After deployment:
30
42
 
31
- Update demo.html:
32
- ```javascript
33
- const serverUrl = 'https://signal.peer.ooo/'; // Your domain
34
- ```
35
-
36
- Then reload the demo and it will connect to your Cloudflare Workers signaling server! 🚀
37
-
38
- ## Testing
39
-
40
- Test the server:
41
- ```bash
42
- curl https://signal.peer.ooo/health
43
- ```
44
-
45
- View logs:
46
- ```bash
47
- wrangler tail --env production
48
- ```
49
-
50
- Local development:
51
- ```bash
52
- wrangler dev
53
- ```
43
+ Then set your custom domain in Cloudflare Pages (and point `signal` to `<project>.pages.dev`).
54
44
 
55
- That's it! Your WebRTC signaling is now on Cloudflare! 🎉
45
+ That's it! Your demo is now on Cloudflare Pages (no Durable Objects).
@@ -1,9 +1,10 @@
1
1
  @echo off
2
- REM UniWRTC Cloudflare Automated Setup Script (Windows)
2
+ REM UniWRTC Cloudflare Automated Setup Script (Windows, NO Durable Objects)
3
+ REM Deploys the static demo to Cloudflare Pages.
3
4
 
4
5
  setlocal enabledelayedexpansion
5
6
 
6
- echo 🚀 UniWRTC Cloudflare Setup
7
+ echo 🚀 UniWRTC Cloudflare Setup (Pages / no Durable Objects)
7
8
  echo ============================
8
9
  echo.
9
10
 
@@ -40,74 +41,29 @@ if errorlevel 1 (
40
41
  echo ✅ Authenticated with Cloudflare
41
42
  echo.
42
43
 
43
- REM Ask for domain
44
- echo 🌐 Domain Configuration
45
- echo =====================
46
- set /p DOMAIN="Enter your Cloudflare domain (e.g., peer.ooo): "
47
- set /p SUBDOMAIN="Enter subdomain for signaling (e.g., signal): "
44
+ REM Project name
45
+ set PROJECT_NAME=signal-peer-ooo
46
+ if not "%~1"=="" set PROJECT_NAME=%~1
48
47
 
49
- if "!DOMAIN!"=="" (
50
- echo Domain required
51
- exit /b 1
52
- )
53
-
54
- if "!SUBDOMAIN!"=="" (
55
- echo ❌ Subdomain required
56
- exit /b 1
57
- )
58
-
59
- set FULL_DOMAIN=!SUBDOMAIN!.!DOMAIN!
60
-
61
- REM Update wrangler.toml
62
- echo 📝 Updating wrangler.toml...
63
- (
64
- echo name = "uniwrtc"
65
- echo main = "src/index.js"
66
- echo compatibility_date = "2024-12-20"
67
- echo.
68
- echo [env.production]
69
- echo routes = [
70
- echo { pattern = "!FULL_DOMAIN!/*", zone_name = "!DOMAIN!" }
71
- echo ]
72
- echo.
73
- echo [[durable_objects.bindings]]
74
- echo name = "ROOMS"
75
- echo class_name = "Room"
76
- echo.
77
- echo [durable_objects]
78
- echo migrations = [
79
- echo { tag = "v1", new_classes = ["Room"] }
80
- echo ]
81
- echo.
82
- echo [build]
83
- echo command = "npm install"
84
- ) > wrangler.toml
48
+ echo 📦 Building static site...
49
+ call npm run build
85
50
 
86
- echo wrangler.toml updated
51
+ echo 🚀 Deploying to Cloudflare Pages project: %PROJECT_NAME%
87
52
  echo.
88
53
 
54
+ REM Create project if needed (ignore errors)
55
+ call npx wrangler pages project create %PROJECT_NAME% --production-branch main >nul 2>nul
56
+
89
57
  REM Deploy
90
- echo 🚀 Deploying to Cloudflare...
91
- echo.
92
- call wrangler deploy --env production
58
+ call npx wrangler pages deploy dist --project-name %PROJECT_NAME%
93
59
 
94
60
  echo.
95
61
  echo ✅ Deployment Complete!
96
62
  echo.
97
- echo 🎉 Your UniWRTC signaling server is live at:
98
- echo https://!FULL_DOMAIN!/
99
- echo.
100
- echo 📊 Test it:
101
- echo curl https://!FULL_DOMAIN!/health
102
- echo.
103
- echo 🧪 Local testing:
104
- echo wrangler dev
105
- echo.
106
- echo 📊 View logs:
107
- echo wrangler tail --env production
108
- echo.
109
- echo 🛠️ Next: Update demo.html to use:
110
- echo const serverUrl = 'https://!FULL_DOMAIN!/';
63
+ echo 🎉 Your Pages site is deployed.
64
+ echo Next step for custom domain (manual in Cloudflare UI):
65
+ echo - Pages ^> %PROJECT_NAME% ^> Custom domains ^> add signal.peer.ooo
66
+ echo - DNS: CNAME signal ^> %PROJECT_NAME%.pages.dev
111
67
  echo.
112
68
 
113
69
  endlocal
@@ -1,11 +1,11 @@
1
1
  #!/bin/bash
2
2
 
3
- # UniWRTC Cloudflare Automated Setup Script
4
- # Run this to setup and deploy to Cloudflare
3
+ # UniWRTC Cloudflare Automated Setup Script (NO Durable Objects)
4
+ # Deploys the static demo to Cloudflare Pages.
5
5
 
6
6
  set -e
7
7
 
8
- echo "🚀 UniWRTC Cloudflare Setup"
8
+ echo "🚀 UniWRTC Cloudflare Setup (Pages / no Durable Objects)"
9
9
  echo "============================"
10
10
  echo ""
11
11
 
@@ -41,67 +41,24 @@ fi
41
41
  echo "✅ Authenticated with Cloudflare"
42
42
  echo ""
43
43
 
44
- # Domain configuration
45
- DOMAIN="peer.ooo"
46
- SUBDOMAIN="signal"
44
+ PROJECT_NAME=${1:-"signal-peer-ooo"}
47
45
 
48
- FULL_DOMAIN="${SUBDOMAIN}.${DOMAIN}"
46
+ echo "📦 Building static site..."
47
+ npm run build
49
48
 
50
- echo "📝 Updating wrangler.toml..."
51
- cat > wrangler.toml << EOF
52
- name = "uniwrtc"
53
- main = "src/index.js"
54
- compatibility_date = "2024-12-20"
49
+ echo "🚀 Deploying to Cloudflare Pages project: ${PROJECT_NAME}"
55
50
 
56
- assets = { directory = "./dist", binding = "ASSETS" }
51
+ # Create the project if it doesn't exist (ignore error if it already exists)
52
+ npx wrangler pages project create "${PROJECT_NAME}" --production-branch main 2>/dev/null || true
57
53
 
58
- [[durable_objects.bindings]]
59
- name = "ROOMS"
60
- class_name = "Room"
61
-
62
- [[migrations]]
63
- tag = "v1"
64
- new_classes = ["Room"]
65
-
66
- [env.production]
67
- routes = [
68
- { pattern = "${FULL_DOMAIN}/*", zone_name = "${DOMAIN}" }
69
- ]
70
-
71
- assets = { directory = "./dist", binding = "ASSETS" }
72
-
73
- [[env.production.durable_objects.bindings]]
74
- name = "ROOMS"
75
- class_name = "Room"
76
-
77
- [build]
78
- command = "npm install"
79
- EOF
80
-
81
- echo "✅ wrangler.toml updated"
82
- echo ""
83
-
84
- # Deploy
85
- echo "🚀 Deploying to Cloudflare..."
86
- echo ""
87
- echo "Deploying to production..."
88
- wrangler deploy --env production
54
+ # Deploy the built assets
55
+ npx wrangler pages deploy dist --project-name "${PROJECT_NAME}"
89
56
 
90
57
  echo ""
91
58
  echo "✅ Deployment Complete!"
92
59
  echo ""
93
- echo "🎉 Your UniWRTC signaling server is live at:"
94
- echo " https://${FULL_DOMAIN}/"
95
- echo ""
96
- echo "📊 Test it:"
97
- echo " curl https://${FULL_DOMAIN}/health"
98
- echo ""
99
- echo "🧪 Local testing:"
100
- echo " wrangler dev"
101
- echo ""
102
- echo "📊 View logs:"
103
- echo " wrangler tail --env production"
104
- echo ""
105
- echo "🛠️ Next: Update demo.html to use:"
106
- echo " const serverUrl = 'https://${FULL_DOMAIN}/';"
60
+ echo "🎉 Your Pages site is deployed."
61
+ echo "Next step for custom domain (manual in Cloudflare UI):"
62
+ echo " - Pages → ${PROJECT_NAME} → Custom domains → add signal.peer.ooo"
63
+ echo " - DNS: CNAME signal → ${PROJECT_NAME}.pages.dev"
107
64
  echo ""
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uniwrtc",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "A universal WebRTC signaling service",
5
5
  "main": "server.js",
6
6
  "type": "module",
@@ -9,6 +9,8 @@
9
9
  "dev": "vite",
10
10
  "build": "vite build",
11
11
  "preview": "vite preview",
12
+ "deploy:cf:pages": "npm run build && npx wrangler pages deploy dist",
13
+ "deploy:cf:no-do": "npm run deploy:cf:pages",
12
14
  "server": "node server.js",
13
15
  "test": "node test.js"
14
16
  },
package/src/main.js CHANGED
@@ -7,6 +7,7 @@ window.UniWRTCClient = UniWRTCClient;
7
7
 
8
8
  // Nostr is the default transport (no toggle)
9
9
  let nostrClient = null;
10
+ let myPeerId = null;
10
11
 
11
12
  let client = null;
12
13
  const peerConnections = new Map();
@@ -154,6 +155,31 @@ window.connect = async function() {
154
155
  await connectNostr();
155
156
  };
156
157
 
158
+ function shouldInitiateWith(peerId) {
159
+ // Deterministic initiator to avoid offer glare
160
+ if (!myPeerId) return false;
161
+ return myPeerId.localeCompare(peerId) < 0;
162
+ }
163
+
164
+ function sendSignal(to, payload) {
165
+ if (!nostrClient) throw new Error('Not connected to Nostr');
166
+ return nostrClient.send({
167
+ ...payload,
168
+ to,
169
+ });
170
+ }
171
+
172
+ async function ensurePeerConnection(peerId) {
173
+ if (!peerId || peerId === myPeerId) return null;
174
+ if (peerConnections.has(peerId) && peerConnections.get(peerId) instanceof RTCPeerConnection) {
175
+ return peerConnections.get(peerId);
176
+ }
177
+
178
+ const initiator = shouldInitiateWith(peerId);
179
+ const pc = await createPeerConnection(peerId, initiator);
180
+ return pc;
181
+ }
182
+
157
183
  async function connectNostr() {
158
184
  const relayUrl = document.getElementById('relayUrl').value.trim();
159
185
  const roomIdInput = document.getElementById('roomId');
@@ -182,22 +208,65 @@ async function connectNostr() {
182
208
  if (state === 'connected') updateStatus(true);
183
209
  if (state === 'disconnected') updateStatus(false);
184
210
  },
185
- onMessage: ({ from, text }) => {
186
- displayChatMessage(text, from, false);
187
- },
188
- onPeer: ({ peerId }) => {
211
+ onPayload: async ({ from, payload }) => {
212
+ const peerId = from;
213
+ if (!peerId || peerId === myPeerId) return;
214
+
189
215
  // Use the existing peer list UI as a simple "seen peers" list
190
216
  if (!peerConnections.has(peerId)) {
191
217
  peerConnections.set(peerId, null);
192
218
  updatePeerList();
193
219
  log(`Peer seen: ${peerId.substring(0, 6)}...`, 'success');
194
220
  }
195
- }
221
+
222
+ if (!payload || typeof payload !== 'object') return;
223
+
224
+ // Presence
225
+ if (payload.type === 'hello') {
226
+ await ensurePeerConnection(peerId);
227
+ return;
228
+ }
229
+
230
+ // Signaling messages are always targeted
231
+ if (payload.to && payload.to !== myPeerId) return;
232
+
233
+ if (payload.type === 'signal-offer' && payload.sdp) {
234
+ log(`Received offer from ${peerId.substring(0, 6)}...`, 'info');
235
+ const pc = await ensurePeerConnection(peerId);
236
+ await pc.setRemoteDescription(new RTCSessionDescription(payload.sdp));
237
+ const answer = await pc.createAnswer();
238
+ await pc.setLocalDescription(answer);
239
+ await sendSignal(peerId, { type: 'signal-answer', sdp: pc.localDescription });
240
+ log(`Sent answer to ${peerId.substring(0, 6)}...`, 'success');
241
+ return;
242
+ }
243
+
244
+ if (payload.type === 'signal-answer' && payload.sdp) {
245
+ log(`Received answer from ${peerId.substring(0, 6)}...`, 'info');
246
+ const pc = peerConnections.get(peerId);
247
+ if (pc instanceof RTCPeerConnection) {
248
+ await pc.setRemoteDescription(new RTCSessionDescription(payload.sdp));
249
+ }
250
+ return;
251
+ }
252
+
253
+ if (payload.type === 'signal-ice' && payload.candidate) {
254
+ const pc = peerConnections.get(peerId);
255
+ if (pc instanceof RTCPeerConnection) {
256
+ try {
257
+ await pc.addIceCandidate(new RTCIceCandidate(payload.candidate));
258
+ } catch (e) {
259
+ log(`Failed to add ICE candidate: ${e?.message || e}`, 'warning');
260
+ }
261
+ }
262
+ }
263
+ },
196
264
  });
197
265
 
198
266
  await nostrClient.connect();
199
267
 
200
268
  const myPubkey = nostrClient.getPublicKey();
269
+ myPeerId = myPubkey;
201
270
  document.getElementById('clientId').textContent = myPubkey.substring(0, 16) + '...';
202
271
  document.getElementById('sessionId').textContent = effectiveRoom;
203
272
  log(`Joined Nostr room: ${effectiveRoom}`, 'success');
@@ -336,6 +405,13 @@ window.disconnect = function() {
336
405
  if (nostrClient) {
337
406
  nostrClient.disconnect().catch(() => {});
338
407
  nostrClient = null;
408
+ myPeerId = null;
409
+ peerConnections.forEach((pc) => {
410
+ if (pc instanceof RTCPeerConnection) pc.close();
411
+ });
412
+ peerConnections.clear();
413
+ dataChannels.clear();
414
+ updatePeerList();
339
415
  }
340
416
  if (client) {
341
417
  client.disconnect();
@@ -366,7 +442,11 @@ async function createPeerConnection(peerId, shouldInitiate) {
366
442
  pc.onicecandidate = (event) => {
367
443
  if (event.candidate) {
368
444
  log(`Sending ICE candidate to ${peerId.substring(0, 6)}...`, 'info');
369
- client.sendIceCandidate(event.candidate, peerId);
445
+ if (nostrClient) {
446
+ sendSignal(peerId, { type: 'signal-ice', candidate: event.candidate.toJSON?.() || event.candidate });
447
+ } else if (client) {
448
+ client.sendIceCandidate(event.candidate, peerId);
449
+ }
370
450
  }
371
451
  };
372
452
 
@@ -381,7 +461,11 @@ async function createPeerConnection(peerId, shouldInitiate) {
381
461
 
382
462
  const offer = await pc.createOffer();
383
463
  await pc.setLocalDescription(offer);
384
- client.sendOffer(offer, peerId);
464
+ if (nostrClient) {
465
+ await sendSignal(peerId, { type: 'signal-offer', sdp: pc.localDescription });
466
+ } else if (client) {
467
+ client.sendOffer(offer, peerId);
468
+ }
385
469
  log(`Sent offer to ${peerId.substring(0, 6)}...`, 'success');
386
470
  } else {
387
471
  log(`Waiting for offer from ${peerId.substring(0, 6)}...`, 'info');
@@ -416,17 +500,8 @@ window.sendChatMessage = function() {
416
500
  return;
417
501
  }
418
502
 
419
- if (nostrClient) {
420
- nostrClient.sendMessage(message).catch((e) => {
421
- log(`Failed to send via Nostr: ${e?.message || e}`, 'error');
422
- });
423
- displayChatMessage(message, 'You', true);
424
- document.getElementById('chatMessage').value = '';
425
- return;
426
- }
427
-
428
503
  if (dataChannels.size === 0) {
429
- log('No peer connections available. Wait for data channels to open.', 'error');
504
+ log('No data channels yet. Open this room in another tab/browser and wait for WebRTC to connect.', 'error');
430
505
  return;
431
506
  }
432
507
 
@@ -15,7 +15,7 @@ function isHex64(s) {
15
15
  * - Publishes kind:1 events tagged with ['t', room] and ['room', room]
16
16
  * - Subscribes to kind:1 events filtered by #t
17
17
  */
18
- export function createNostrClient({ relayUrl, room, onMessage, onPeer, onState } = {}) {
18
+ export function createNostrClient({ relayUrl, room, onPayload, onState } = {}) {
19
19
  if (!relayUrl) throw new Error('relayUrl is required');
20
20
  if (!room) throw new Error('room is required');
21
21
 
@@ -80,6 +80,9 @@ export function createNostrClient({ relayUrl, room, onMessage, onPeer, onState }
80
80
  if (nostrEvent.id && state.seen.has(nostrEvent.id)) return;
81
81
  if (nostrEvent.id) state.seen.add(nostrEvent.id);
82
82
 
83
+ // Ignore our own events
84
+ if (nostrEvent.pubkey && nostrEvent.pubkey === state.pubkey) return;
85
+
83
86
  // Ensure it's for our room
84
87
  const tags = Array.isArray(nostrEvent.tags) ? nostrEvent.tags : [];
85
88
  const roomTag = tags.find((t) => Array.isArray(t) && t[0] === 'room');
@@ -93,22 +96,15 @@ export function createNostrClient({ relayUrl, room, onMessage, onPeer, onState }
93
96
  return;
94
97
  }
95
98
 
96
- if (payload?.type === 'peer-join' && payload.peerId) {
97
- try {
98
- onPeer?.({ peerId: payload.peerId });
99
- } catch {
100
- // ignore
101
- }
102
- return;
103
- }
104
-
105
- if (payload?.type === 'message' && typeof payload.message === 'string') {
106
- const from = (payload.peerId || nostrEvent.pubkey || 'peer').substring(0, 8) + '...';
107
- try {
108
- onMessage?.({ from, text: payload.message });
109
- } catch {
110
- // ignore
111
- }
99
+ try {
100
+ onPayload?.({
101
+ from: nostrEvent.pubkey,
102
+ payload,
103
+ eventId: nostrEvent.id,
104
+ createdAt: nostrEvent.created_at,
105
+ });
106
+ } catch {
107
+ // ignore
112
108
  }
113
109
  }
114
110
  }
@@ -160,37 +156,12 @@ export function createNostrClient({ relayUrl, room, onMessage, onPeer, onState }
160
156
  sendRaw(['REQ', state.subId, filter]);
161
157
 
162
158
  // Announce presence
163
- await sendJoin();
159
+ await send({ type: 'hello' });
164
160
 
165
161
  setState('connected');
166
162
  }
167
163
 
168
- async function sendJoin() {
169
- ensureKeys();
170
- const created_at = Math.floor(Date.now() / 1000);
171
- const tags = [
172
- ['room', state.room],
173
- ['t', state.room],
174
- ];
175
-
176
- const eventTemplate = {
177
- kind: 1,
178
- created_at,
179
- tags,
180
- content: JSON.stringify({
181
- type: 'peer-join',
182
- peerId: state.pubkey,
183
- room: state.room,
184
- timestamp: Date.now(),
185
- }),
186
- pubkey: state.pubkey,
187
- };
188
-
189
- const signed = finalizeEvent(eventTemplate, state.secretKeyHex);
190
- sendRaw(['EVENT', signed]);
191
- }
192
-
193
- async function sendMessage(text) {
164
+ async function send(payload) {
194
165
  ensureKeys();
195
166
  const created_at = Math.floor(Date.now() / 1000);
196
167
  const tags = [
@@ -203,9 +174,8 @@ export function createNostrClient({ relayUrl, room, onMessage, onPeer, onState }
203
174
  created_at,
204
175
  tags,
205
176
  content: JSON.stringify({
206
- type: 'message',
207
- message: text,
208
- peerId: state.pubkey,
177
+ ...payload,
178
+ from: state.pubkey,
209
179
  room: state.room,
210
180
  timestamp: Date.now(),
211
181
  }),
@@ -237,7 +207,7 @@ export function createNostrClient({ relayUrl, room, onMessage, onPeer, onState }
237
207
  return {
238
208
  connect,
239
209
  disconnect,
240
- sendMessage,
210
+ send,
241
211
  getPublicKey: getPublicKeyHex,
242
212
  };
243
213
  }
package/wrangler.toml CHANGED
@@ -1,27 +1,2 @@
1
- name = "uniwrtc"
2
- main = "src/index.js"
3
- compatibility_date = "2024-12-20"
4
-
5
- assets = { directory = "./dist", binding = "ASSETS" }
6
-
7
- [[durable_objects.bindings]]
8
- name = "ROOMS"
9
- class_name = "Room"
10
-
11
- [[migrations]]
12
- tag = "v1"
13
- new_classes = ["Room"]
14
-
15
- [env.production]
16
- routes = [
17
- { pattern = "signal.peer.ooo/*", zone_name = "peer.ooo" }
18
- ]
19
-
20
- assets = { directory = "./dist", binding = "ASSETS" }
21
-
22
- [[env.production.durable_objects.bindings]]
23
- name = "ROOMS"
24
- class_name = "Room"
25
-
26
- [build]
27
- command = "npm install"
1
+ name = "signal-peer-ooo"
2
+ pages_build_output_dir = "dist"