@phystack/hub-client 4.5.19-dev → 4.5.20-dev

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.
Files changed (119) hide show
  1. package/dist/index.d.ts +22 -28
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +252 -378
  4. package/dist/index.js.map +1 -1
  5. package/dist/peripheral-twin.d.ts +34 -0
  6. package/dist/peripheral-twin.d.ts.map +1 -0
  7. package/dist/peripheral-twin.js +234 -0
  8. package/dist/peripheral-twin.js.map +1 -0
  9. package/dist/services/phyhub-connection.service.d.ts +1 -0
  10. package/dist/services/phyhub-connection.service.d.ts.map +1 -1
  11. package/dist/services/phyhub-connection.service.js +17 -0
  12. package/dist/services/phyhub-connection.service.js.map +1 -1
  13. package/dist/services/phyhub-direct-connection.service.d.ts +21 -0
  14. package/dist/services/phyhub-direct-connection.service.d.ts.map +1 -0
  15. package/dist/services/phyhub-direct-connection.service.js +101 -0
  16. package/dist/services/phyhub-direct-connection.service.js.map +1 -0
  17. package/dist/services/webrtc/data-channel-handler.d.ts +45 -0
  18. package/dist/services/webrtc/data-channel-handler.d.ts.map +1 -0
  19. package/dist/services/webrtc/data-channel-handler.js +260 -0
  20. package/dist/services/webrtc/data-channel-handler.js.map +1 -0
  21. package/dist/services/webrtc/index.d.ts +8 -0
  22. package/dist/services/webrtc/index.d.ts.map +1 -0
  23. package/dist/services/webrtc/index.js +18 -0
  24. package/dist/services/webrtc/index.js.map +1 -0
  25. package/dist/services/webrtc/media-stream-handler.d.ts +57 -0
  26. package/dist/services/webrtc/media-stream-handler.d.ts.map +1 -0
  27. package/dist/services/webrtc/media-stream-handler.js +383 -0
  28. package/dist/services/webrtc/media-stream-handler.js.map +1 -0
  29. package/dist/services/webrtc/peer-connection-manager.d.ts +40 -0
  30. package/dist/services/webrtc/peer-connection-manager.d.ts.map +1 -0
  31. package/dist/services/webrtc/peer-connection-manager.js +336 -0
  32. package/dist/services/webrtc/peer-connection-manager.js.map +1 -0
  33. package/dist/services/webrtc/types.d.ts +134 -0
  34. package/dist/services/webrtc/types.d.ts.map +1 -0
  35. package/dist/services/webrtc/types.js +12 -0
  36. package/dist/services/webrtc/types.js.map +1 -0
  37. package/dist/services/webrtc/webrtc-globals.d.ts +4 -0
  38. package/dist/services/webrtc/webrtc-globals.d.ts.map +1 -0
  39. package/dist/services/webrtc/webrtc-globals.js +72 -0
  40. package/dist/services/webrtc/webrtc-globals.js.map +1 -0
  41. package/dist/services/webrtc/webrtc-manager.d.ts +35 -0
  42. package/dist/services/webrtc/webrtc-manager.d.ts.map +1 -0
  43. package/dist/services/webrtc/webrtc-manager.js +274 -0
  44. package/dist/services/webrtc/webrtc-manager.js.map +1 -0
  45. package/dist/test/communication-comprehensive-test.d.ts +8 -0
  46. package/dist/test/communication-comprehensive-test.d.ts.map +1 -0
  47. package/dist/test/communication-comprehensive-test.js +356 -0
  48. package/dist/test/communication-comprehensive-test.js.map +1 -0
  49. package/dist/test/webrtc-channel-names-test.d.ts +2 -0
  50. package/dist/test/webrtc-channel-names-test.d.ts.map +1 -0
  51. package/dist/test/webrtc-channel-names-test.js +177 -0
  52. package/dist/test/webrtc-channel-names-test.js.map +1 -0
  53. package/dist/test/webrtc-comprehensive-test.d.ts +2 -0
  54. package/dist/test/webrtc-comprehensive-test.d.ts.map +1 -0
  55. package/dist/test/webrtc-comprehensive-test.js +328 -0
  56. package/dist/test/webrtc-comprehensive-test.js.map +1 -0
  57. package/dist/test/webrtc-reconnect-test.d.ts +4 -0
  58. package/dist/test/webrtc-reconnect-test.d.ts.map +1 -0
  59. package/dist/test/webrtc-reconnect-test.js +244 -0
  60. package/dist/test/webrtc-reconnect-test.js.map +1 -0
  61. package/dist/test/webrtc-test-harness.d.ts +4 -0
  62. package/dist/test/webrtc-test-harness.d.ts.map +1 -0
  63. package/dist/test/webrtc-test-harness.js +169 -0
  64. package/dist/test/webrtc-test-harness.js.map +1 -0
  65. package/dist/twin-messaging.d.ts +20 -0
  66. package/dist/twin-messaging.d.ts.map +1 -0
  67. package/dist/twin-messaging.js +94 -0
  68. package/dist/twin-messaging.js.map +1 -0
  69. package/dist/twin-registry.d.ts +9 -0
  70. package/dist/twin-registry.d.ts.map +1 -0
  71. package/dist/twin-registry.js +26 -0
  72. package/dist/twin-registry.js.map +1 -0
  73. package/dist/types/index.d.ts +4 -0
  74. package/dist/types/index.d.ts.map +1 -0
  75. package/dist/types/index.js +20 -0
  76. package/dist/types/index.js.map +1 -0
  77. package/dist/types/twin.types.d.ts +62 -14
  78. package/dist/types/twin.types.d.ts.map +1 -1
  79. package/dist/types/twin.types.js +8 -1
  80. package/dist/types/twin.types.js.map +1 -1
  81. package/docs/webrtc-howto.md +398 -0
  82. package/docs/webrtc-test.md +330 -0
  83. package/package.json +3 -3
  84. package/scripts/webrtc-test.sh +401 -0
  85. package/src/index.ts +378 -568
  86. package/src/peripheral-twin.ts +337 -0
  87. package/src/services/phyhub-connection.service.ts +24 -0
  88. package/src/services/phyhub-direct-connection.service.ts +159 -0
  89. package/src/services/webrtc/data-channel-handler.ts +362 -0
  90. package/src/services/webrtc/index.ts +36 -0
  91. package/src/services/webrtc/media-stream-handler.ts +536 -0
  92. package/src/services/webrtc/peer-connection-manager.ts +467 -0
  93. package/src/services/webrtc/types.ts +273 -0
  94. package/src/services/webrtc/webrtc-globals.ts +108 -0
  95. package/src/services/webrtc/webrtc-manager.ts +490 -0
  96. package/src/test/communication-comprehensive-test.ts +533 -0
  97. package/src/test/webrtc-channel-names-test.ts +266 -0
  98. package/src/test/webrtc-comprehensive-test.ts +494 -0
  99. package/src/test/webrtc-reconnect-test.ts +345 -0
  100. package/src/test/webrtc-test-harness.ts +254 -0
  101. package/src/twin-messaging.ts +184 -0
  102. package/src/twin-registry.ts +39 -0
  103. package/src/types/index.ts +3 -0
  104. package/src/types/twin.types.ts +80 -14
  105. package/dist/services/webrtc/datachannel.d.ts +0 -10
  106. package/dist/services/webrtc/datachannel.d.ts.map +0 -1
  107. package/dist/services/webrtc/datachannel.js +0 -290
  108. package/dist/services/webrtc/datachannel.js.map +0 -1
  109. package/dist/services/webrtc/mediastream.d.ts +0 -10
  110. package/dist/services/webrtc/mediastream.d.ts.map +0 -1
  111. package/dist/services/webrtc/mediastream.js +0 -396
  112. package/dist/services/webrtc/mediastream.js.map +0 -1
  113. package/dist/services/webrtc/peer-connection-ice.d.ts +0 -32
  114. package/dist/services/webrtc/peer-connection-ice.d.ts.map +0 -1
  115. package/dist/services/webrtc/peer-connection-ice.js +0 -483
  116. package/dist/services/webrtc/peer-connection-ice.js.map +0 -1
  117. package/src/services/webrtc/datachannel.ts +0 -421
  118. package/src/services/webrtc/mediastream.ts +0 -602
  119. package/src/services/webrtc/peer-connection-ice.ts +0 -689
