@dobuki/hello-worker 1.0.4 → 1.0.6
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/README.md +1 -1
- package/dist/enter-world.d.ts +26 -0
- package/dist/enter-world.d.ts.map +1 -0
- package/dist/enter-world.js +89 -0
- package/dist/enter-world.js.map +1 -0
- package/dist/impl/signal-room.d.ts +5 -15
- package/dist/impl/signal-room.d.ts.map +1 -1
- package/dist/impl/signal-room.js +6 -15
- package/dist/impl/signal-room.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- package/dist/sample/index.d.ts +4 -0
- package/dist/sample/index.d.ts.map +1 -0
- package/dist/sample/index.js +145 -0
- package/dist/sample/index.js.map +1 -0
- package/dist/sample.js +1 -1
- package/dist/sample.js.map +1 -1
- package/dist/signal-room.d.ts +4 -5
- package/dist/signal-room.d.ts.map +1 -1
- package/dist/signal-room.js +13 -10
- package/dist/signal-room.js.map +1 -1
- package/dist/signal-room.worker.d.ts +25 -1
- package/dist/signal-room.worker.d.ts.map +1 -1
- package/dist/signal-room.worker.js +9 -10
- package/dist/signal-room.worker.js.map +1 -1
- package/dist/webrtc-peer-collector.d.ts +34 -0
- package/dist/webrtc-peer-collector.d.ts.map +1 -0
- package/dist/webrtc-peer-collector.js +154 -0
- package/dist/webrtc-peer-collector.js.map +1 -0
- package/dist/webrtc-room.d.ts +36 -8
- package/dist/webrtc-room.d.ts.map +1 -1
- package/dist/webrtc-room.js +112 -111
- package/dist/webrtc-room.js.map +1 -1
- package/package.json +2 -2
|
@@ -27,32 +27,31 @@ self.addEventListener("message", (e) => {
|
|
|
27
27
|
},
|
|
28
28
|
onPeerJoined: (user) => {
|
|
29
29
|
// Save the ability to send to this peer
|
|
30
|
-
peerSend.set(user.
|
|
31
|
-
emit({ kind: "peer-joined", userId: user.userId });
|
|
30
|
+
peerSend.set(user.peerId, user.receive);
|
|
31
|
+
emit({ kind: "peer-joined", userId: user.userId, peerId: user.peerId });
|
|
32
32
|
},
|
|
33
|
-
onPeerLeft: (userId) => {
|
|
34
|
-
peerSend.delete(
|
|
35
|
-
emit({ kind: "peer-left", userId });
|
|
33
|
+
onPeerLeft: (userId, peerId) => {
|
|
34
|
+
peerSend.delete(peerId);
|
|
35
|
+
emit({ kind: "peer-left", userId, peerId });
|
|
36
36
|
},
|
|
37
37
|
onMessage: (type, payload, from) => {
|
|
38
38
|
// We can also learn peerSend via onMessage in case join events vary
|
|
39
|
-
peerSend.set(from.
|
|
40
|
-
emit({ kind: "message", type, payload, fromUserId: from.userId });
|
|
39
|
+
peerSend.set(from.peerId, from.receive);
|
|
40
|
+
emit({ kind: "message", type, payload, fromUserId: from.userId, fromPeerId: from.peerId });
|
|
41
41
|
},
|
|
42
42
|
});
|
|
43
43
|
exitRoom = result.exitRoom;
|
|
44
44
|
return;
|
|
45
45
|
}
|
|
46
46
|
if (msg.cmd === "send") {
|
|
47
|
-
const sendFn = peerSend.get(msg.
|
|
47
|
+
const sendFn = peerSend.get(msg.toPeerId);
|
|
48
48
|
if (sendFn)
|
|
49
49
|
sendFn(msg.type, msg.payload);
|
|
50
50
|
return;
|
|
51
51
|
}
|
|
52
52
|
if (msg.cmd === "exit") {
|
|
53
53
|
exitRoom?.();
|
|
54
|
-
|
|
55
|
-
peerSend.clear();
|
|
54
|
+
self.close();
|
|
56
55
|
return;
|
|
57
56
|
}
|
|
58
57
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signal-room.worker.js","sourceRoot":"","sources":["../src/browser/signal-room.worker.ts"],"names":[],"mappings":"AAAA,iCAAiC;AAEjC,OAAO,EAAE,SAAS,EAAc,MAAM,uBAAuB,CAAC;AAgB9D,IAAI,QAAQ,GAAwB,IAAI,CAAC;AAEzC,gFAAgF;AAChF,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAgD,CAAC;AAEzE,SAAS,IAAI,CAAsB,EAAmB;IACnD,IAAmC,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;AACvD,CAAC;AAED,IAAI,CAAC,gBAAgB,CAAC,SAAS,EAAE,CAAC,CAA8B,EAAE,EAAE;IAClE,MAAM,GAAG,GAAG,CAAC,CAAC,IAAI,CAAC;IACnB,OAAO,CAAC,KAAK,CAAC,uCAAuC,EAAE,GAAG,CAAC,CAAC;IAE5D,IAAI,GAAG,CAAC,GAAG,KAAK,OAAO,EAAE,CAAC;QACxB,iCAAiC;QACjC,QAAQ,EAAE,EAAE,CAAC;QACb,QAAQ,GAAG,IAAI,CAAC;QAChB,QAAQ,CAAC,KAAK,EAAE,CAAC;QAEjB,MAAM,MAAM,GAAG,SAAS,CAAC;YACvB,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,MAAM,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;YACpC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;YACtC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;YACtC,OAAO,EAAE,CAAC,SAAiB,EAAE,GAAS,EAAE,EAAE;gBACxC,OAAO,CAAC,KAAK,CAAC,wBAAwB,SAAS,EAAE,EAAE,GAAG,CAAC,CAAC;gBACxD,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC;YACxC,CAAC;YACD,YAAY,EAAE,CAAC,IAAW,EAAE,EAAE;gBAC5B,wCAAwC;gBACxC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"signal-room.worker.js","sourceRoot":"","sources":["../src/browser/signal-room.worker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { EnterRoom } from "./signal-room";
|
|
2
|
+
export type SigType = "offer" | "answer" | "ice";
|
|
3
|
+
export type SigPayload = RTCSessionDescriptionInit | RTCIceCandidateInit;
|
|
4
|
+
export declare function collectPeerConnections({ userId, receivePeerConnection, leaveUserWithoutPeer, rtcConfig, enterRoomFunction: enterRoom, logLine, onLeaveUser, workerUrl, }: {
|
|
5
|
+
userId: string;
|
|
6
|
+
rtcConfig?: RTCConfiguration;
|
|
7
|
+
enterRoomFunction?: EnterRoom<SigType, SigPayload>;
|
|
8
|
+
onLeaveUser?: (userId: string) => void;
|
|
9
|
+
logLine?: (direction: string, obj?: any) => void;
|
|
10
|
+
workerUrl?: URL;
|
|
11
|
+
leaveUserWithoutPeer?: boolean;
|
|
12
|
+
receivePeerConnection(connection: {
|
|
13
|
+
pc: RTCPeerConnection;
|
|
14
|
+
userId: string;
|
|
15
|
+
initiator: boolean;
|
|
16
|
+
}): void;
|
|
17
|
+
}): {
|
|
18
|
+
enterRoom: ({ room, host }: {
|
|
19
|
+
room: string;
|
|
20
|
+
host: string;
|
|
21
|
+
}) => void;
|
|
22
|
+
exitRoom: ({ room, host }: {
|
|
23
|
+
room: string;
|
|
24
|
+
host: string;
|
|
25
|
+
}) => void;
|
|
26
|
+
leaveUser: (userId: string) => void;
|
|
27
|
+
getUsers(): string[];
|
|
28
|
+
getRooms(): {
|
|
29
|
+
room: string;
|
|
30
|
+
host: string;
|
|
31
|
+
exitRoom: () => void;
|
|
32
|
+
}[];
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=webrtc-peer-collector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webrtc-peer-collector.d.ts","sourceRoot":"","sources":["../src/browser/webrtc-peer-collector.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAa,MAAM,eAAe,CAAC;AAErD,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;AACjD,MAAM,MAAM,UAAU,GAAG,yBAAyB,GAAG,mBAAmB,CAAC;AAgBzE,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,EACN,qBAAqB,EACrB,oBAA4B,EAC5B,SAAsE,EACtE,iBAAiB,EAAE,SAA8B,EACjD,OAAuB,EACvB,WAAW,EACX,SAAS,GACV,EAAE;IACD,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,iBAAiB,CAAC,EAAE,SAAS,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IACnD,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IACjD,SAAS,CAAC,EAAE,GAAG,CAAC;IAChB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,qBAAqB,CAAC,UAAU,EAAE;QAAE,EAAE,EAAE,iBAAiB,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;CACxG;gCAoEgC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;KAAE;+BAThC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;KAAE;wBA1BlC,MAAM;;;cAwBY,MAAM;cAAQ,MAAM;kBAAY,MAAM,IAAI;;EAwGxF"}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { enterRoom } from "./signal-room";
|
|
2
|
+
const DEFAULT_ENTER_ROOM = enterRoom;
|
|
3
|
+
export function collectPeerConnections({ userId, receivePeerConnection, leaveUserWithoutPeer = false, rtcConfig = { iceServers: [{ urls: "stun:stun.l.google.com:19302" }] }, enterRoomFunction: enterRoom = DEFAULT_ENTER_ROOM, logLine = console.debug, onLeaveUser, workerUrl, }) {
|
|
4
|
+
const users = new Map();
|
|
5
|
+
function getPeer(peer) {
|
|
6
|
+
let state = users.get(peer.userId);
|
|
7
|
+
if (!state) {
|
|
8
|
+
const newState = {
|
|
9
|
+
userId: peer.userId,
|
|
10
|
+
pc: new RTCPeerConnection(rtcConfig),
|
|
11
|
+
pendingRemoteIce: [],
|
|
12
|
+
peers: new Set([peer]),
|
|
13
|
+
};
|
|
14
|
+
users.set(peer.userId, newState);
|
|
15
|
+
// Send local ICE candidates to this peer
|
|
16
|
+
newState.pc.onicecandidate = (ev) => {
|
|
17
|
+
if (!ev.candidate)
|
|
18
|
+
return;
|
|
19
|
+
for (let user of newState.peers) {
|
|
20
|
+
const success = user.receive("ice", ev.candidate.toJSON());
|
|
21
|
+
if (success)
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
newState.pc.onconnectionstatechange = () => {
|
|
26
|
+
logLine("💬", { event: "pc-state", userId: newState.userId, state: newState.pc.connectionState });
|
|
27
|
+
};
|
|
28
|
+
state = newState;
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
state.peers.add(peer);
|
|
32
|
+
}
|
|
33
|
+
users.set(state.userId, state);
|
|
34
|
+
return state;
|
|
35
|
+
}
|
|
36
|
+
function leaveUser(userId) {
|
|
37
|
+
onLeaveUser?.(userId);
|
|
38
|
+
const p = users.get(userId);
|
|
39
|
+
if (!p)
|
|
40
|
+
return;
|
|
41
|
+
try {
|
|
42
|
+
p.pc.close();
|
|
43
|
+
}
|
|
44
|
+
catch { }
|
|
45
|
+
users.delete(userId);
|
|
46
|
+
logLine("👤 USER LEFT", userId);
|
|
47
|
+
}
|
|
48
|
+
async function flushRemoteIce(state) {
|
|
49
|
+
if (!state.pc.remoteDescription)
|
|
50
|
+
return;
|
|
51
|
+
const queued = state.pendingRemoteIce;
|
|
52
|
+
state.pendingRemoteIce = [];
|
|
53
|
+
for (const ice of queued) {
|
|
54
|
+
try {
|
|
55
|
+
await state.pc.addIceCandidate(ice);
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
logLine("⚠️ ERROR", { error: "add-ice-failed", userId: state.userId, detail: String(e) });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
const roomsEntered = new Map();
|
|
63
|
+
function exit({ room, host }) {
|
|
64
|
+
const key = `${host}/room/${room}`;
|
|
65
|
+
const session = roomsEntered.get(key);
|
|
66
|
+
if (session) {
|
|
67
|
+
session.exitRoom();
|
|
68
|
+
roomsEntered.delete(key);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function enter({ room, host }) {
|
|
72
|
+
const { exitRoom } = enterRoom({
|
|
73
|
+
userId,
|
|
74
|
+
room,
|
|
75
|
+
host,
|
|
76
|
+
logLine,
|
|
77
|
+
workerUrl,
|
|
78
|
+
// Existing peers initiate to the newcomer (Option 1)
|
|
79
|
+
async onPeerJoined(user) {
|
|
80
|
+
const state = getPeer(user);
|
|
81
|
+
const pc = state.pc;
|
|
82
|
+
// Offer flow: createOffer -> setLocalDescription -> send localDescription
|
|
83
|
+
const offer = await pc.createOffer();
|
|
84
|
+
await pc.setLocalDescription(offer);
|
|
85
|
+
user.receive("offer", pc.localDescription?.toJSON());
|
|
86
|
+
},
|
|
87
|
+
onPeerLeft(userId, peerId) {
|
|
88
|
+
const state = users.get(userId);
|
|
89
|
+
if (!state)
|
|
90
|
+
return;
|
|
91
|
+
for (const user of state.peers) {
|
|
92
|
+
if (user.peerId === peerId) {
|
|
93
|
+
state.peers.delete(user);
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (state.peers.size === 0 && leaveUserWithoutPeer) {
|
|
98
|
+
leaveUser(userId);
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
async onMessage(type, payload, from) {
|
|
102
|
+
const state = getPeer(from);
|
|
103
|
+
const pc = state.pc;
|
|
104
|
+
if (type === "offer") {
|
|
105
|
+
// Responder: set remote offer
|
|
106
|
+
await pc.setRemoteDescription(payload);
|
|
107
|
+
// Create and send answer
|
|
108
|
+
const answer = await pc.createAnswer();
|
|
109
|
+
await pc.setLocalDescription(answer);
|
|
110
|
+
from.receive("answer", pc.localDescription?.toJSON());
|
|
111
|
+
// Now safe to apply any queued ICE from this peer
|
|
112
|
+
await flushRemoteIce(state);
|
|
113
|
+
receivePeerConnection({ pc, userId: from.userId, initiator: false });
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (type === "answer") {
|
|
117
|
+
// Initiator: set remote answer
|
|
118
|
+
await pc.setRemoteDescription(payload);
|
|
119
|
+
await flushRemoteIce(state);
|
|
120
|
+
receivePeerConnection({ pc, userId: from.userId, initiator: true });
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (type === "ice") {
|
|
124
|
+
const ice = payload;
|
|
125
|
+
// If we don't have remoteDescription yet, queue it
|
|
126
|
+
if (!pc.remoteDescription) {
|
|
127
|
+
state.pendingRemoteIce.push(ice);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
try {
|
|
131
|
+
await pc.addIceCandidate(ice);
|
|
132
|
+
}
|
|
133
|
+
catch (e) {
|
|
134
|
+
logLine("⚠️ ERROR", { error: "add-ice-failed", userId: state.userId, detail: String(e) });
|
|
135
|
+
}
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
roomsEntered.set(`${host}/room/${room}`, { exitRoom, room, host });
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
enterRoom: enter,
|
|
144
|
+
exitRoom: exit,
|
|
145
|
+
leaveUser,
|
|
146
|
+
getUsers() {
|
|
147
|
+
return Array.from(users.keys());
|
|
148
|
+
},
|
|
149
|
+
getRooms() {
|
|
150
|
+
return Array.from(roomsEntered.values());
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=webrtc-peer-collector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"webrtc-peer-collector.js","sourceRoot":"","sources":["../src/browser/webrtc-peer-collector.ts"],"names":[],"mappings":"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"}
|
package/dist/webrtc-room.d.ts
CHANGED
|
@@ -1,24 +1,52 @@
|
|
|
1
1
|
import { EnterRoom } from "./signal-room";
|
|
2
2
|
type SigType = "offer" | "answer" | "ice";
|
|
3
3
|
type SigPayload = RTCSessionDescriptionInit | RTCIceCandidateInit;
|
|
4
|
-
export declare function
|
|
4
|
+
export declare function collectPeerConnections({ userId, receivePeerConnection, leaveUserWithoutPeer, rtcConfig, enterRoomFunction: enterRoom, logLine, onLeaveUser, workerUrl, }: {
|
|
5
|
+
userId: string;
|
|
6
|
+
rtcConfig?: RTCConfiguration;
|
|
7
|
+
enterRoomFunction?: EnterRoom<SigType, SigPayload>;
|
|
8
|
+
onLeaveUser?: (userId: string) => void;
|
|
9
|
+
logLine?: (direction: string, obj?: any) => void;
|
|
10
|
+
workerUrl?: URL;
|
|
11
|
+
leaveUserWithoutPeer?: boolean;
|
|
12
|
+
receivePeerConnection(connection: {
|
|
13
|
+
pc: RTCPeerConnection;
|
|
14
|
+
userId: string;
|
|
15
|
+
initiator: boolean;
|
|
16
|
+
}): void;
|
|
17
|
+
}): {
|
|
18
|
+
enterRoom: ({ room, host }: {
|
|
19
|
+
room: string;
|
|
20
|
+
host: string;
|
|
21
|
+
}) => void;
|
|
22
|
+
exitRoom: ({ room, host }: {
|
|
23
|
+
room: string;
|
|
24
|
+
host: string;
|
|
25
|
+
}) => void;
|
|
26
|
+
leaveUser: (userId: string) => void;
|
|
27
|
+
getUsers(): string[];
|
|
28
|
+
};
|
|
29
|
+
export declare function joinWebRTCRoom({ uid, onMessage, logLine, enterRoomFunction, autoLeaveUsers, workerUrl, }: {
|
|
30
|
+
uid?: string;
|
|
5
31
|
onMessage?: (data: any, from: string) => void;
|
|
6
32
|
logLine?: (direction: string, obj?: any) => void;
|
|
7
|
-
|
|
33
|
+
enterRoomFunction?: EnterRoom<SigType, SigPayload>;
|
|
34
|
+
autoLeaveUsers?: boolean;
|
|
8
35
|
workerUrl?: URL;
|
|
9
36
|
}): {
|
|
10
|
-
userId:
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
end: () => void;
|
|
14
|
-
enter: ({ room, host }: {
|
|
37
|
+
userId: string;
|
|
38
|
+
send: (data: any, userId?: string) => void;
|
|
39
|
+
enterRoom: ({ room, host }: {
|
|
15
40
|
room: string;
|
|
16
41
|
host: string;
|
|
17
42
|
}) => void;
|
|
18
|
-
|
|
43
|
+
exitRoom: ({ room, host }: {
|
|
19
44
|
room: string;
|
|
20
45
|
host: string;
|
|
21
46
|
}) => void;
|
|
47
|
+
leaveUser: (userId: string) => void;
|
|
48
|
+
getUsers: () => string[];
|
|
49
|
+
end(): void;
|
|
22
50
|
};
|
|
23
51
|
export {};
|
|
24
52
|
//# sourceMappingURL=webrtc-room.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webrtc-room.d.ts","sourceRoot":"","sources":["../src/browser/webrtc-room.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"webrtc-room.d.ts","sourceRoot":"","sources":["../src/browser/webrtc-room.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAa,MAAM,eAAe,CAAC;AAErD,KAAK,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,KAAK,CAAC;AAC1C,KAAK,UAAU,GAAG,yBAAyB,GAAG,mBAAmB,CAAC;AAgBlE,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,EACN,qBAAqB,EACrB,oBAA4B,EAC5B,SAAsE,EACtE,iBAAiB,EAAE,SAA8B,EACjD,OAAuB,EACvB,WAAW,EACX,SAAS,GACV,EAAE;IACD,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B,iBAAiB,CAAC,EAAE,SAAS,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IACnD,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IACjD,SAAS,CAAC,EAAE,GAAG,CAAC;IAChB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,qBAAqB,CAAC,UAAU,EAAE;QAAE,EAAE,EAAE,iBAAiB,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI,CAAC;CACxG;gCAoEgC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;KAAE;+BAThC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;KAAE;wBA1BlC,MAAM;;EAsHlC;AAGD,wBAAgB,cAAc,CAAC,EAC7B,GAAG,EACH,SAAS,EACT,OAAuB,EACvB,iBAAsC,EACtC,cAAsB,EACtB,SAAS,GACV,EAAE;IACD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC9C,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;IACjD,iBAAiB,CAAC,EAAE,SAAS,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;IACnD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,SAAS,CAAC,EAAE,GAAG,CAAC;CACjB;;iBA+CqB,GAAG,WAAW,MAAM;gCAnJT;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;KAAE;+BAThC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;KAAE;wBA1BlC,MAAM;;;EA2MlC"}
|
package/dist/webrtc-room.js
CHANGED
|
@@ -1,91 +1,73 @@
|
|
|
1
1
|
import { enterRoom } from "./signal-room";
|
|
2
2
|
const DEFAULT_ENTER_ROOM = enterRoom;
|
|
3
|
-
export function
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
};
|
|
8
|
-
const peers = new Map();
|
|
9
|
-
function wireDataChannel(state) {
|
|
10
|
-
const dc = state.dataChannel;
|
|
11
|
-
if (!dc)
|
|
12
|
-
return;
|
|
13
|
-
dc.onopen = () => logLine("💬", { event: "dc-open", userId: state.userId });
|
|
14
|
-
dc.onmessage = (e) => {
|
|
15
|
-
onMessage?.(e.data, state.userId);
|
|
16
|
-
logLine("💬", { event: "dc-message", userId: state.userId, data: e.data });
|
|
17
|
-
};
|
|
18
|
-
dc.onclose = () => logLine("💬", { event: "dc-close", userId: state.userId });
|
|
19
|
-
dc.onerror = () => logLine("⚠️ ERROR", { error: "dc-error", userId: state.userId });
|
|
20
|
-
}
|
|
21
|
-
async function flushRemoteIce(state) {
|
|
22
|
-
if (!state.pc.remoteDescription)
|
|
23
|
-
return;
|
|
24
|
-
const queued = state.pendingRemoteIce;
|
|
25
|
-
state.pendingRemoteIce = [];
|
|
26
|
-
for (const ice of queued) {
|
|
27
|
-
try {
|
|
28
|
-
await state.pc.addIceCandidate(ice);
|
|
29
|
-
}
|
|
30
|
-
catch (e) {
|
|
31
|
-
logLine("⚠️ ERROR", { error: "add-ice-failed", userId: state.userId, detail: String(e) });
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
function getPeer(user) {
|
|
36
|
-
let state = peers.get(user.userId);
|
|
3
|
+
export function collectPeerConnections({ userId, receivePeerConnection, leaveUserWithoutPeer = false, rtcConfig = { iceServers: [{ urls: "stun:stun.l.google.com:19302" }] }, enterRoomFunction: enterRoom = DEFAULT_ENTER_ROOM, logLine = console.debug, onLeaveUser, workerUrl, }) {
|
|
4
|
+
const users = new Map();
|
|
5
|
+
function getPeer(peer) {
|
|
6
|
+
let state = users.get(peer.userId);
|
|
37
7
|
if (!state) {
|
|
38
8
|
const newState = {
|
|
39
|
-
userId:
|
|
9
|
+
userId: peer.userId,
|
|
40
10
|
pc: new RTCPeerConnection(rtcConfig),
|
|
41
11
|
pendingRemoteIce: [],
|
|
42
|
-
|
|
43
|
-
dataChannel: null,
|
|
12
|
+
peers: new Set([peer]),
|
|
44
13
|
};
|
|
45
|
-
|
|
14
|
+
users.set(peer.userId, newState);
|
|
46
15
|
// Send local ICE candidates to this peer
|
|
47
16
|
newState.pc.onicecandidate = (ev) => {
|
|
48
17
|
if (!ev.candidate)
|
|
49
18
|
return;
|
|
50
|
-
for (let user of newState.
|
|
19
|
+
for (let user of newState.peers) {
|
|
51
20
|
const success = user.receive("ice", ev.candidate.toJSON());
|
|
52
21
|
if (success)
|
|
53
22
|
break;
|
|
54
|
-
newState.users.delete(user);
|
|
55
23
|
}
|
|
56
24
|
};
|
|
57
|
-
// Responder receives DataChannel here
|
|
58
|
-
newState.pc.ondatachannel = (ev) => {
|
|
59
|
-
newState.dataChannel = ev.channel;
|
|
60
|
-
wireDataChannel(newState);
|
|
61
|
-
};
|
|
62
25
|
newState.pc.onconnectionstatechange = () => {
|
|
63
26
|
logLine("💬", { event: "pc-state", userId: newState.userId, state: newState.pc.connectionState });
|
|
64
27
|
};
|
|
65
28
|
state = newState;
|
|
66
29
|
}
|
|
67
30
|
else {
|
|
68
|
-
state.
|
|
31
|
+
state.peers.add(peer);
|
|
69
32
|
}
|
|
70
|
-
|
|
33
|
+
users.set(state.userId, state);
|
|
71
34
|
return state;
|
|
72
35
|
}
|
|
73
36
|
function leaveUser(userId) {
|
|
74
|
-
|
|
37
|
+
onLeaveUser?.(userId);
|
|
38
|
+
const p = users.get(userId);
|
|
75
39
|
if (!p)
|
|
76
40
|
return;
|
|
77
|
-
try {
|
|
78
|
-
p.dataChannel?.close();
|
|
79
|
-
}
|
|
80
|
-
catch { }
|
|
81
41
|
try {
|
|
82
42
|
p.pc.close();
|
|
83
43
|
}
|
|
84
44
|
catch { }
|
|
85
|
-
|
|
45
|
+
users.delete(userId);
|
|
86
46
|
logLine("👤 USER LEFT", userId);
|
|
87
47
|
}
|
|
48
|
+
async function flushRemoteIce(state) {
|
|
49
|
+
if (!state.pc.remoteDescription)
|
|
50
|
+
return;
|
|
51
|
+
const queued = state.pendingRemoteIce;
|
|
52
|
+
state.pendingRemoteIce = [];
|
|
53
|
+
for (const ice of queued) {
|
|
54
|
+
try {
|
|
55
|
+
await state.pc.addIceCandidate(ice);
|
|
56
|
+
}
|
|
57
|
+
catch (e) {
|
|
58
|
+
logLine("⚠️ ERROR", { error: "add-ice-failed", userId: state.userId, detail: String(e) });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
88
62
|
const roomsEntered = new Map();
|
|
63
|
+
function exit({ room, host }) {
|
|
64
|
+
const key = `${host}/room/${room}`;
|
|
65
|
+
const session = roomsEntered.get(key);
|
|
66
|
+
if (session) {
|
|
67
|
+
session.exitRoom();
|
|
68
|
+
roomsEntered.delete(key);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
89
71
|
function enter({ room, host }) {
|
|
90
72
|
const { exitRoom } = enterRoom({
|
|
91
73
|
userId,
|
|
@@ -94,42 +76,29 @@ export function joinWebRTCRoom({ onMessage, logLine = console.debug, enterRoom =
|
|
|
94
76
|
logLine,
|
|
95
77
|
workerUrl,
|
|
96
78
|
// Existing peers initiate to the newcomer (Option 1)
|
|
97
|
-
|
|
79
|
+
async onPeerJoined(user) {
|
|
98
80
|
const state = getPeer(user);
|
|
99
81
|
const pc = state.pc;
|
|
100
|
-
// Initiator creates the DataChannel
|
|
101
|
-
if (!state.dataChannel) {
|
|
102
|
-
state.dataChannel = pc.createDataChannel("data");
|
|
103
|
-
wireDataChannel(state);
|
|
104
|
-
}
|
|
105
82
|
// Offer flow: createOffer -> setLocalDescription -> send localDescription
|
|
106
83
|
const offer = await pc.createOffer();
|
|
107
84
|
await pc.setLocalDescription(offer);
|
|
108
|
-
user.receive("offer", pc.localDescription);
|
|
85
|
+
user.receive("offer", pc.localDescription?.toJSON());
|
|
109
86
|
},
|
|
110
|
-
onPeerLeft
|
|
111
|
-
const state =
|
|
87
|
+
onPeerLeft(userId, peerId) {
|
|
88
|
+
const state = users.get(userId);
|
|
112
89
|
if (!state)
|
|
113
90
|
return;
|
|
114
|
-
for (const user of state.
|
|
115
|
-
if (user.
|
|
116
|
-
state.
|
|
91
|
+
for (const user of state.peers) {
|
|
92
|
+
if (user.peerId === peerId) {
|
|
93
|
+
state.peers.delete(user);
|
|
117
94
|
break;
|
|
118
95
|
}
|
|
119
96
|
}
|
|
120
|
-
if (state.
|
|
121
|
-
try {
|
|
122
|
-
state.dataChannel?.close();
|
|
123
|
-
}
|
|
124
|
-
catch { }
|
|
125
|
-
try {
|
|
126
|
-
state.pc.close();
|
|
127
|
-
}
|
|
128
|
-
catch { }
|
|
97
|
+
if (state.peers.size === 0 && leaveUserWithoutPeer) {
|
|
129
98
|
leaveUser(userId);
|
|
130
99
|
}
|
|
131
100
|
},
|
|
132
|
-
|
|
101
|
+
async onMessage(type, payload, from) {
|
|
133
102
|
const state = getPeer(from);
|
|
134
103
|
const pc = state.pc;
|
|
135
104
|
if (type === "offer") {
|
|
@@ -138,15 +107,17 @@ export function joinWebRTCRoom({ onMessage, logLine = console.debug, enterRoom =
|
|
|
138
107
|
// Create and send answer
|
|
139
108
|
const answer = await pc.createAnswer();
|
|
140
109
|
await pc.setLocalDescription(answer);
|
|
141
|
-
from.receive("answer", pc.localDescription);
|
|
110
|
+
from.receive("answer", pc.localDescription?.toJSON());
|
|
142
111
|
// Now safe to apply any queued ICE from this peer
|
|
143
112
|
await flushRemoteIce(state);
|
|
113
|
+
receivePeerConnection({ pc, userId: from.userId, initiator: false });
|
|
144
114
|
return;
|
|
145
115
|
}
|
|
146
116
|
if (type === "answer") {
|
|
147
117
|
// Initiator: set remote answer
|
|
148
118
|
await pc.setRemoteDescription(payload);
|
|
149
119
|
await flushRemoteIce(state);
|
|
120
|
+
receivePeerConnection({ pc, userId: from.userId, initiator: true });
|
|
150
121
|
return;
|
|
151
122
|
}
|
|
152
123
|
if (type === "ice") {
|
|
@@ -166,50 +137,80 @@ export function joinWebRTCRoom({ onMessage, logLine = console.debug, enterRoom =
|
|
|
166
137
|
}
|
|
167
138
|
},
|
|
168
139
|
});
|
|
169
|
-
roomsEntered.set(`${host}/room/${room}`, { exitRoom
|
|
170
|
-
}
|
|
171
|
-
function exit({ room, host }) {
|
|
172
|
-
const key = `${host}/room/${room}`;
|
|
173
|
-
const session = roomsEntered.get(key);
|
|
174
|
-
if (session) {
|
|
175
|
-
session.exitRoom();
|
|
176
|
-
roomsEntered.delete(key);
|
|
177
|
-
}
|
|
140
|
+
roomsEntered.set(`${host}/room/${room}`, { exitRoom });
|
|
178
141
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
142
|
+
return { enterRoom: enter, exitRoom: exit, leaveUser, getUsers() { return Array.from(users.keys()); } };
|
|
143
|
+
}
|
|
144
|
+
export function joinWebRTCRoom({ uid, onMessage, logLine = console.debug, enterRoomFunction = DEFAULT_ENTER_ROOM, autoLeaveUsers = false, workerUrl, }) {
|
|
145
|
+
const userId = uid ?? `user-${crypto.randomUUID()}`;
|
|
146
|
+
const rtcConfig = {
|
|
147
|
+
iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
|
|
185
148
|
};
|
|
186
|
-
function
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
149
|
+
function wireDataChannel(userId, dc) {
|
|
150
|
+
dc.onopen = () => logLine("💬", { event: "dc-open", userId });
|
|
151
|
+
dc.onmessage = ({ data }) => {
|
|
152
|
+
onMessage?.(data, userId);
|
|
153
|
+
logLine("💬", { event: "dc-message", userId, data: data });
|
|
154
|
+
};
|
|
155
|
+
dc.onclose = () => logLine("💬", { event: "dc-close", userId });
|
|
156
|
+
dc.onerror = () => logLine("⚠️ ERROR", { error: "dc-error", userId });
|
|
157
|
+
}
|
|
158
|
+
const dataChannels = new Map();
|
|
159
|
+
const { enterRoom, exitRoom, leaveUser, getUsers } = collectPeerConnections({
|
|
160
|
+
userId,
|
|
161
|
+
rtcConfig,
|
|
162
|
+
enterRoomFunction,
|
|
163
|
+
logLine,
|
|
164
|
+
workerUrl,
|
|
165
|
+
leaveUserWithoutPeer: autoLeaveUsers,
|
|
166
|
+
onLeaveUser: (userId) => {
|
|
167
|
+
const dc = dataChannels.get(userId);
|
|
168
|
+
try {
|
|
169
|
+
dc?.close();
|
|
170
|
+
}
|
|
171
|
+
catch { }
|
|
172
|
+
dataChannels.delete(userId);
|
|
173
|
+
},
|
|
174
|
+
receivePeerConnection({ pc, userId, initiator }) {
|
|
175
|
+
if (initiator) {
|
|
176
|
+
const dc = pc.createDataChannel("data");
|
|
177
|
+
wireDataChannel(userId, dc);
|
|
178
|
+
dataChannels.set(userId, dc);
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
pc.ondatachannel = (ev) => {
|
|
182
|
+
const dc = ev.channel;
|
|
183
|
+
wireDataChannel(userId, dc);
|
|
184
|
+
dataChannels.set(userId, dc);
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
logLine("💬", { event: "pc-ready", userId, initiator });
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
function send(data, userId) {
|
|
191
|
+
dataChannels.forEach((dataChannel, pUserId) => {
|
|
192
|
+
if (userId && pUserId !== userId)
|
|
193
|
+
return;
|
|
194
|
+
if (dataChannel.readyState === "open")
|
|
195
|
+
dataChannel.send(data);
|
|
196
|
+
});
|
|
191
197
|
}
|
|
192
198
|
return {
|
|
193
199
|
userId,
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
+
send,
|
|
201
|
+
enterRoom,
|
|
202
|
+
exitRoom,
|
|
203
|
+
leaveUser,
|
|
204
|
+
getUsers,
|
|
205
|
+
end() {
|
|
206
|
+
dataChannels.forEach((dataChannel) => {
|
|
200
207
|
try {
|
|
201
|
-
|
|
208
|
+
dataChannel.close();
|
|
202
209
|
}
|
|
203
210
|
catch { }
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
207
|
-
catch { }
|
|
208
|
-
}
|
|
209
|
-
peers.clear();
|
|
211
|
+
});
|
|
212
|
+
dataChannels.clear();
|
|
210
213
|
},
|
|
211
|
-
enter,
|
|
212
|
-
exit,
|
|
213
214
|
};
|
|
214
215
|
}
|
|
215
216
|
//# sourceMappingURL=webrtc-room.js.map
|