@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.
- package/dist/index.d.ts +22 -28
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +252 -378
- package/dist/index.js.map +1 -1
- package/dist/peripheral-twin.d.ts +34 -0
- package/dist/peripheral-twin.d.ts.map +1 -0
- package/dist/peripheral-twin.js +234 -0
- package/dist/peripheral-twin.js.map +1 -0
- package/dist/services/phyhub-connection.service.d.ts +1 -0
- package/dist/services/phyhub-connection.service.d.ts.map +1 -1
- package/dist/services/phyhub-connection.service.js +17 -0
- package/dist/services/phyhub-connection.service.js.map +1 -1
- package/dist/services/phyhub-direct-connection.service.d.ts +21 -0
- package/dist/services/phyhub-direct-connection.service.d.ts.map +1 -0
- package/dist/services/phyhub-direct-connection.service.js +101 -0
- package/dist/services/phyhub-direct-connection.service.js.map +1 -0
- package/dist/services/webrtc/data-channel-handler.d.ts +45 -0
- package/dist/services/webrtc/data-channel-handler.d.ts.map +1 -0
- package/dist/services/webrtc/data-channel-handler.js +260 -0
- package/dist/services/webrtc/data-channel-handler.js.map +1 -0
- package/dist/services/webrtc/index.d.ts +8 -0
- package/dist/services/webrtc/index.d.ts.map +1 -0
- package/dist/services/webrtc/index.js +18 -0
- package/dist/services/webrtc/index.js.map +1 -0
- package/dist/services/webrtc/media-stream-handler.d.ts +57 -0
- package/dist/services/webrtc/media-stream-handler.d.ts.map +1 -0
- package/dist/services/webrtc/media-stream-handler.js +383 -0
- package/dist/services/webrtc/media-stream-handler.js.map +1 -0
- package/dist/services/webrtc/peer-connection-manager.d.ts +40 -0
- package/dist/services/webrtc/peer-connection-manager.d.ts.map +1 -0
- package/dist/services/webrtc/peer-connection-manager.js +336 -0
- package/dist/services/webrtc/peer-connection-manager.js.map +1 -0
- package/dist/services/webrtc/types.d.ts +134 -0
- package/dist/services/webrtc/types.d.ts.map +1 -0
- package/dist/services/webrtc/types.js +12 -0
- package/dist/services/webrtc/types.js.map +1 -0
- package/dist/services/webrtc/webrtc-globals.d.ts +4 -0
- package/dist/services/webrtc/webrtc-globals.d.ts.map +1 -0
- package/dist/services/webrtc/webrtc-globals.js +72 -0
- package/dist/services/webrtc/webrtc-globals.js.map +1 -0
- package/dist/services/webrtc/webrtc-manager.d.ts +35 -0
- package/dist/services/webrtc/webrtc-manager.d.ts.map +1 -0
- package/dist/services/webrtc/webrtc-manager.js +274 -0
- package/dist/services/webrtc/webrtc-manager.js.map +1 -0
- package/dist/test/communication-comprehensive-test.d.ts +8 -0
- package/dist/test/communication-comprehensive-test.d.ts.map +1 -0
- package/dist/test/communication-comprehensive-test.js +356 -0
- package/dist/test/communication-comprehensive-test.js.map +1 -0
- package/dist/test/webrtc-channel-names-test.d.ts +2 -0
- package/dist/test/webrtc-channel-names-test.d.ts.map +1 -0
- package/dist/test/webrtc-channel-names-test.js +177 -0
- package/dist/test/webrtc-channel-names-test.js.map +1 -0
- package/dist/test/webrtc-comprehensive-test.d.ts +2 -0
- package/dist/test/webrtc-comprehensive-test.d.ts.map +1 -0
- package/dist/test/webrtc-comprehensive-test.js +328 -0
- package/dist/test/webrtc-comprehensive-test.js.map +1 -0
- package/dist/test/webrtc-reconnect-test.d.ts +4 -0
- package/dist/test/webrtc-reconnect-test.d.ts.map +1 -0
- package/dist/test/webrtc-reconnect-test.js +244 -0
- package/dist/test/webrtc-reconnect-test.js.map +1 -0
- package/dist/test/webrtc-test-harness.d.ts +4 -0
- package/dist/test/webrtc-test-harness.d.ts.map +1 -0
- package/dist/test/webrtc-test-harness.js +169 -0
- package/dist/test/webrtc-test-harness.js.map +1 -0
- package/dist/twin-messaging.d.ts +20 -0
- package/dist/twin-messaging.d.ts.map +1 -0
- package/dist/twin-messaging.js +94 -0
- package/dist/twin-messaging.js.map +1 -0
- package/dist/twin-registry.d.ts +9 -0
- package/dist/twin-registry.d.ts.map +1 -0
- package/dist/twin-registry.js +26 -0
- package/dist/twin-registry.js.map +1 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +20 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/twin.types.d.ts +62 -14
- package/dist/types/twin.types.d.ts.map +1 -1
- package/dist/types/twin.types.js +8 -1
- package/dist/types/twin.types.js.map +1 -1
- package/docs/webrtc-howto.md +398 -0
- package/docs/webrtc-test.md +330 -0
- package/package.json +3 -3
- package/scripts/webrtc-test.sh +401 -0
- package/src/index.ts +378 -568
- package/src/peripheral-twin.ts +337 -0
- package/src/services/phyhub-connection.service.ts +24 -0
- package/src/services/phyhub-direct-connection.service.ts +159 -0
- package/src/services/webrtc/data-channel-handler.ts +362 -0
- package/src/services/webrtc/index.ts +36 -0
- package/src/services/webrtc/media-stream-handler.ts +536 -0
- package/src/services/webrtc/peer-connection-manager.ts +467 -0
- package/src/services/webrtc/types.ts +273 -0
- package/src/services/webrtc/webrtc-globals.ts +108 -0
- package/src/services/webrtc/webrtc-manager.ts +490 -0
- package/src/test/communication-comprehensive-test.ts +533 -0
- package/src/test/webrtc-channel-names-test.ts +266 -0
- package/src/test/webrtc-comprehensive-test.ts +494 -0
- package/src/test/webrtc-reconnect-test.ts +345 -0
- package/src/test/webrtc-test-harness.ts +254 -0
- package/src/twin-messaging.ts +184 -0
- package/src/twin-registry.ts +39 -0
- package/src/types/index.ts +3 -0
- package/src/types/twin.types.ts +80 -14
- package/dist/services/webrtc/datachannel.d.ts +0 -10
- package/dist/services/webrtc/datachannel.d.ts.map +0 -1
- package/dist/services/webrtc/datachannel.js +0 -290
- package/dist/services/webrtc/datachannel.js.map +0 -1
- package/dist/services/webrtc/mediastream.d.ts +0 -10
- package/dist/services/webrtc/mediastream.d.ts.map +0 -1
- package/dist/services/webrtc/mediastream.js +0 -396
- package/dist/services/webrtc/mediastream.js.map +0 -1
- package/dist/services/webrtc/peer-connection-ice.d.ts +0 -32
- package/dist/services/webrtc/peer-connection-ice.d.ts.map +0 -1
- package/dist/services/webrtc/peer-connection-ice.js +0 -483
- package/dist/services/webrtc/peer-connection-ice.js.map +0 -1
- package/src/services/webrtc/datachannel.ts +0 -421
- package/src/services/webrtc/mediastream.ts +0 -602
- 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';
|