@dobuki/hello-worker 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/LICENSE +21 -0
- package/README.md +5 -0
- package/dist/browser/index.d.ts +3 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +5 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/sample.d.ts +4 -0
- package/dist/browser/sample.d.ts.map +1 -0
- package/dist/browser/sample.js +147 -0
- package/dist/browser/sample.js.map +1 -0
- package/dist/browser/signal-room.d.ts +50 -0
- package/dist/browser/signal-room.d.ts.map +1 -0
- package/dist/browser/signal-room.js +85 -0
- package/dist/browser/signal-room.js.map +1 -0
- package/dist/browser/webrtc-room.d.ts +26 -0
- package/dist/browser/webrtc-room.d.ts.map +1 -0
- package/dist/browser/webrtc-room.js +211 -0
- package/dist/browser/webrtc-room.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/room.d.ts +10 -0
- package/dist/room.d.ts.map +1 -0
- package/dist/room.js +111 -0
- package/dist/room.js.map +1 -0
- package/package.json +28 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Vincent Le Quang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/browser/index.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,oCAAoC;AAEpC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sample.d.ts","sourceRoot":"","sources":["../../src/browser/sample.ts"],"names":[],"mappings":"AAyBA,wBAAgB,QAAQ,SAEvB;AAED,wBAAgB,WAAW,eAkC1B;AAED,wBAAgB,UAAU,eAkGzB"}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
/// <reference lib="dom.iterable" />
|
|
3
|
+
import { enterRoom } from "./signal-room.js";
|
|
4
|
+
import { joinWebRTCRoom } from "./webrtc-room.js";
|
|
5
|
+
const statusEl = document.getElementById("status");
|
|
6
|
+
const logEl = document.getElementById("log");
|
|
7
|
+
const welcomeEl = document.getElementById("welcome");
|
|
8
|
+
const userId = crypto.randomUUID();
|
|
9
|
+
function ts() {
|
|
10
|
+
// HH:MM:SS.mmm (local time)
|
|
11
|
+
const d = new Date();
|
|
12
|
+
const p2 = (n) => String(n).padStart(2, "0");
|
|
13
|
+
const p3 = (n) => String(n).padStart(3, "0");
|
|
14
|
+
return `${p2(d.getHours())}:${p2(d.getMinutes())}:${p2(d.getSeconds())}.${p3(d.getMilliseconds())}`;
|
|
15
|
+
}
|
|
16
|
+
function logLine(direction, obj) {
|
|
17
|
+
logEl.textContent += ts() + " " + direction + " " + (obj ? JSON.stringify(obj) : "") + "\n";
|
|
18
|
+
logEl.scrollTop = logEl.scrollHeight;
|
|
19
|
+
}
|
|
20
|
+
export function clearLog() {
|
|
21
|
+
logEl.textContent = "";
|
|
22
|
+
}
|
|
23
|
+
export function testWelcome() {
|
|
24
|
+
const { exitRoom } = enterRoom({
|
|
25
|
+
userId,
|
|
26
|
+
room: "test",
|
|
27
|
+
host: location.host,
|
|
28
|
+
onOpen: () => {
|
|
29
|
+
statusEl.textContent = "connected";
|
|
30
|
+
logLine("🔗 CONNECTED");
|
|
31
|
+
},
|
|
32
|
+
onClose: () => {
|
|
33
|
+
statusEl.textContent = "closed";
|
|
34
|
+
logLine("⛓️💥 DISCONNECTED");
|
|
35
|
+
statusEl.textContent = "closed";
|
|
36
|
+
},
|
|
37
|
+
onError: () => {
|
|
38
|
+
statusEl.textContent = "error";
|
|
39
|
+
logLine("⚠️ ERROR");
|
|
40
|
+
},
|
|
41
|
+
onPeerJoined: (user) => {
|
|
42
|
+
user.receive("welcome", { note: welcomeEl.value });
|
|
43
|
+
},
|
|
44
|
+
onPeerLeft: (info) => {
|
|
45
|
+
logLine("👤 LEFT", info);
|
|
46
|
+
},
|
|
47
|
+
onMessage: (type, payload, user) => {
|
|
48
|
+
if (type === "welcome") {
|
|
49
|
+
user.receive("thanks", { note: "Thank you! 🙏" });
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
logLine,
|
|
53
|
+
});
|
|
54
|
+
return () => {
|
|
55
|
+
exitRoom();
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
export function testWebRTC() {
|
|
59
|
+
// --- create a stage + emoji (if not already in HTML) ---
|
|
60
|
+
let stageEl = document.getElementById("stage");
|
|
61
|
+
if (!stageEl) {
|
|
62
|
+
stageEl = document.createElement("div");
|
|
63
|
+
stageEl.id = "stage";
|
|
64
|
+
stageEl.style.position = "relative";
|
|
65
|
+
stageEl.style.width = "100%";
|
|
66
|
+
stageEl.style.height = "280px";
|
|
67
|
+
stageEl.style.border = "1px solid #333";
|
|
68
|
+
stageEl.style.borderRadius = "8px";
|
|
69
|
+
stageEl.style.margin = "12px 0";
|
|
70
|
+
stageEl.style.userSelect = "none";
|
|
71
|
+
stageEl.style.touchAction = "none";
|
|
72
|
+
document.body.insertBefore(stageEl, welcomeEl);
|
|
73
|
+
}
|
|
74
|
+
welcomeEl.classList.add("hidden");
|
|
75
|
+
let emojiEl = document.getElementById("emoji");
|
|
76
|
+
if (!emojiEl) {
|
|
77
|
+
emojiEl = document.createElement("div");
|
|
78
|
+
emojiEl.id = "emoji";
|
|
79
|
+
emojiEl.textContent = "🦊";
|
|
80
|
+
emojiEl.style.position = "absolute";
|
|
81
|
+
emojiEl.style.left = "0px";
|
|
82
|
+
emojiEl.style.top = "0px";
|
|
83
|
+
emojiEl.style.transform = "translate(-50%, -50%)";
|
|
84
|
+
emojiEl.style.fontSize = "36px";
|
|
85
|
+
emojiEl.style.pointerEvents = "none";
|
|
86
|
+
stageEl.appendChild(emojiEl);
|
|
87
|
+
}
|
|
88
|
+
function setEmojiPos01(x01, y01) {
|
|
89
|
+
const r = stageEl.getBoundingClientRect();
|
|
90
|
+
const x = Math.max(0, Math.min(1, x01)) * r.width;
|
|
91
|
+
const y = Math.max(0, Math.min(1, y01)) * r.height;
|
|
92
|
+
emojiEl.style.left = `${x}px`;
|
|
93
|
+
emojiEl.style.top = `${y}px`;
|
|
94
|
+
}
|
|
95
|
+
// --- start WebRTC mesh using YOUR joinWebRTCRoom ---
|
|
96
|
+
statusEl.textContent = "connecting";
|
|
97
|
+
logLine("💬", { event: "start-webrtc-test" });
|
|
98
|
+
const session = joinWebRTCRoom({
|
|
99
|
+
userId,
|
|
100
|
+
logLine,
|
|
101
|
+
enterRoom,
|
|
102
|
+
onMessage: (data, from) => {
|
|
103
|
+
try {
|
|
104
|
+
const { x, y } = JSON.parse(String(data));
|
|
105
|
+
if (typeof x === "number" && typeof y === "number") {
|
|
106
|
+
setEmojiPos01(x, y);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
// ignore non-json
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
session.enter({
|
|
115
|
+
room: "test",
|
|
116
|
+
host: location.host,
|
|
117
|
+
});
|
|
118
|
+
// --- send mouse position over all open data channels ---
|
|
119
|
+
let lastSent = 0;
|
|
120
|
+
function broadcastMove(x01, y01) {
|
|
121
|
+
const now = performance.now();
|
|
122
|
+
if (now - lastSent < 16)
|
|
123
|
+
return; // ~60Hz throttle
|
|
124
|
+
lastSent = now;
|
|
125
|
+
const msg = JSON.stringify({ x: x01, y: y01 });
|
|
126
|
+
session.sendToAll(msg);
|
|
127
|
+
}
|
|
128
|
+
function onPointerMove(ev) {
|
|
129
|
+
const r = stageEl.getBoundingClientRect();
|
|
130
|
+
const x01 = (ev.clientX - r.left) / r.width;
|
|
131
|
+
const y01 = (ev.clientY - r.top) / r.height;
|
|
132
|
+
// move locally too (feels instant)
|
|
133
|
+
setEmojiPos01(x01, y01);
|
|
134
|
+
// broadcast to peers via datachannel
|
|
135
|
+
broadcastMove(x01, y01);
|
|
136
|
+
}
|
|
137
|
+
stageEl.addEventListener("pointermove", onPointerMove);
|
|
138
|
+
// return cleanup function (same pattern as testWelcome)
|
|
139
|
+
return () => {
|
|
140
|
+
stageEl.removeEventListener("pointermove", onPointerMove);
|
|
141
|
+
stageEl.remove();
|
|
142
|
+
welcomeEl.classList.remove("hidden");
|
|
143
|
+
session.end();
|
|
144
|
+
logLine("💬", { event: "stop-webrtc-test" });
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=sample.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sample.js","sourceRoot":"","sources":["../../src/browser/sample.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export interface IUser<T extends string = string, P = any> {
|
|
2
|
+
info: {
|
|
3
|
+
peerId: string;
|
|
4
|
+
userId: string;
|
|
5
|
+
};
|
|
6
|
+
receive(type: T, payload: P): boolean;
|
|
7
|
+
}
|
|
8
|
+
export type EnterRoom<T extends string = string, P = any> = (options: {
|
|
9
|
+
userId: string;
|
|
10
|
+
room: string;
|
|
11
|
+
host: string;
|
|
12
|
+
onOpen?: () => void;
|
|
13
|
+
onClose?: () => void;
|
|
14
|
+
onError?: () => void;
|
|
15
|
+
logLine?: (direction: string, obj?: any) => void;
|
|
16
|
+
onPeerJoined?: (user: IUser<T, P>) => void;
|
|
17
|
+
onPeerLeft?: (info: IUser["info"]) => void;
|
|
18
|
+
onMessage?: (type: T, payload: P, from: IUser<T, P>) => void;
|
|
19
|
+
}) => {
|
|
20
|
+
exitRoom: () => void;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* enterRoom connects to the signaling room via WebSocket.
|
|
24
|
+
*
|
|
25
|
+
* Usage:
|
|
26
|
+
* const { exitRoom } = enterRoom({
|
|
27
|
+
* room: "test",
|
|
28
|
+
* host: location.host,
|
|
29
|
+
* onOpen: () => { ... },
|
|
30
|
+
* onClose: () => { ... },
|
|
31
|
+
* onError: () => { ... },
|
|
32
|
+
* onPeerJoined: (user) => { ... },
|
|
33
|
+
* onMessage: (type, payload, fromUser) => { ... },
|
|
34
|
+
* });
|
|
35
|
+
*/
|
|
36
|
+
export declare function enterRoom<T extends string, P = any>({ userId, room, host, onOpen, onClose, onError, logLine, onPeerJoined, onPeerLeft, onMessage, }: {
|
|
37
|
+
userId: string;
|
|
38
|
+
room: string;
|
|
39
|
+
host: string;
|
|
40
|
+
onOpen?: () => void;
|
|
41
|
+
onClose?: () => void;
|
|
42
|
+
onError?: () => void;
|
|
43
|
+
logLine?: (direction: string, obj?: any) => void;
|
|
44
|
+
onPeerJoined?: (user: IUser) => void;
|
|
45
|
+
onPeerLeft?: (info: IUser["info"]) => void;
|
|
46
|
+
onMessage?: (type: T, payload: P, from: IUser) => void;
|
|
47
|
+
}): {
|
|
48
|
+
exitRoom: () => void;
|
|
49
|
+
};
|
|
50
|
+
//# sourceMappingURL=signal-room.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signal-room.d.ts","sourceRoot":"","sources":["../../src/browser/signal-room.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,GAAG;IACrD,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACzC,OAAO,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,GAAG,OAAO,CAAC;CACzC;AAED,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,CAAC,GAAG,GAAG,IAAI,CAAC,OAAO,EAAE;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IACjD,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC;IAC3C,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;IAC3C,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,KAAK,IAAI,CAAC;CAChE,KAAK;IAAE,QAAQ,EAAE,MAAM,IAAI,CAAA;CAAE,CAAC;AAE/B;;;;;;;;;;;;;GAaG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,GAAG,GAAG,EAAE,EACjD,MAAM,EACN,IAAI,EACJ,IAAI,EACJ,MAAM,EACN,OAAO,EACP,OAAO,EACP,OAAO,EACP,YAAY,EACZ,UAAU,EACV,SAAS,GACZ,EAAE;IACC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IACjD,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,IAAI,CAAC;IACrC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;IAC3C,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,KAAK,KAAK,IAAI,CAAC;CAC1D;;EAqEA"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* enterRoom connects to the signaling room via WebSocket.
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* const { exitRoom } = enterRoom({
|
|
6
|
+
* room: "test",
|
|
7
|
+
* host: location.host,
|
|
8
|
+
* onOpen: () => { ... },
|
|
9
|
+
* onClose: () => { ... },
|
|
10
|
+
* onError: () => { ... },
|
|
11
|
+
* onPeerJoined: (user) => { ... },
|
|
12
|
+
* onMessage: (type, payload, fromUser) => { ... },
|
|
13
|
+
* });
|
|
14
|
+
*/
|
|
15
|
+
export function enterRoom({ userId, room, host, onOpen, onClose, onError, logLine, onPeerJoined, onPeerLeft, onMessage, }) {
|
|
16
|
+
const wsUrl = "wss://" + host + "/room/" + room + "?userId=" + encodeURIComponent(userId);
|
|
17
|
+
const ws = new WebSocket(wsUrl);
|
|
18
|
+
let exited = false;
|
|
19
|
+
function send(type, toPeerId, payload) {
|
|
20
|
+
if (exited)
|
|
21
|
+
return false;
|
|
22
|
+
const obj = { type, to: toPeerId, payload };
|
|
23
|
+
ws.send(JSON.stringify(obj));
|
|
24
|
+
logLine?.("👤 ➡️ 🖥️", obj);
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
function onmessage(e) {
|
|
28
|
+
let msg;
|
|
29
|
+
try {
|
|
30
|
+
msg = JSON.parse(e.data);
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
logLine?.("⚠️ ERROR", { error: "invalid-json" });
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
logLine?.("🖥️ ➡️ 👤", msg);
|
|
37
|
+
// Existing client greets newcomers
|
|
38
|
+
if (msg.type === "peer-joined" && msg.peerId && msg.userId) {
|
|
39
|
+
const { userId, peerId } = msg;
|
|
40
|
+
onPeerJoined?.({
|
|
41
|
+
info: { peerId, userId },
|
|
42
|
+
receive: (type, payload) => {
|
|
43
|
+
return send(type, peerId, payload);
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (msg.type === "peer-left" && msg.peerId && msg.userId) {
|
|
49
|
+
const { userId, peerId } = msg;
|
|
50
|
+
onPeerLeft?.({ peerId, userId });
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (msg.peerId && msg.userId) {
|
|
54
|
+
const { userId, peerId } = msg;
|
|
55
|
+
onMessage?.(msg.type, msg.payload, {
|
|
56
|
+
info: { peerId, userId },
|
|
57
|
+
receive: (type, payload) => {
|
|
58
|
+
return send(type, peerId, payload);
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
;
|
|
64
|
+
ws.addEventListener("message", onmessage);
|
|
65
|
+
if (onOpen)
|
|
66
|
+
ws.addEventListener("open", onOpen);
|
|
67
|
+
if (onClose)
|
|
68
|
+
ws.addEventListener("close", onClose);
|
|
69
|
+
if (onError)
|
|
70
|
+
ws.addEventListener("error", onError);
|
|
71
|
+
return {
|
|
72
|
+
exitRoom: () => {
|
|
73
|
+
exited = true;
|
|
74
|
+
ws.close();
|
|
75
|
+
ws.removeEventListener("message", onmessage);
|
|
76
|
+
if (onOpen)
|
|
77
|
+
ws.removeEventListener("open", onOpen);
|
|
78
|
+
if (onClose)
|
|
79
|
+
ws.removeEventListener("close", onClose);
|
|
80
|
+
if (onError)
|
|
81
|
+
ws.removeEventListener("error", onError);
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=signal-room.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signal-room.js","sourceRoot":"","sources":["../../src/browser/signal-room.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { EnterRoom } from "./signal-room";
|
|
2
|
+
type SigType = "offer" | "answer" | "ice";
|
|
3
|
+
type SigPayload = RTCSessionDescriptionInit | RTCIceCandidateInit;
|
|
4
|
+
export declare function joinWebRTCRoom({ userId, onMessage, logLine, enterRoom, }: {
|
|
5
|
+
userId: string;
|
|
6
|
+
onMessage?: (data: any, from: {
|
|
7
|
+
userId: string;
|
|
8
|
+
peerId: string;
|
|
9
|
+
}) => void;
|
|
10
|
+
logLine: (direction: string, obj?: any) => void;
|
|
11
|
+
enterRoom: EnterRoom<SigType, SigPayload>;
|
|
12
|
+
}): {
|
|
13
|
+
sendToUser: (userId: string, data: string) => void;
|
|
14
|
+
sendToAll: (data: string) => void;
|
|
15
|
+
end: () => void;
|
|
16
|
+
enter: ({ room, host }: {
|
|
17
|
+
room: string;
|
|
18
|
+
host: string;
|
|
19
|
+
}) => void;
|
|
20
|
+
exit: ({ room, host }: {
|
|
21
|
+
room: string;
|
|
22
|
+
host: string;
|
|
23
|
+
}) => void;
|
|
24
|
+
};
|
|
25
|
+
export {};
|
|
26
|
+
//# sourceMappingURL=webrtc-room.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webrtc-room.d.ts","sourceRoot":"","sources":["../../src/browser/webrtc-room.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAS,MAAM,eAAe,CAAC;AAEjD,KAAK,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;AAC1C,KAAK,UAAU,GAAG,yBAAyB,GAAG,mBAAmB,CAAC;AAelE,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,SAAS,EACT,OAAO,EACP,SAAS,GACV,EAAE;IACD,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAC1E,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IAChD,SAAS,EAAE,SAAS,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;CAC3C;yBAqL6B,MAAM,QAAQ,MAAM;sBAMvB,MAAM;;4BAxGA;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;KAAE;2BAyFhC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;KAAE;EAsC9D"}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
export function joinWebRTCRoom({ userId, onMessage, logLine, enterRoom, }) {
|
|
2
|
+
const rtcConfig = {
|
|
3
|
+
iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
|
|
4
|
+
};
|
|
5
|
+
const peers = new Map();
|
|
6
|
+
function wireDataChannel(state) {
|
|
7
|
+
const dc = state.dataChannel;
|
|
8
|
+
if (!dc)
|
|
9
|
+
return;
|
|
10
|
+
dc.onopen = () => logLine("💬", { event: "dc-open", userId: state.userId });
|
|
11
|
+
dc.onmessage = (e) => {
|
|
12
|
+
onMessage?.(e.data, { userId: state.userId, peerId: state.userId });
|
|
13
|
+
logLine("💬", { event: "dc-message", userId: state.userId, data: e.data });
|
|
14
|
+
};
|
|
15
|
+
dc.onclose = () => logLine("💬", { event: "dc-close", userId: state.userId });
|
|
16
|
+
dc.onerror = () => logLine("⚠️ ERROR", { error: "dc-error", userId: state.userId });
|
|
17
|
+
}
|
|
18
|
+
async function flushRemoteIce(state) {
|
|
19
|
+
if (!state.pc.remoteDescription)
|
|
20
|
+
return;
|
|
21
|
+
const queued = state.pendingRemoteIce;
|
|
22
|
+
state.pendingRemoteIce = [];
|
|
23
|
+
for (const ice of queued) {
|
|
24
|
+
try {
|
|
25
|
+
await state.pc.addIceCandidate(ice);
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
logLine("⚠️ ERROR", { error: "add-ice-failed", userId: state.userId, detail: String(e) });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function getPeer(user) {
|
|
33
|
+
let state = peers.get(user.info.userId);
|
|
34
|
+
if (!state) {
|
|
35
|
+
const newState = {
|
|
36
|
+
userId: user.info.userId,
|
|
37
|
+
pc: new RTCPeerConnection(rtcConfig),
|
|
38
|
+
pendingRemoteIce: [],
|
|
39
|
+
users: new Set([user]),
|
|
40
|
+
dataChannel: null,
|
|
41
|
+
};
|
|
42
|
+
peers.set(user.info.userId, newState);
|
|
43
|
+
// Send local ICE candidates to this peer
|
|
44
|
+
newState.pc.onicecandidate = (ev) => {
|
|
45
|
+
if (!ev.candidate)
|
|
46
|
+
return;
|
|
47
|
+
for (let user of newState.users) {
|
|
48
|
+
const success = user.receive("ice", ev.candidate.toJSON());
|
|
49
|
+
if (success)
|
|
50
|
+
break;
|
|
51
|
+
newState.users.delete(user);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
// Responder receives DataChannel here
|
|
55
|
+
newState.pc.ondatachannel = (ev) => {
|
|
56
|
+
newState.dataChannel = ev.channel;
|
|
57
|
+
wireDataChannel(newState);
|
|
58
|
+
};
|
|
59
|
+
newState.pc.onconnectionstatechange = () => {
|
|
60
|
+
logLine("💬", { event: "pc-state", userId: newState.userId, state: newState.pc.connectionState });
|
|
61
|
+
};
|
|
62
|
+
state = newState;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
state.users.add(user);
|
|
66
|
+
}
|
|
67
|
+
peers.set(state.userId, state);
|
|
68
|
+
return state;
|
|
69
|
+
}
|
|
70
|
+
function leaveUser(userId) {
|
|
71
|
+
const p = peers.get(userId);
|
|
72
|
+
if (!p)
|
|
73
|
+
return;
|
|
74
|
+
try {
|
|
75
|
+
p.dataChannel?.close();
|
|
76
|
+
}
|
|
77
|
+
catch { }
|
|
78
|
+
try {
|
|
79
|
+
p.pc.close();
|
|
80
|
+
}
|
|
81
|
+
catch { }
|
|
82
|
+
peers.delete(userId);
|
|
83
|
+
logLine("👤 USER LEFT", userId);
|
|
84
|
+
}
|
|
85
|
+
const roomsEntered = new Map();
|
|
86
|
+
function enter({ room, host }) {
|
|
87
|
+
const { exitRoom } = enterRoom({
|
|
88
|
+
userId,
|
|
89
|
+
room,
|
|
90
|
+
host,
|
|
91
|
+
logLine,
|
|
92
|
+
// Existing peers initiate to the newcomer (Option 1)
|
|
93
|
+
onPeerJoined: async (user) => {
|
|
94
|
+
const state = getPeer(user);
|
|
95
|
+
const pc = state.pc;
|
|
96
|
+
// Initiator creates the DataChannel
|
|
97
|
+
if (!state.dataChannel) {
|
|
98
|
+
state.dataChannel = pc.createDataChannel("data");
|
|
99
|
+
wireDataChannel(state);
|
|
100
|
+
}
|
|
101
|
+
// Offer flow: createOffer -> setLocalDescription -> send localDescription
|
|
102
|
+
const offer = await pc.createOffer();
|
|
103
|
+
await pc.setLocalDescription(offer);
|
|
104
|
+
user.receive("offer", pc.localDescription);
|
|
105
|
+
},
|
|
106
|
+
onPeerLeft: (info) => {
|
|
107
|
+
const state = peers.get(info.userId);
|
|
108
|
+
if (!state)
|
|
109
|
+
return;
|
|
110
|
+
for (const user of state.users) {
|
|
111
|
+
if (user.info.userId === info.userId) {
|
|
112
|
+
state.users.delete(user);
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
logLine("👤 LEFT", info);
|
|
117
|
+
if (state.users.size === 0) {
|
|
118
|
+
try {
|
|
119
|
+
state.dataChannel?.close();
|
|
120
|
+
}
|
|
121
|
+
catch { }
|
|
122
|
+
try {
|
|
123
|
+
state.pc.close();
|
|
124
|
+
}
|
|
125
|
+
catch { }
|
|
126
|
+
leaveUser(info.userId);
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
onMessage: async (type, payload, from) => {
|
|
130
|
+
const state = getPeer(from);
|
|
131
|
+
const pc = state.pc;
|
|
132
|
+
if (type === "offer") {
|
|
133
|
+
// Responder: set remote offer
|
|
134
|
+
await pc.setRemoteDescription(payload);
|
|
135
|
+
// Create and send answer
|
|
136
|
+
const answer = await pc.createAnswer();
|
|
137
|
+
await pc.setLocalDescription(answer);
|
|
138
|
+
from.receive("answer", pc.localDescription);
|
|
139
|
+
// Now safe to apply any queued ICE from this peer
|
|
140
|
+
await flushRemoteIce(state);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (type === "answer") {
|
|
144
|
+
// Initiator: set remote answer
|
|
145
|
+
await pc.setRemoteDescription(payload);
|
|
146
|
+
await flushRemoteIce(state);
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (type === "ice") {
|
|
150
|
+
const ice = payload;
|
|
151
|
+
// If we don't have remoteDescription yet, queue it
|
|
152
|
+
if (!pc.remoteDescription) {
|
|
153
|
+
state.pendingRemoteIce.push(ice);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
await pc.addIceCandidate(ice);
|
|
158
|
+
}
|
|
159
|
+
catch (e) {
|
|
160
|
+
logLine("⚠️ ERROR", { error: "add-ice-failed", userId: state.userId, detail: String(e) });
|
|
161
|
+
}
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
roomsEntered.set(`${host}/room/${room}`, { exitRoom, host, room });
|
|
167
|
+
}
|
|
168
|
+
function exit({ room, host }) {
|
|
169
|
+
const key = `${host}/room/${room}`;
|
|
170
|
+
const session = roomsEntered.get(key);
|
|
171
|
+
if (session) {
|
|
172
|
+
session.exitRoom();
|
|
173
|
+
roomsEntered.delete(key);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
const sendToUser = (userId, data) => {
|
|
177
|
+
const p = peers.get(userId);
|
|
178
|
+
if (!p)
|
|
179
|
+
return;
|
|
180
|
+
if (p.dataChannel?.readyState === "open")
|
|
181
|
+
p.dataChannel.send(data);
|
|
182
|
+
};
|
|
183
|
+
function sendToAll(data) {
|
|
184
|
+
for (const p of peers.values()) {
|
|
185
|
+
if (p.dataChannel?.readyState === "open")
|
|
186
|
+
p.dataChannel.send(data);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
sendToUser,
|
|
191
|
+
sendToAll,
|
|
192
|
+
end: () => {
|
|
193
|
+
roomsEntered.values().forEach(({ exitRoom }) => exitRoom());
|
|
194
|
+
roomsEntered.clear();
|
|
195
|
+
for (const p of peers.values()) {
|
|
196
|
+
try {
|
|
197
|
+
p.dataChannel?.close();
|
|
198
|
+
}
|
|
199
|
+
catch { }
|
|
200
|
+
try {
|
|
201
|
+
p.pc.close();
|
|
202
|
+
}
|
|
203
|
+
catch { }
|
|
204
|
+
}
|
|
205
|
+
peers.clear();
|
|
206
|
+
},
|
|
207
|
+
enter,
|
|
208
|
+
exit,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
//# sourceMappingURL=webrtc-room.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webrtc-room.js","sourceRoot":"","sources":["../../src/browser/webrtc-room.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface Env {
|
|
2
|
+
ROOM: DurableObjectNamespace;
|
|
3
|
+
ASSETS: Fetcher;
|
|
4
|
+
}
|
|
5
|
+
export { Room } from "./room";
|
|
6
|
+
declare const _default: {
|
|
7
|
+
fetch(req: Request, env: Env): Promise<Response>;
|
|
8
|
+
};
|
|
9
|
+
export default _default;
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,GAAG;IAClB,IAAI,EAAE,sBAAsB,CAAC;IAC7B,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;;eAGX,OAAO,OAAO,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC;;AADxD,wBAuBE"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export { Room } from "./room";
|
|
2
|
+
export default {
|
|
3
|
+
async fetch(req, env) {
|
|
4
|
+
const url = new URL(req.url);
|
|
5
|
+
// If NOT /room/<id>, serve test HTML
|
|
6
|
+
const match = url.pathname.match(/^\/room\/([^/]+)$/);
|
|
7
|
+
if (!match) {
|
|
8
|
+
// everything else falls through to static assets
|
|
9
|
+
return env.ASSETS.fetch(req);
|
|
10
|
+
}
|
|
11
|
+
// WebSocket upgrade required
|
|
12
|
+
const upgrade = req.headers.get("Upgrade");
|
|
13
|
+
if (upgrade?.toLowerCase() !== "websocket") {
|
|
14
|
+
return new Response("Expected WebSocket", { status: 426 });
|
|
15
|
+
}
|
|
16
|
+
const roomId = decodeURIComponent(match[1]);
|
|
17
|
+
const id = env.ROOM.idFromName(roomId);
|
|
18
|
+
const stub = env.ROOM.get(id);
|
|
19
|
+
return stub.fetch(req);
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAE9B,eAAe;IACb,KAAK,CAAC,KAAK,CAAC,GAAY,EAAE,GAAQ;QAChC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAE7B,qCAAqC;QACrC,MAAM,KAAK,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;QACtD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,iDAAiD;YACjD,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC/B,CAAC;QAED,6BAA6B;QAC7B,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,OAAO,EAAE,WAAW,EAAE,KAAK,WAAW,EAAE,CAAC;YAC3C,OAAO,IAAI,QAAQ,CAAC,oBAAoB,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QAC7D,CAAC;QAED,MAAM,MAAM,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAC5C,MAAM,EAAE,GAAG,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QACvC,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAE9B,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;CACF,CAAC"}
|
package/dist/room.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare class Room implements DurableObject {
|
|
2
|
+
private state;
|
|
3
|
+
private env;
|
|
4
|
+
constructor(state: DurableObjectState, env: unknown);
|
|
5
|
+
fetch(req: Request): Promise<Response>;
|
|
6
|
+
webSocketMessage(ws: WebSocket, message: string | ArrayBuffer): void;
|
|
7
|
+
webSocketClose(ws: WebSocket): void;
|
|
8
|
+
webSocketError(ws: WebSocket, err: unknown): void;
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=room.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"room.d.ts","sourceRoot":"","sources":["../src/room.ts"],"names":[],"mappings":"AAmBA,qBAAa,IAAK,YAAW,aAAa;IAC5B,OAAO,CAAC,KAAK;IAAsB,OAAO,CAAC,GAAG;gBAAtC,KAAK,EAAE,kBAAkB,EAAU,GAAG,EAAE,OAAO;IAI7D,KAAK,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;IA6B5C,gBAAgB,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,WAAW;IA0D7D,cAAc,CAAC,EAAE,EAAE,SAAS;IAiB5B,cAAc,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO;CAI3C"}
|
package/dist/room.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
function getAttachment(ws) {
|
|
2
|
+
try {
|
|
3
|
+
const a = ws.deserializeAttachment();
|
|
4
|
+
return a;
|
|
5
|
+
}
|
|
6
|
+
catch {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export class Room {
|
|
11
|
+
state;
|
|
12
|
+
env;
|
|
13
|
+
constructor(state, env) {
|
|
14
|
+
this.state = state;
|
|
15
|
+
this.env = env;
|
|
16
|
+
void env;
|
|
17
|
+
}
|
|
18
|
+
async fetch(req) {
|
|
19
|
+
const userId = new URL(req.url).searchParams.get("userId");
|
|
20
|
+
if (!userId) {
|
|
21
|
+
return new Response("Missing userId", { status: 400 });
|
|
22
|
+
}
|
|
23
|
+
const pair = new WebSocketPair();
|
|
24
|
+
const client = pair[0];
|
|
25
|
+
const server = pair[1];
|
|
26
|
+
// IMPORTANT: accept first
|
|
27
|
+
this.state.acceptWebSocket(server);
|
|
28
|
+
// Persist peerId via attachment (survives hibernation)
|
|
29
|
+
const peerId = crypto.randomUUID();
|
|
30
|
+
server.serializeAttachment({ peerId, userId });
|
|
31
|
+
console.log(`Room ${this.state.id.toString()} got new peer: ${peerId} (userId=${userId})`);
|
|
32
|
+
// Notify existing peers about the newcomer
|
|
33
|
+
for (const ws of this.state.getWebSockets()) {
|
|
34
|
+
if (ws === server)
|
|
35
|
+
continue;
|
|
36
|
+
try {
|
|
37
|
+
ws.send(JSON.stringify({ type: "peer-joined", peerId, userId }));
|
|
38
|
+
}
|
|
39
|
+
catch { }
|
|
40
|
+
}
|
|
41
|
+
return new Response(null, { status: 101, webSocket: client });
|
|
42
|
+
}
|
|
43
|
+
webSocketMessage(ws, message) {
|
|
44
|
+
const attachment = getAttachment(ws);
|
|
45
|
+
console.log(`Room ${this.state.id.toString()} got message from ${attachment}:`, message);
|
|
46
|
+
if (!attachment) {
|
|
47
|
+
// This should not happen after the attachment fix,
|
|
48
|
+
// but keep a helpful error if it does.
|
|
49
|
+
try {
|
|
50
|
+
ws.send(JSON.stringify({ type: "error", error: "missing-peerid" }));
|
|
51
|
+
}
|
|
52
|
+
catch { }
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (typeof message !== "string") {
|
|
56
|
+
ws.send(JSON.stringify({ type: "error", error: "binary-not-supported" }));
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
let msg;
|
|
60
|
+
try {
|
|
61
|
+
msg = JSON.parse(message);
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
ws.send(JSON.stringify({ type: "error", error: "invalid-json" }));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
// Generic relay: require msg.to
|
|
68
|
+
if (typeof msg.to === "string") {
|
|
69
|
+
const toPeerId = msg.to;
|
|
70
|
+
const out = {
|
|
71
|
+
type: msg.type,
|
|
72
|
+
userId: attachment.userId,
|
|
73
|
+
peerId: attachment.peerId,
|
|
74
|
+
payload: msg.payload ?? null,
|
|
75
|
+
};
|
|
76
|
+
for (const other of this.state.getWebSockets()) {
|
|
77
|
+
if (getAttachment(other)?.peerId === toPeerId) {
|
|
78
|
+
try {
|
|
79
|
+
other.send(JSON.stringify(out));
|
|
80
|
+
}
|
|
81
|
+
catch { }
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
ws.send(JSON.stringify({ type: "error", error: "peer-not-found", to: toPeerId }));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
ws.send(JSON.stringify({ type: "error", error: "missing-to" }));
|
|
89
|
+
}
|
|
90
|
+
webSocketClose(ws) {
|
|
91
|
+
const attachment = getAttachment(ws);
|
|
92
|
+
console.log(`Room ${this.state.id.toString()} peer disconnected:`, attachment);
|
|
93
|
+
if (!attachment)
|
|
94
|
+
return;
|
|
95
|
+
const { peerId, userId } = attachment;
|
|
96
|
+
// Notify other peers about the departure
|
|
97
|
+
for (const other of this.state.getWebSockets()) {
|
|
98
|
+
if (other === ws)
|
|
99
|
+
continue;
|
|
100
|
+
try {
|
|
101
|
+
other.send(JSON.stringify({ type: "peer-left", peerId, userId }));
|
|
102
|
+
}
|
|
103
|
+
catch { }
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
webSocketError(ws, err) {
|
|
107
|
+
const peerId = getAttachment(ws);
|
|
108
|
+
console.log(`Room ${this.state.id.toString()} ws error for peer ${peerId}:`, err);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=room.js.map
|
package/dist/room.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"room.js","sourceRoot":"","sources":["../src/room.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dobuki/hello-worker",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./webrtc-room": { "types": "./dist/webrtc-room.d.ts", "default": "./dist/webrtc-room.js" },
|
|
13
|
+
"./signal-room": { "types": "./dist/signal-room.d.ts", "default": "./dist/signal-room.js" }
|
|
14
|
+
},
|
|
15
|
+
"files": ["dist"],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"dev": "wrangler dev src/index.ts --config wrangler.dev.toml",
|
|
18
|
+
"deploy": "wrangler deploy",
|
|
19
|
+
"build": "bun i && bun build src/browser/*.ts --target browser --outdir public --minify --sourcemap && tsc -p tsconfig.json",
|
|
20
|
+
"prepublishOnly": "npm run build"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@cloudflare/workers-types": "latest",
|
|
24
|
+
"typescript": "latest",
|
|
25
|
+
"wrangler": "latest"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {}
|
|
28
|
+
}
|