@voicemaster/core 1.0.11 → 1.0.13

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
@@ -10,12 +10,14 @@ declare class VoiceClient {
10
10
  private pc;
11
11
  private localStream;
12
12
  private remoteStream;
13
+ private audioSender;
13
14
  private eventHandlers;
14
15
  private signalingUrl;
15
16
  private roomId;
16
17
  private userId;
17
18
  private iceServers;
18
19
  private isConnectedFlag;
20
+ private isMutedFlag;
19
21
  private targetUserId;
20
22
  private connectingPromise;
21
23
  private peers;
package/dist/index.d.ts CHANGED
@@ -10,12 +10,14 @@ declare class VoiceClient {
10
10
  private pc;
11
11
  private localStream;
12
12
  private remoteStream;
13
+ private audioSender;
13
14
  private eventHandlers;
14
15
  private signalingUrl;
15
16
  private roomId;
16
17
  private userId;
17
18
  private iceServers;
18
19
  private isConnectedFlag;
20
+ private isMutedFlag;
19
21
  private targetUserId;
20
22
  private connectingPromise;
21
23
  private peers;
package/dist/index.js CHANGED
@@ -31,8 +31,10 @@ var VoiceClient = class {
31
31
  this.pc = null;
32
32
  this.localStream = null;
33
33
  this.remoteStream = null;
34
+ this.audioSender = null;
34
35
  this.eventHandlers = /* @__PURE__ */ new Map();
35
36
  this.isConnectedFlag = false;
37
+ this.isMutedFlag = false;
36
38
  this.targetUserId = null;
37
39
  this.connectingPromise = null;
38
40
  this.peers = /* @__PURE__ */ new Set();
@@ -89,12 +91,20 @@ var VoiceClient = class {
89
91
  initPeerConnection() {
90
92
  this.pc = new RTCPeerConnection({ iceServers: this.iceServers });
91
93
  this.localStream?.getTracks().forEach((track) => {
92
- this.pc.addTrack(track, this.localStream);
94
+ const sender = this.pc.addTrack(track, this.localStream);
95
+ if (track.kind === "audio") {
96
+ this.audioSender = sender;
97
+ }
93
98
  });
94
99
  this.pc.ontrack = (event) => {
95
100
  const stream = event.streams?.[0] || new MediaStream([event.track]);
96
101
  this.remoteStream = stream;
97
- console.log("[VoiceClient] Remote track received", { track: event.track, stream });
102
+ console.log("[VoiceClient] Remote track received from peer", {
103
+ track: event.track,
104
+ stream,
105
+ fromUserId: this.targetUserId,
106
+ myUserId: this.userId
107
+ });
98
108
  this.emit("remoteStream", this.remoteStream);
99
109
  };
100
110
  this.pc.onicecandidate = (event) => {
@@ -235,15 +245,39 @@ var VoiceClient = class {
235
245
  return Array.from(this.peers);
236
246
  }
237
247
  toggleMute() {
238
- if (this.localStream) {
239
- const audioTrack = this.localStream.getAudioTracks()[0];
240
- if (audioTrack) {
241
- audioTrack.enabled = !audioTrack.enabled;
242
- }
248
+ if (!this.localStream) {
249
+ console.warn("[VoiceClient] No local stream available for muting");
250
+ return;
251
+ }
252
+ const audioTrack = this.localStream.getAudioTracks()[0];
253
+ if (!audioTrack) {
254
+ console.warn("[VoiceClient] No audio track found for muting");
255
+ return;
243
256
  }
257
+ const shouldMute = audioTrack.enabled;
258
+ audioTrack.enabled = !shouldMute;
259
+ this.isMutedFlag = !shouldMute;
260
+ if (this.audioSender) {
261
+ this.audioSender.replaceTrack(shouldMute ? null : audioTrack).then(() => {
262
+ console.log("[VoiceClient] Audio sender replaced after mute toggle", {
263
+ muted: this.isMutedFlag,
264
+ trackId: audioTrack.id,
265
+ enabled: audioTrack.enabled
266
+ });
267
+ }).catch((err) => {
268
+ console.error("[VoiceClient] Failed to replace audio sender track", err);
269
+ });
270
+ }
271
+ console.log(`[VoiceClient] Mute toggled: now ${this.isMutedFlag ? "muted" : "unmuted"}`, {
272
+ trackId: audioTrack.id,
273
+ enabled: audioTrack.enabled,
274
+ state: audioTrack.readyState
275
+ });
244
276
  }
245
277
  isMuted() {
246
- return this.localStream?.getAudioTracks()[0]?.enabled === false;
278
+ const muted = this.isMutedFlag;
279
+ console.log("[VoiceClient] isMuted check:", { muted });
280
+ return muted;
247
281
  }
248
282
  getUserId() {
249
283
  return this.userId;
package/dist/index.mjs CHANGED
@@ -5,8 +5,10 @@ var VoiceClient = class {
5
5
  this.pc = null;
6
6
  this.localStream = null;
7
7
  this.remoteStream = null;
8
+ this.audioSender = null;
8
9
  this.eventHandlers = /* @__PURE__ */ new Map();
9
10
  this.isConnectedFlag = false;
11
+ this.isMutedFlag = false;
10
12
  this.targetUserId = null;
11
13
  this.connectingPromise = null;
12
14
  this.peers = /* @__PURE__ */ new Set();
@@ -63,12 +65,20 @@ var VoiceClient = class {
63
65
  initPeerConnection() {
64
66
  this.pc = new RTCPeerConnection({ iceServers: this.iceServers });
65
67
  this.localStream?.getTracks().forEach((track) => {
66
- this.pc.addTrack(track, this.localStream);
68
+ const sender = this.pc.addTrack(track, this.localStream);
69
+ if (track.kind === "audio") {
70
+ this.audioSender = sender;
71
+ }
67
72
  });
68
73
  this.pc.ontrack = (event) => {
69
74
  const stream = event.streams?.[0] || new MediaStream([event.track]);
70
75
  this.remoteStream = stream;
71
- console.log("[VoiceClient] Remote track received", { track: event.track, stream });
76
+ console.log("[VoiceClient] Remote track received from peer", {
77
+ track: event.track,
78
+ stream,
79
+ fromUserId: this.targetUserId,
80
+ myUserId: this.userId
81
+ });
72
82
  this.emit("remoteStream", this.remoteStream);
73
83
  };
74
84
  this.pc.onicecandidate = (event) => {
@@ -209,15 +219,39 @@ var VoiceClient = class {
209
219
  return Array.from(this.peers);
210
220
  }
211
221
  toggleMute() {
212
- if (this.localStream) {
213
- const audioTrack = this.localStream.getAudioTracks()[0];
214
- if (audioTrack) {
215
- audioTrack.enabled = !audioTrack.enabled;
216
- }
222
+ if (!this.localStream) {
223
+ console.warn("[VoiceClient] No local stream available for muting");
224
+ return;
225
+ }
226
+ const audioTrack = this.localStream.getAudioTracks()[0];
227
+ if (!audioTrack) {
228
+ console.warn("[VoiceClient] No audio track found for muting");
229
+ return;
217
230
  }
231
+ const shouldMute = audioTrack.enabled;
232
+ audioTrack.enabled = !shouldMute;
233
+ this.isMutedFlag = !shouldMute;
234
+ if (this.audioSender) {
235
+ this.audioSender.replaceTrack(shouldMute ? null : audioTrack).then(() => {
236
+ console.log("[VoiceClient] Audio sender replaced after mute toggle", {
237
+ muted: this.isMutedFlag,
238
+ trackId: audioTrack.id,
239
+ enabled: audioTrack.enabled
240
+ });
241
+ }).catch((err) => {
242
+ console.error("[VoiceClient] Failed to replace audio sender track", err);
243
+ });
244
+ }
245
+ console.log(`[VoiceClient] Mute toggled: now ${this.isMutedFlag ? "muted" : "unmuted"}`, {
246
+ trackId: audioTrack.id,
247
+ enabled: audioTrack.enabled,
248
+ state: audioTrack.readyState
249
+ });
218
250
  }
219
251
  isMuted() {
220
- return this.localStream?.getAudioTracks()[0]?.enabled === false;
252
+ const muted = this.isMutedFlag;
253
+ console.log("[VoiceClient] isMuted check:", { muted });
254
+ return muted;
221
255
  }
222
256
  getUserId() {
223
257
  return this.userId;
package/package.json CHANGED
@@ -1,10 +1,16 @@
1
1
  {
2
2
  "name": "@voicemaster/core",
3
- "version": "1.0.11",
3
+ "version": "1.0.13",
4
4
  "description": "WebRTC voice communication core library",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
7
7
  "types": "./dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "publishConfig": {
12
+ "access": "public"
13
+ },
8
14
  "exports": {
9
15
  ".": {
10
16
  "types": "./dist/index.d.ts",
@@ -1,271 +0,0 @@
1
- export interface VoiceClientConfig {
2
- signalingUrl: string;
3
- roomId: string;
4
- userId: string;
5
- autoConnect?: boolean;
6
- iceServers?: RTCIceServer[];
7
- }
8
-
9
- export class VoiceClient {
10
- private ws: WebSocket | null = null;
11
- private pc: RTCPeerConnection | null = null;
12
- private localStream: MediaStream | null = null;
13
- private remoteStream: MediaStream | null = null;
14
- private eventHandlers: Map<string, Function[]> = new Map();
15
- private signalingUrl: string;
16
- private roomId: string;
17
- private userId: string;
18
- private iceServers: RTCIceServer[];
19
- private isConnectedFlag = false;
20
- private targetUserId: string | null = null;
21
- private connectingPromise: Promise<void> | null = null;
22
- private peers: Set<string> = new Set();
23
-
24
- constructor(config: VoiceClientConfig) {
25
- this.signalingUrl = config.signalingUrl;
26
- this.roomId = config.roomId;
27
- this.userId = config.userId;
28
- this.iceServers = config.iceServers || [
29
- { urls: 'stun:stun.l.google.com:19302' }
30
- ];
31
-
32
- if (config.autoConnect !== false) {
33
- Promise.resolve().then(() => this.connect());
34
- }
35
- }
36
-
37
- on(event: string, callback: Function): void {
38
- if (!this.eventHandlers.has(event)) {
39
- this.eventHandlers.set(event, []);
40
- }
41
- this.eventHandlers.get(event)!.push(callback);
42
- }
43
-
44
-
45
-
46
- private emit(event: string, ...args: any[]): void {
47
- const handlers = this.eventHandlers.get(event);
48
- if (handlers) {
49
- handlers.forEach(handler => handler(...args));
50
- }
51
- }
52
-
53
- async connect(): Promise<void> {
54
- if (this.connectingPromise) {
55
- return this.connectingPromise;
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;
72
- }
73
-
74
- private async initMicrophone(): Promise<void> {
75
- this.localStream = await navigator.mediaDevices.getUserMedia({
76
- audio: {
77
- echoCancellation: true,
78
- noiseSuppression: true,
79
- autoGainControl: true
80
- }
81
- });
82
- this.emit('localStream', this.localStream);
83
- }
84
-
85
- private initPeerConnection(): void {
86
- this.pc = new RTCPeerConnection({ iceServers: this.iceServers });
87
-
88
- this.localStream?.getTracks().forEach(track => {
89
- this.pc!.addTrack(track, this.localStream!);
90
- });
91
-
92
- this.pc.ontrack = (event) => {
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 });
96
- this.emit('remoteStream', this.remoteStream);
97
- };
98
-
99
- this.pc.onicecandidate = (event) => {
100
- if (event.candidate) {
101
- this.send({
102
- type: 'candidate',
103
- candidate: event.candidate
104
- });
105
- }
106
- };
107
-
108
- this.pc.onconnectionstatechange = () => {
109
- if (this.pc?.connectionState === 'connected') {
110
- if (!this.isConnectedFlag) {
111
- this.isConnectedFlag = true;
112
- this.emit('connected');
113
- }
114
- }
115
- };
116
- }
117
-
118
- private initWebSocket(): void {
119
- const url = `${this.signalingUrl}?userId=${this.userId}&roomId=${this.roomId}`;
120
- this.ws = new WebSocket(url);
121
-
122
- this.ws.onopen = () => {
123
- console.log('[VoiceClient] WebSocket connected');
124
- this.send({ type: 'join', roomId: this.roomId, userId: this.userId });
125
- };
126
-
127
- this.ws.onmessage = async (event) => {
128
- const message = JSON.parse(event.data);
129
- await this.handleSignalingMessage(message);
130
- };
131
-
132
- this.ws.onerror = (event) => {
133
- console.error('[VoiceClient] WebSocket error', event);
134
- this.emit('error', new Error('WebSocket error'));
135
- };
136
-
137
- this.ws.onclose = () => {
138
- console.log('[VoiceClient] WebSocket closed');
139
- this.emit('disconnected');
140
- };
141
- }
142
-
143
- private async handleSignalingMessage(message: any): Promise<void> {
144
- console.log('[VoiceClient] Received message:', message.type, message);
145
-
146
- switch (message.type) {
147
- case 'user-joined':
148
- console.log('[VoiceClient] User joined, myId:', this.userId, 'theirId:', message.userId);
149
- if (message.userId !== this.userId) {
150
- this.peers.add(message.userId);
151
- console.log('[VoiceClient] Creating offer...');
152
- this.targetUserId = message.userId;
153
- await this.createOffer();
154
- this.emit('userJoined', message.userId);
155
- } else {
156
- console.log('[VoiceClient] Ignoring my own join');
157
- }
158
- break;
159
- case 'offer':
160
- console.log('[VoiceClient] Received offer');
161
- this.targetUserId = message.userId;
162
- await this.handleOffer(message);
163
- break;
164
- case 'answer':
165
- console.log('[VoiceClient] Received answer');
166
- await this.handleAnswer(message);
167
- break;
168
- case 'candidate':
169
- console.log('[VoiceClient] Received candidate');
170
- await this.handleCandidate(message);
171
- break;
172
- case 'user-left':
173
- this.peers.delete(message.userId);
174
- this.emit('userLeft', message.userId);
175
- break;
176
- default:
177
- console.log('[VoiceClient] Unknown message type:', message.type);
178
- }
179
- }
180
-
181
- private async createOffer(): Promise<void> {
182
- const offer = await this.pc!.createOffer();
183
- await this.pc!.setLocalDescription(offer);
184
- this.send({
185
- type: 'offer',
186
- sdp: this.pc!.localDescription
187
- });
188
- }
189
-
190
- private async handleOffer(message: any): Promise<void> {
191
- try {
192
- console.log('Handling offer', message);
193
- const offer = new RTCSessionDescription(message.sdp);
194
- await this.pc!.setRemoteDescription(offer);
195
- console.log('Remote description set');
196
-
197
- const answer = await this.pc!.createAnswer();
198
- console.log('Answer created');
199
-
200
- await this.pc!.setLocalDescription(answer);
201
- console.log('Local description set');
202
-
203
- this.send({
204
- type: 'answer',
205
- sdp: this.pc!.localDescription
206
- });
207
- } catch (err) {
208
- console.error('Offer handling error:', err);
209
- }
210
- }
211
-
212
- private async handleAnswer(message: any): Promise<void> {
213
- const answer = new RTCSessionDescription(message.sdp);
214
- await this.pc!.setRemoteDescription(answer);
215
- }
216
-
217
- private async handleCandidate(message: any): Promise<void> {
218
- try {
219
- if (message.candidate) {
220
- const candidate = new RTCIceCandidate(message.candidate);
221
- await this.pc!.addIceCandidate(candidate);
222
- console.log('ICE candidate added');
223
- }
224
- } catch (err) {
225
- console.error('Candidate error:', err);
226
- }
227
- }
228
-
229
- private send(data: any): void {
230
- if (this.ws?.readyState === WebSocket.OPEN) {
231
- this.ws.send(JSON.stringify(data));
232
- } else {
233
- console.warn('[VoiceClient] WebSocket is not open, cannot send message', data);
234
- }
235
- }
236
-
237
- disconnect(): void {
238
- this.peers.clear();
239
- if (this.ws) {
240
- this.send({ type: 'leave', roomId: this.roomId, userId: this.userId });
241
- this.ws.close();
242
- }
243
- this.pc?.close();
244
- if (this.localStream) {
245
- this.localStream.getTracks().forEach(track => track.stop());
246
- }
247
- this.isConnectedFlag = false;
248
- this.emit('disconnected');
249
- }
250
-
251
- getPeers(): string[] {
252
- return Array.from(this.peers);
253
- }
254
-
255
- toggleMute(): void {
256
- if (this.localStream) {
257
- const audioTrack = this.localStream.getAudioTracks()[0];
258
- if (audioTrack) {
259
- audioTrack.enabled = !audioTrack.enabled;
260
- }
261
- }
262
- }
263
-
264
- isMuted(): boolean {
265
- return this.localStream?.getAudioTracks()[0]?.enabled === false;
266
- }
267
-
268
- getUserId(): string {
269
- return this.userId;
270
- }
271
- }
package/src/index.ts DELETED
@@ -1,2 +0,0 @@
1
- export { VoiceClient } from './VoiceClient';
2
- export type { VoiceClientConfig } from './VoiceClient';
package/tsconfig.json DELETED
@@ -1,16 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "ESNext",
5
- "lib": ["ES2020", "DOM"],
6
- "moduleResolution": "bundler",
7
- "strict": true,
8
- "esModuleInterop": true,
9
- "skipLibCheck": true,
10
- "forceConsistentCasingInFileNames": true,
11
- "resolveJsonModule": true,
12
- "noEmit": true
13
- },
14
- "include": ["src/**/*"],
15
- "exclude": ["node_modules", "dist", "**/*.test.ts"]
16
- }