@@ -0,0 +1,490 @@
1
+ /**
2
+ * WebRTCManager
3
+ *
4
+ * Main entry point for WebRTC functionality. Provides:
5
+ * - Simple API for DataChannel and MediaStream connections
6
+ * - Event system with opt-in verbose mode
7
+ * - Connection management and lifecycle handling
8
+ * - Automatic reconnection (transparent to developers)
9
+ */
10
+
11
+ import { DataChannelHandler } from './data-channel-handler';
12
+ import { MediaStreamHandler } from './media-stream-handler';
13
+ import { ensureWebRTCGlobals, isWebRTCAvailable } from './webrtc-globals';
14
+ import {
15
+ TwinMessagingInterface,
16
+ WebRTCManagerOptions,
17
+ WebRTCEvent,
18
+ WebRTCEventData,
19
+ WebRTCEventCallback,
20
+ PhygridDataChannel,
21
+ PhygridMediaStream,
22
+ MediaStreamOptions,
23
+ DEFAULT_WEBRTC_OPTIONS,
24
+ } from './types';
25
+
26
+ type DataChannelCallback = (channel: PhygridDataChannel, sourceTwinId: string) => void;
27
+ type MediaStreamCallback = (stream: PhygridMediaStream, sourceTwinId: string) => void;
28
+
29
+ export class WebRTCManager {
30
+ private twinMessaging: TwinMessagingInterface;
31
+ private options: Required<WebRTCManagerOptions>;
32
+
33
+ // Active connections by "{targetTwinId}:{channelName}"
34
+ private dataChannelHandlers: Map<string, DataChannelHandler> = new Map();
35
+ private mediaStreamHandlers: Map<string, MediaStreamHandler> = new Map();
36
+
37
+ // Listeners for incoming connections
38
+ private dataChannelListeners: Set<DataChannelCallback> = new Set();
39
+ private mediaStreamListeners: Set<MediaStreamCallback> = new Set();
40
+
41
+ // Event listeners
42
+ private eventListeners: Map<WebRTCEvent, Set<WebRTCEventCallback<any>>> = new Map();
43
+
44
+ private isInitialized = false;
45
+ private isClosed = false;
46
+
47
+ constructor(twinMessaging: TwinMessagingInterface, options: WebRTCManagerOptions = {}) {
48
+ this.twinMessaging = twinMessaging;
49
+ this.options = {
50
+ verbose: options.verbose ?? DEFAULT_WEBRTC_OPTIONS.verbose,
51
+ useStun: options.useStun ?? DEFAULT_WEBRTC_OPTIONS.useStun,
52
+ stunServers: options.stunServers ?? DEFAULT_WEBRTC_OPTIONS.stunServers,
53
+ connectionTimeout: options.connectionTimeout ?? DEFAULT_WEBRTC_OPTIONS.connectionTimeout,
54
+ initialRetryDelay: options.initialRetryDelay ?? DEFAULT_WEBRTC_OPTIONS.initialRetryDelay,
55
+ maxRetryDelay: options.maxRetryDelay ?? DEFAULT_WEBRTC_OPTIONS.maxRetryDelay,
56
+ };
57
+ }
58
+
59
+ // ===========================================================================
60
+ // DataChannel API
61
+ // ===========================================================================
62
+
63
+ /**
64
+ * Create a DataChannel connection to a target twin.
65
+ * Returns a PhygridDataChannel for sending/receiving messages.
66
+ * @param targetTwinId - The twin ID to connect to
67
+ * @param channelName - Optional channel name for multiple channels to same peer (default: 'default')
68
+ */
69
+ async createDataChannel(targetTwinId: string, channelName: string = 'default'): Promise<PhygridDataChannel> {
70
+ await this.ensureInitialized();
71
+
72
+ if (this.isClosed) {
73
+ throw new Error('WebRTCManager has been closed');
74
+ }
75
+
76
+ // Key includes channel name to support multiple channels per peer
77
+ const handlerKey = `${targetTwinId}:${channelName}`;
78
+
79
+ // Check for existing connection
80
+ const existing = this.dataChannelHandlers.get(handlerKey);
81
+ if (existing) {
82
+ // Return a new channel to the same target
83
+ return existing.connect();
84
+ }
85
+
86
+ const handler = new DataChannelHandler(
87
+ targetTwinId,
88
+ true, // isInitiator
89
+ this.twinMessaging,
90
+ {
91
+ useStun: this.options.useStun,
92
+ stunServers: this.options.stunServers,
93
+ connectionTimeout: this.options.connectionTimeout,
94
+ initialRetryDelay: this.options.initialRetryDelay,
95
+ maxRetryDelay: this.options.maxRetryDelay,
96
+ },
97
+ channelName
98
+ );
99
+
100
+ this.setupDataChannelHandlerCallbacks(handler, targetTwinId);
101
+ this.dataChannelHandlers.set(handlerKey, handler);
102
+
103
+ try {
104
+ return await handler.connect();
105
+ } catch (error) {
106
+ this.dataChannelHandlers.delete(handlerKey);
107
+ throw error;
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Listen for incoming DataChannel connections.
113
+ * The callback is called when a remote peer initiates a connection.
114
+ */
115
+ onDataChannel(callback: DataChannelCallback): void {
116
+ this.dataChannelListeners.add(callback);
117
+ }
118
+
119
+ /**
120
+ * Stop listening for incoming DataChannel connections.
121
+ */
122
+ offDataChannel(callback: DataChannelCallback): void {
123
+ this.dataChannelListeners.delete(callback);
124
+ }
125
+
126
+ /**
127
+ * Accept an incoming DataChannel connection from a specific twin.
128
+ * Call this when you know a twin will initiate a connection.
129
+ * @param sourceTwinId - The twin ID that will initiate the connection
130
+ * @param channelName - Optional channel name for multiple channels (default: 'default')
131
+ */
132
+ async acceptDataChannel(sourceTwinId: string, channelName: string = 'default'): Promise<PhygridDataChannel> {
133
+ await this.ensureInitialized();
134
+
135
+ if (this.isClosed) {
136
+ throw new Error('WebRTCManager has been closed');
137
+ }
138
+
139
+ // Key includes channel name to support multiple channels per peer
140
+ const handlerKey = `${sourceTwinId}:${channelName}`;
141
+
142
+ const handler = new DataChannelHandler(
143
+ sourceTwinId,
144
+ false, // not initiator (responder)
145
+ this.twinMessaging,
146
+ {
147
+ useStun: this.options.useStun,
148
+ stunServers: this.options.stunServers,
149
+ connectionTimeout: this.options.connectionTimeout,
150
+ initialRetryDelay: this.options.initialRetryDelay,
151
+ maxRetryDelay: this.options.maxRetryDelay,
152
+ },
153
+ channelName
154
+ );
155
+
156
+ this.setupDataChannelHandlerCallbacks(handler, sourceTwinId);
157
+ this.dataChannelHandlers.set(handlerKey, handler);
158
+
159
+ try {
160
+ const channel = await handler.connect();
161
+
162
+ // Notify listeners
163
+ this.dataChannelListeners.forEach((listener) => {
164
+ try {
165
+ listener(channel, sourceTwinId);
166
+ } catch (err) {
167
+ console.error('[WebRTCManager] Error in DataChannel listener:', err);
168
+ }
169
+ });
170
+
171
+ return channel;
172
+ } catch (error) {
173
+ this.dataChannelHandlers.delete(handlerKey);
174
+ throw error;
175
+ }
176
+ }
177
+
178
+ // ===========================================================================
179
+ // MediaStream API
180
+ // ===========================================================================
181
+
182
+ /**
183
+ * Create a MediaStream connection to a target twin.
184
+ * Returns a PhygridMediaStream for sending/receiving media.
185
+ * @param targetTwinId - The twin ID to connect to
186
+ * @param options - Optional MediaStream options
187
+ * @param channelName - Optional channel name for multiple streams to same peer (default: 'default')
188
+ */
189
+ async createMediaStream(
190
+ targetTwinId: string,
191
+ options?: MediaStreamOptions,
192
+ channelName: string = 'default'
193
+ ): Promise<PhygridMediaStream> {
194
+ await this.ensureInitialized();
195
+
196
+ if (this.isClosed) {
197
+ throw new Error('WebRTCManager has been closed');
198
+ }
199
+
200
+ // Key includes channel name to support multiple streams per peer
201
+ const handlerKey = `${targetTwinId}:${channelName}`;
202
+
203
+ // Check for existing connection
204
+ const existing = this.mediaStreamHandlers.get(handlerKey);
205
+ if (existing) {
206
+ return existing.connect();
207
+ }
208
+
209
+ const handler = new MediaStreamHandler(
210
+ targetTwinId,
211
+ true, // isInitiator
212
+ this.twinMessaging,
213
+ {
214
+ useStun: this.options.useStun,
215
+ stunServers: this.options.stunServers,
216
+ connectionTimeout: this.options.connectionTimeout,
217
+ initialRetryDelay: this.options.initialRetryDelay,
218
+ maxRetryDelay: this.options.maxRetryDelay,
219
+ mediaOptions: options,
220
+ },
221
+ channelName
222
+ );
223
+
224
+ this.setupMediaStreamHandlerCallbacks(handler, targetTwinId);
225
+ this.mediaStreamHandlers.set(handlerKey, handler);
226
+
227
+ try {
228
+ return await handler.connect();
229
+ } catch (error) {
230
+ this.mediaStreamHandlers.delete(handlerKey);
231
+ throw error;
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Listen for incoming MediaStream connections.
237
+ * The callback is called when a remote peer initiates a connection.
238
+ */
239
+ onMediaStream(callback: MediaStreamCallback): void {
240
+ this.mediaStreamListeners.add(callback);
241
+ }
242
+
243
+ /**
244
+ * Stop listening for incoming MediaStream connections.
245
+ */
246
+ offMediaStream(callback: MediaStreamCallback): void {
247
+ this.mediaStreamListeners.delete(callback);
248
+ }
249
+
250
+ /**
251
+ * Accept an incoming MediaStream connection from a specific twin.
252
+ * Call this when you know a twin will initiate a connection.
253
+ * @param sourceTwinId - The twin ID that will initiate the connection
254
+ * @param options - Optional MediaStream options
255
+ * @param channelName - Optional channel name for multiple streams (default: 'default')
256
+ */
257
+ async acceptMediaStream(
258
+ sourceTwinId: string,
259
+ options?: MediaStreamOptions,
260
+ channelName: string = 'default'
261
+ ): Promise<PhygridMediaStream> {
262
+ await this.ensureInitialized();
263
+
264
+ if (this.isClosed) {
265
+ throw new Error('WebRTCManager has been closed');
266
+ }
267
+
268
+ // Key includes channel name to support multiple streams per peer
269
+ const handlerKey = `${sourceTwinId}:${channelName}`;
270
+
271
+ const handler = new MediaStreamHandler(
272
+ sourceTwinId,
273
+ false, // not initiator (responder)
274
+ this.twinMessaging,
275
+ {
276
+ useStun: this.options.useStun,
277
+ stunServers: this.options.stunServers,
278
+ connectionTimeout: this.options.connectionTimeout,
279
+ initialRetryDelay: this.options.initialRetryDelay,
280
+ maxRetryDelay: this.options.maxRetryDelay,
281
+ mediaOptions: options,
282
+ },
283
+ channelName
284
+ );
285
+
286
+ this.setupMediaStreamHandlerCallbacks(handler, sourceTwinId);
287
+ this.mediaStreamHandlers.set(handlerKey, handler);
288
+
289
+ try {
290
+ const stream = await handler.connect();
291
+
292
+ // Notify listeners
293
+ this.mediaStreamListeners.forEach((listener) => {
294
+ try {
295
+ listener(stream, sourceTwinId);
296
+ } catch (err) {
297
+ console.error('[WebRTCManager] Error in MediaStream listener:', err);
298
+ }
299
+ });
300
+
301
+ return stream;
302
+ } catch (error) {
303
+ this.mediaStreamHandlers.delete(handlerKey);
304
+ throw error;
305
+ }
306
+ }
307
+
308
+ // ===========================================================================
309
+ // Event API
310
+ // ===========================================================================
311
+
312
+ /**
313
+ * Subscribe to WebRTC events.
314
+ *
315
+ * Standard events (always available):
316
+ * - 'connected': Connection established
317
+ * - 'disconnected': Connection lost
318
+ * - 'error': An error occurred
319
+ *
320
+ * Verbose events (when options.verbose is true):
321
+ * - 'reconnecting': Attempting to reconnect
322
+ * - 'reconnected': Successfully reconnected
323
+ * - 'ice-state-change': ICE connection state changed
324
+ * - 'signaling-state-change': Signaling state changed
325
+ * - 'ice-candidate': ICE candidate received
326
+ */
327
+ on<E extends WebRTCEvent>(event: E, callback: WebRTCEventCallback<E>): void {
328
+ if (!this.eventListeners.has(event)) {
329
+ this.eventListeners.set(event, new Set());
330
+ }
331
+ this.eventListeners.get(event)!.add(callback);
332
+ }
333
+
334
+ /**
335
+ * Unsubscribe from WebRTC events.
336
+ */
337
+ off<E extends WebRTCEvent>(event: E, callback: WebRTCEventCallback<E>): void {
338
+ const listeners = this.eventListeners.get(event);
339
+ if (listeners) {
340
+ listeners.delete(callback);
341
+ }
342
+ }
343
+
344
+ // ===========================================================================
345
+ // Lifecycle
346
+ // ===========================================================================
347
+
348
+ /**
349
+ * Check if WebRTC is available in the current environment.
350
+ */
351
+ static isAvailable(): boolean {
352
+ return isWebRTCAvailable();
353
+ }
354
+
355
+ /**
356
+ * Get active DataChannel connection count.
357
+ */
358
+ getActiveDataChannelCount(): number {
359
+ return this.dataChannelHandlers.size;
360
+ }
361
+
362
+ /**
363
+ * Get active MediaStream connection count.
364
+ */
365
+ getActiveMediaStreamCount(): number {
366
+ return this.mediaStreamHandlers.size;
367
+ }
368
+
369
+ /**
370
+ * Close all connections and clean up resources.
371
+ */
372
+ close(): void {
373
+ if (this.isClosed) return;
374
+
375
+ this.isClosed = true;
376
+
377
+ // Close all DataChannel handlers
378
+ this.dataChannelHandlers.forEach((handler) => {
379
+ try {
380
+ handler.close();
381
+ } catch (err) {
382
+ console.error('[WebRTCManager] Error closing DataChannel handler:', err);
383
+ }
384
+ });
385
+ this.dataChannelHandlers.clear();
386
+
387
+ // Close all MediaStream handlers
388
+ this.mediaStreamHandlers.forEach((handler) => {
389
+ try {
390
+ handler.close();
391
+ } catch (err) {
392
+ console.error('[WebRTCManager] Error closing MediaStream handler:', err);
393
+ }
394
+ });
395
+ this.mediaStreamHandlers.clear();
396
+
397
+ // Clear listeners
398
+ this.dataChannelListeners.clear();
399
+ this.mediaStreamListeners.clear();
400
+ this.eventListeners.clear();
401
+ }
402
+
403
+ // ===========================================================================
404
+ // Private Methods
405
+ // ===========================================================================
406
+
407
+ private async ensureInitialized(): Promise<void> {
408
+ if (this.isInitialized) return;
409
+
410
+ await ensureWebRTCGlobals();
411
+ this.isInitialized = true;
412
+ }
413
+
414
+ private setupDataChannelHandlerCallbacks(
415
+ handler: DataChannelHandler,
416
+ targetTwinId: string
417
+ ): void {
418
+ handler.setCallbacks({
419
+ onConnected: () => {
420
+ this.emit('connected', { targetTwinId, connectionType: 'datachannel' });
421
+ },
422
+ onDisconnected: () => {
423
+ this.emit('disconnected', { targetTwinId, connectionType: 'datachannel' });
424
+ },
425
+ onError: (error) => {
426
+ this.emit('error', { targetTwinId, error });
427
+ },
428
+ onReconnecting: (attempt) => {
429
+ if (this.options.verbose) {
430
+ this.emit('reconnecting', { targetTwinId, attempt });
431
+ }
432
+ },
433
+ onReconnected: (attempt) => {
434
+ if (this.options.verbose) {
435
+ this.emit('reconnected', { targetTwinId, attempt });
436
+ }
437
+ },
438
+ });
439
+ }
440
+
441
+ private setupMediaStreamHandlerCallbacks(
442
+ handler: MediaStreamHandler,
443
+ targetTwinId: string
444
+ ): void {
445
+ handler.setCallbacks({
446
+ onConnected: () => {
447
+ this.emit('connected', { targetTwinId, connectionType: 'mediastream' });
448
+ },
449
+ onDisconnected: () => {
450
+ this.emit('disconnected', { targetTwinId, connectionType: 'mediastream' });
451
+ },
452
+ onError: (error) => {
453
+ this.emit('error', { targetTwinId, error });
454
+ },
455
+ onReconnecting: (attempt) => {
456
+ if (this.options.verbose) {
457
+ this.emit('reconnecting', { targetTwinId, attempt });
458
+ }
459
+ },
460
+ onReconnected: (attempt) => {
461
+ if (this.options.verbose) {
462
+ this.emit('reconnected', { targetTwinId, attempt });
463
+ }
464
+ },
465
+ });
466
+ }
467
+
468
+ private emit<E extends WebRTCEvent>(event: E, data: WebRTCEventData[E]): void {
469
+ const listeners = this.eventListeners.get(event);
470
+ if (!listeners) return;
471
+
472
+ listeners.forEach((callback) => {
473
+ try {
474
+ callback(data);
475
+ } catch (err) {
476
+ console.error(`[WebRTCManager] Error in ${event} event listener:`, err);
477
+ }
478
+ });
479
+ }
480
+ }
481
+
482
+ // Re-export types for convenience
483
+ export {
484
+ TwinMessagingInterface,
485
+ WebRTCManagerOptions,
486
+ WebRTCEvent,
487
+ PhygridDataChannel,
488
+ PhygridMediaStream,
489
+ MediaStreamOptions,
490
+ } from './types';