@voicemaster/core 1.0.10 → 1.0.11

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/dist/index.d.mts CHANGED
@@ -17,6 +17,8 @@ declare class VoiceClient {
17
17
  private iceServers;
18
18
  private isConnectedFlag;
19
19
  private targetUserId;
20
+ private connectingPromise;
21
+ private peers;
20
22
  constructor(config: VoiceClientConfig);
21
23
  on(event: string, callback: Function): void;
22
24
  private emit;
@@ -31,6 +33,7 @@ declare class VoiceClient {
31
33
  private handleCandidate;
32
34
  private send;
33
35
  disconnect(): void;
36
+ getPeers(): string[];
34
37
  toggleMute(): void;
35
38
  isMuted(): boolean;
36
39
  getUserId(): string;
package/dist/index.d.ts CHANGED
@@ -17,6 +17,8 @@ declare class VoiceClient {
17
17
  private iceServers;
18
18
  private isConnectedFlag;
19
19
  private targetUserId;
20
+ private connectingPromise;
21
+ private peers;
20
22
  constructor(config: VoiceClientConfig);
21
23
  on(event: string, callback: Function): void;
22
24
  private emit;
@@ -31,6 +33,7 @@ declare class VoiceClient {
31
33
  private handleCandidate;
32
34
  private send;
33
35
  disconnect(): void;
36
+ getPeers(): string[];
34
37
  toggleMute(): void;
35
38
  isMuted(): boolean;
36
39
  getUserId(): string;
package/dist/index.js CHANGED
@@ -34,6 +34,8 @@ var VoiceClient = class {
34
34
  this.eventHandlers = /* @__PURE__ */ new Map();
35
35
  this.isConnectedFlag = false;
36
36
  this.targetUserId = null;
37
+ this.connectingPromise = null;
38
+ this.peers = /* @__PURE__ */ new Set();
37
39
  this.signalingUrl = config.signalingUrl;
38
40
  this.roomId = config.roomId;
39
41
  this.userId = config.userId;
@@ -41,7 +43,7 @@ var VoiceClient = class {
41
43
  { urls: "stun:stun.l.google.com:19302" }
42
44
  ];
43
45
  if (config.autoConnect !== false) {
44
- this.connect();
46
+ Promise.resolve().then(() => this.connect());
45
47
  }
46
48
  }
47
49
  on(event, callback) {
@@ -57,13 +59,22 @@ var VoiceClient = class {
57
59
  }
58
60
  }
59
61
  async connect() {
60
- try {
61
- await this.initMicrophone();
62
- this.initPeerConnection();
63
- this.initWebSocket();
64
- } catch (error) {
65
- this.emit("error", error);
62
+ if (this.connectingPromise) {
63
+ return this.connectingPromise;
66
64
  }
65
+ this.connectingPromise = (async () => {
66
+ try {
67
+ await this.initMicrophone();
68
+ this.initPeerConnection();
69
+ this.initWebSocket();
70
+ } catch (error) {
71
+ this.emit("error", error);
72
+ throw error;
73
+ } finally {
74
+ this.connectingPromise = null;
75
+ }
76
+ })();
77
+ return this.connectingPromise;
67
78
  }
68
79
  async initMicrophone() {
69
80
  this.localStream = await navigator.mediaDevices.getUserMedia({
@@ -81,7 +92,9 @@ var VoiceClient = class {
81
92
  this.pc.addTrack(track, this.localStream);
82
93
  });
83
94
  this.pc.ontrack = (event) => {
84
- this.remoteStream = event.streams[0];
95
+ const stream = event.streams?.[0] || new MediaStream([event.track]);
96
+ this.remoteStream = stream;
97
+ console.log("[VoiceClient] Remote track received", { track: event.track, stream });
85
98
  this.emit("remoteStream", this.remoteStream);
86
99
  };
87
100
  this.pc.onicecandidate = (event) => {
@@ -105,13 +118,19 @@ var VoiceClient = class {
105
118
  const url = `${this.signalingUrl}?userId=${this.userId}&roomId=${this.roomId}`;
106
119
  this.ws = new WebSocket(url);
107
120
  this.ws.onopen = () => {
121
+ console.log("[VoiceClient] WebSocket connected");
108
122
  this.send({ type: "join", roomId: this.roomId, userId: this.userId });
109
123
  };
110
124
  this.ws.onmessage = async (event) => {
111
125
  const message = JSON.parse(event.data);
112
126
  await this.handleSignalingMessage(message);
113
127
  };
128
+ this.ws.onerror = (event) => {
129
+ console.error("[VoiceClient] WebSocket error", event);
130
+ this.emit("error", new Error("WebSocket error"));
131
+ };
114
132
  this.ws.onclose = () => {
133
+ console.log("[VoiceClient] WebSocket closed");
115
134
  this.emit("disconnected");
116
135
  };
117
136
  }
@@ -121,6 +140,7 @@ var VoiceClient = class {
121
140
  case "user-joined":
122
141
  console.log("[VoiceClient] User joined, myId:", this.userId, "theirId:", message.userId);
123
142
  if (message.userId !== this.userId) {
143
+ this.peers.add(message.userId);
124
144
  console.log("[VoiceClient] Creating offer...");
125
145
  this.targetUserId = message.userId;
126
146
  await this.createOffer();
@@ -143,6 +163,7 @@ var VoiceClient = class {
143
163
  await this.handleCandidate(message);
144
164
  break;
145
165
  case "user-left":
166
+ this.peers.delete(message.userId);
146
167
  this.emit("userLeft", message.userId);
147
168
  break;
148
169
  default:
@@ -193,9 +214,12 @@ var VoiceClient = class {
193
214
  send(data) {
194
215
  if (this.ws?.readyState === WebSocket.OPEN) {
195
216
  this.ws.send(JSON.stringify(data));
217
+ } else {
218
+ console.warn("[VoiceClient] WebSocket is not open, cannot send message", data);
196
219
  }
197
220
  }
198
221
  disconnect() {
222
+ this.peers.clear();
199
223
  if (this.ws) {
200
224
  this.send({ type: "leave", roomId: this.roomId, userId: this.userId });
201
225
  this.ws.close();
@@ -207,6 +231,9 @@ var VoiceClient = class {
207
231
  this.isConnectedFlag = false;
208
232
  this.emit("disconnected");
209
233
  }
234
+ getPeers() {
235
+ return Array.from(this.peers);
236
+ }
210
237
  toggleMute() {
211
238
  if (this.localStream) {
212
239
  const audioTrack = this.localStream.getAudioTracks()[0];
package/dist/index.mjs CHANGED
@@ -8,6 +8,8 @@ var VoiceClient = class {
8
8
  this.eventHandlers = /* @__PURE__ */ new Map();
9
9
  this.isConnectedFlag = false;
10
10
  this.targetUserId = null;
11
+ this.connectingPromise = null;
12
+ this.peers = /* @__PURE__ */ new Set();
11
13
  this.signalingUrl = config.signalingUrl;
12
14
  this.roomId = config.roomId;
13
15
  this.userId = config.userId;
@@ -15,7 +17,7 @@ var VoiceClient = class {
15
17
  { urls: "stun:stun.l.google.com:19302" }
16
18
  ];
17
19
  if (config.autoConnect !== false) {
18
- this.connect();
20
+ Promise.resolve().then(() => this.connect());
19
21
  }
20
22
  }
21
23
  on(event, callback) {
@@ -31,13 +33,22 @@ var VoiceClient = class {
31
33
  }
32
34
  }
33
35
  async connect() {
34
- try {
35
- await this.initMicrophone();
36
- this.initPeerConnection();
37
- this.initWebSocket();
38
- } catch (error) {
39
- this.emit("error", error);
36
+ if (this.connectingPromise) {
37
+ return this.connectingPromise;
40
38
  }
39
+ this.connectingPromise = (async () => {
40
+ try {
41
+ await this.initMicrophone();
42
+ this.initPeerConnection();
43
+ this.initWebSocket();
44
+ } catch (error) {
45
+ this.emit("error", error);
46
+ throw error;
47
+ } finally {
48
+ this.connectingPromise = null;
49
+ }
50
+ })();
51
+ return this.connectingPromise;
41
52
  }
42
53
  async initMicrophone() {
43
54
  this.localStream = await navigator.mediaDevices.getUserMedia({
@@ -55,7 +66,9 @@ var VoiceClient = class {
55
66
  this.pc.addTrack(track, this.localStream);
56
67
  });
57
68
  this.pc.ontrack = (event) => {
58
- this.remoteStream = event.streams[0];
69
+ const stream = event.streams?.[0] || new MediaStream([event.track]);
70
+ this.remoteStream = stream;
71
+ console.log("[VoiceClient] Remote track received", { track: event.track, stream });
59
72
  this.emit("remoteStream", this.remoteStream);
60
73
  };
61
74
  this.pc.onicecandidate = (event) => {
@@ -79,13 +92,19 @@ var VoiceClient = class {
79
92
  const url = `${this.signalingUrl}?userId=${this.userId}&roomId=${this.roomId}`;
80
93
  this.ws = new WebSocket(url);
81
94
  this.ws.onopen = () => {
95
+ console.log("[VoiceClient] WebSocket connected");
82
96
  this.send({ type: "join", roomId: this.roomId, userId: this.userId });
83
97
  };
84
98
  this.ws.onmessage = async (event) => {
85
99
  const message = JSON.parse(event.data);
86
100
  await this.handleSignalingMessage(message);
87
101
  };
102
+ this.ws.onerror = (event) => {
103
+ console.error("[VoiceClient] WebSocket error", event);
104
+ this.emit("error", new Error("WebSocket error"));
105
+ };
88
106
  this.ws.onclose = () => {
107
+ console.log("[VoiceClient] WebSocket closed");
89
108
  this.emit("disconnected");
90
109
  };
91
110
  }
@@ -95,6 +114,7 @@ var VoiceClient = class {
95
114
  case "user-joined":
96
115
  console.log("[VoiceClient] User joined, myId:", this.userId, "theirId:", message.userId);
97
116
  if (message.userId !== this.userId) {
117
+ this.peers.add(message.userId);
98
118
  console.log("[VoiceClient] Creating offer...");
99
119
  this.targetUserId = message.userId;
100
120
  await this.createOffer();
@@ -117,6 +137,7 @@ var VoiceClient = class {
117
137
  await this.handleCandidate(message);
118
138
  break;
119
139
  case "user-left":
140
+ this.peers.delete(message.userId);
120
141
  this.emit("userLeft", message.userId);
121
142
  break;
122
143
  default:
@@ -167,9 +188,12 @@ var VoiceClient = class {
167
188
  send(data) {
168
189
  if (this.ws?.readyState === WebSocket.OPEN) {
169
190
  this.ws.send(JSON.stringify(data));
191
+ } else {
192
+ console.warn("[VoiceClient] WebSocket is not open, cannot send message", data);
170
193
  }
171
194
  }
172
195
  disconnect() {
196
+ this.peers.clear();
173
197
  if (this.ws) {
174
198
  this.send({ type: "leave", roomId: this.roomId, userId: this.userId });
175
199
  this.ws.close();
@@ -181,6 +205,9 @@ var VoiceClient = class {
181
205
  this.isConnectedFlag = false;
182
206
  this.emit("disconnected");
183
207
  }
208
+ getPeers() {
209
+ return Array.from(this.peers);
210
+ }
184
211
  toggleMute() {
185
212
  if (this.localStream) {
186
213
  const audioTrack = this.localStream.getAudioTracks()[0];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voicemaster/core",
3
- "version": "1.0.10",
3
+ "version": "1.0.11",
4
4
  "description": "WebRTC voice communication core library",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -18,6 +18,8 @@ export class VoiceClient {
18
18
  private iceServers: RTCIceServer[];
19
19
  private isConnectedFlag = false;
20
20
  private targetUserId: string | null = null;
21
+ private connectingPromise: Promise<void> | null = null;
22
+ private peers: Set<string> = new Set();
21
23
 
22
24
  constructor(config: VoiceClientConfig) {
23
25
  this.signalingUrl = config.signalingUrl;
@@ -28,7 +30,7 @@ export class VoiceClient {
28
30
  ];
29
31
 
30
32
  if (config.autoConnect !== false) {
31
- this.connect();
33
+ Promise.resolve().then(() => this.connect());
32
34
  }
33
35
  }
34
36
 
@@ -49,13 +51,24 @@ export class VoiceClient {
49
51
  }
50
52
 
51
53
  async connect(): Promise<void> {
52
- try {
53
- await this.initMicrophone();
54
- this.initPeerConnection();
55
- this.initWebSocket();
56
- } catch (error) {
57
- this.emit('error', error);
54
+ if (this.connectingPromise) {
55
+ return this.connectingPromise;
58
56
  }
57
+
58
+ this.connectingPromise = (async () => {
59
+ try {
60
+ await this.initMicrophone();
61
+ this.initPeerConnection();
62
+ this.initWebSocket();
63
+ } catch (error) {
64
+ this.emit('error', error);
65
+ throw error;
66
+ } finally {
67
+ this.connectingPromise = null;
68
+ }
69
+ })();
70
+
71
+ return this.connectingPromise;
59
72
  }
60
73
 
61
74
  private async initMicrophone(): Promise<void> {
@@ -77,7 +90,9 @@ export class VoiceClient {
77
90
  });
78
91
 
79
92
  this.pc.ontrack = (event) => {
80
- this.remoteStream = event.streams[0];
93
+ const stream = event.streams?.[0] || new MediaStream([event.track]);
94
+ this.remoteStream = stream;
95
+ console.log('[VoiceClient] Remote track received', { track: event.track, stream });
81
96
  this.emit('remoteStream', this.remoteStream);
82
97
  };
83
98
 
@@ -105,6 +120,7 @@ export class VoiceClient {
105
120
  this.ws = new WebSocket(url);
106
121
 
107
122
  this.ws.onopen = () => {
123
+ console.log('[VoiceClient] WebSocket connected');
108
124
  this.send({ type: 'join', roomId: this.roomId, userId: this.userId });
109
125
  };
110
126
 
@@ -113,7 +129,13 @@ export class VoiceClient {
113
129
  await this.handleSignalingMessage(message);
114
130
  };
115
131
 
132
+ this.ws.onerror = (event) => {
133
+ console.error('[VoiceClient] WebSocket error', event);
134
+ this.emit('error', new Error('WebSocket error'));
135
+ };
136
+
116
137
  this.ws.onclose = () => {
138
+ console.log('[VoiceClient] WebSocket closed');
117
139
  this.emit('disconnected');
118
140
  };
119
141
  }
@@ -125,6 +147,7 @@ export class VoiceClient {
125
147
  case 'user-joined':
126
148
  console.log('[VoiceClient] User joined, myId:', this.userId, 'theirId:', message.userId);
127
149
  if (message.userId !== this.userId) {
150
+ this.peers.add(message.userId);
128
151
  console.log('[VoiceClient] Creating offer...');
129
152
  this.targetUserId = message.userId;
130
153
  await this.createOffer();
@@ -147,6 +170,7 @@ export class VoiceClient {
147
170
  await this.handleCandidate(message);
148
171
  break;
149
172
  case 'user-left':
173
+ this.peers.delete(message.userId);
150
174
  this.emit('userLeft', message.userId);
151
175
  break;
152
176
  default:
@@ -205,10 +229,13 @@ export class VoiceClient {
205
229
  private send(data: any): void {
206
230
  if (this.ws?.readyState === WebSocket.OPEN) {
207
231
  this.ws.send(JSON.stringify(data));
232
+ } else {
233
+ console.warn('[VoiceClient] WebSocket is not open, cannot send message', data);
208
234
  }
209
235
  }
210
236
 
211
237
  disconnect(): void {
238
+ this.peers.clear();
212
239
  if (this.ws) {
213
240
  this.send({ type: 'leave', roomId: this.roomId, userId: this.userId });
214
241
  this.ws.close();
@@ -221,6 +248,10 @@ export class VoiceClient {
221
248
  this.emit('disconnected');
222
249
  }
223
250
 
251
+ getPeers(): string[] {
252
+ return Array.from(this.peers);
253
+ }
254
+
224
255
  toggleMute(): void {
225
256
  if (this.localStream) {
226
257
  const audioTrack = this.localStream.getAudioTracks()[0];