@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
@@ -1,421 +0,0 @@
1
- // Initialize WebRTC globals if we're in Node environment
2
- (async function initializeWebRTCGlobals() {
3
- // Only run in Node environment
4
- if (typeof window !== 'undefined') return;
5
-
6
- try {
7
- // Dynamically import wrtc
8
- const wrtc = require('@roamhq/wrtc');
9
-
10
- // Set all WebRTC globals
11
- if (typeof global.MediaStream === 'undefined' && wrtc.MediaStream) {
12
- global.MediaStream = wrtc.MediaStream;
13
- console.log('Global MediaStream initialized');
14
- }
15
-
16
- if (typeof global.RTCDataChannel === 'undefined' && wrtc.RTCDataChannel) {
17
- global.RTCDataChannel = wrtc.RTCDataChannel;
18
- console.log('Global RTCDataChannel initialized');
19
- }
20
-
21
- if (typeof global.RTCPeerConnection === 'undefined' && wrtc.RTCPeerConnection) {
22
- global.RTCPeerConnection = wrtc.RTCPeerConnection;
23
- console.log('Global RTCPeerConnection initialized');
24
- }
25
-
26
- if (typeof global.RTCSessionDescription === 'undefined' && wrtc.RTCSessionDescription) {
27
- global.RTCSessionDescription = wrtc.RTCSessionDescription;
28
- console.log('Global RTCSessionDescription initialized');
29
- }
30
-
31
- if (typeof global.RTCIceCandidate === 'undefined' && wrtc.RTCIceCandidate) {
32
- global.RTCIceCandidate = wrtc.RTCIceCandidate;
33
- console.log('Global RTCIceCandidate initialized');
34
- }
35
-
36
- console.log('WebRTC globals successfully initialized');
37
- } catch (error) {
38
- console.error('Failed to initialize WebRTC globals:', error);
39
- }
40
- })().catch(err => console.error('Error in WebRTC globals initialization:', err));
41
-
42
- import { PhygridDataChannel, WebRTCConnectionOptions } from '../../types/webrtc.types';
43
- import {
44
- PeerConnectionState,
45
- PeerConnectionCallbacks,
46
- attemptConnection as attemptConnectionICE,
47
- triggerReconnect as triggerReconnectICE,
48
- } from './peer-connection-ice';
49
-
50
- // Initialize WebRTC globals if we're in Node environment
51
- (async function initializeWebRTCGlobals() {
52
- // Only run in Node environment
53
- if (typeof window !== 'undefined') return;
54
-
55
- try {
56
- // Dynamically import wrtc
57
- const wrtc = require('@roamhq/wrtc');
58
-
59
- // Set all WebRTC globals
60
- if (typeof global.MediaStream === 'undefined' && wrtc.MediaStream) {
61
- global.MediaStream = wrtc.MediaStream;
62
- console.log('Global MediaStream initialized');
63
- }
64
-
65
- if (typeof global.RTCPeerConnection === 'undefined' && wrtc.RTCPeerConnection) {
66
- global.RTCPeerConnection = wrtc.RTCPeerConnection;
67
- console.log('Global RTCPeerConnection initialized');
68
- }
69
-
70
- if (typeof global.RTCSessionDescription === 'undefined' && wrtc.RTCSessionDescription) {
71
- global.RTCSessionDescription = wrtc.RTCSessionDescription;
72
- console.log('Global RTCSessionDescription initialized');
73
- }
74
-
75
- if (typeof global.RTCIceCandidate === 'undefined' && wrtc.RTCIceCandidate) {
76
- global.RTCIceCandidate = wrtc.RTCIceCandidate;
77
- console.log('Global RTCIceCandidate initialized');
78
- }
79
-
80
- console.log('WebRTC globals successfully initialized');
81
- } catch (error) {
82
- console.error('Failed to initialize WebRTC globals:', error);
83
- }
84
- })().catch(err => console.error('Error in WebRTC globals initialization:', err));
85
-
86
- export interface WebRTCDataChannelResult {
87
- dataChannel: PhygridDataChannel;
88
- close: () => void;
89
- _rawPeerConnection?: RTCPeerConnection;
90
- _rawDataChannel?: RTCDataChannel;
91
- }
92
-
93
- export type WebRTCDataChannelConnectionResult = WebRTCDataChannelResult;
94
-
95
- const CONNECTION_TIMEOUT = 15000; // 15 seconds
96
- const RECONNECT_DELAY = 1000; // 1 second initial reconnect delay
97
- const INITIAL_RETRY_DELAY = 1000; // 1 second
98
- const MAX_RETRY_DELAY = 30000; // 30 seconds maximum
99
-
100
- export async function createWebRTCDataChannelConnection(
101
- options: WebRTCConnectionOptions
102
- ): Promise<WebRTCDataChannelConnectionResult> {
103
- let pc: RTCPeerConnection | null = null;
104
- let isShuttingDown = false;
105
- let isReconnecting = false;
106
- let currentConnectionId = 0;
107
- let pendingCandidates: RTCIceCandidateInit[] = [];
108
- let activeDataChannel: RTCDataChannel | null = null;
109
-
110
- const {
111
- targetTwinId,
112
- sendTwinMessage,
113
- subscribeTwin,
114
- onTwinMessage,
115
- offTwinMessage,
116
- useStun = true,
117
- isInitiator = true,
118
- channelPrefix = 'channel',
119
- mediaOptions = { isMediaConnection: false },
120
- } = options;
121
-
122
- // Create the persistent data channel once - this is the key to maintaining state across reconnections
123
- const { persistentChannel, setupDataChannel, setCleanupFn } = createPersistentDataChannel();
124
-
125
- // Set up cleanup function
126
- setCleanupFn(() => {
127
- if (mediaOptions.isMediaConnection && mediaOptions.localStream) {
128
- mediaOptions.localStream.getTracks().forEach(track => track.stop());
129
- }
130
- });
131
-
132
- const callbacks: PeerConnectionCallbacks = {
133
- async onCreateConnection(
134
- pcInstance: RTCPeerConnection,
135
- connectionId: number,
136
- localInitiator: boolean
137
- ) {
138
- console.log(
139
- `Creating data channel for connection ID: ${connectionId} ${JSON.stringify(localInitiator)}`
140
- );
141
- const channelId = `${channelPrefix}-${targetTwinId}`;
142
-
143
- return new Promise<RTCDataChannel | null>(resolve => {
144
- if (isInitiator) {
145
- const dataChannel = pcInstance.createDataChannel(channelId);
146
- dataChannel.binaryType = 'arraybuffer';
147
- setupDataChannel(dataChannel, isReconnecting, isShuttingDown, triggerReconnect);
148
-
149
- // This is important - we need to resolve with the data channel
150
- // even before it's open, so the ICE process can continue
151
- resolve(dataChannel);
152
- } else {
153
- pcInstance.ondatachannel = event => {
154
- console.log('Data channel received in hub-client');
155
- const dataChannel = event.channel;
156
- dataChannel.binaryType = 'arraybuffer';
157
- setupDataChannel(dataChannel, isReconnecting, isShuttingDown, triggerReconnect);
158
- };
159
- resolve(null);
160
- }
161
- });
162
- },
163
-
164
- onCloseCleanup() {
165
- if (mediaOptions.isMediaConnection && mediaOptions.localStream) {
166
- mediaOptions.localStream.getTracks().forEach(track => track.stop());
167
- }
168
- },
169
-
170
- async onSubscribe() {
171
- await subscribeTwin(targetTwinId);
172
- },
173
-
174
- async sendSignalingMessage(type, data) {
175
- await sendTwinMessage(targetTwinId, {
176
- type: `${channelPrefix}-${targetTwinId}:${type}`,
177
- data,
178
- });
179
- },
180
-
181
- setupMessageHandling(messageHandler) {
182
- onTwinMessage(targetTwinId, messageHandler);
183
- },
184
-
185
- removeMessageHandling(messageHandler) {
186
- console.log(`Removing message listener for twin ${targetTwinId}`);
187
- offTwinMessage(targetTwinId, messageHandler);
188
- },
189
-
190
- reportConnectionState(info: string) {
191
- console.log(info);
192
- },
193
-
194
- reportError(info: string, error?: any) {
195
- console.error(info, error || '');
196
- },
197
-
198
- async retryConnection(retries: number, toggledUseStun?: boolean, nextDelay?: number) {
199
- // Important: we need to reset isReconnecting before starting a new connection attempt
200
- isReconnecting = false;
201
-
202
- return attemptConnectionICE(
203
- state,
204
- { ...options, useStun: toggledUseStun ?? useStun },
205
- retries,
206
- toggledUseStun ?? useStun,
207
- nextDelay ?? INITIAL_RETRY_DELAY,
208
- INITIAL_RETRY_DELAY,
209
- CONNECTION_TIMEOUT,
210
- MAX_RETRY_DELAY,
211
- callbacks
212
- );
213
- },
214
-
215
- safeReject(error) {
216
- throw error;
217
- },
218
- };
219
-
220
- const state: PeerConnectionState = {
221
- pc,
222
- isShuttingDown,
223
- isReconnecting,
224
- currentConnectionId,
225
- pendingCandidates,
226
- activeDataChannel,
227
- useStun,
228
- channelPrefix,
229
- targetTwinId,
230
- isInitiator,
231
- connectionType: 'datachannel',
232
- };
233
-
234
- function triggerReconnect(attempt: number) {
235
- if (isReconnecting || isShuttingDown) {
236
- console.log(
237
- `triggerReconnect() called but already reconnecting or shutting down (attempt ${attempt})`
238
- );
239
- return;
240
- }
241
-
242
- isReconnecting = true;
243
-
244
- triggerReconnectICE(state, attempt, RECONNECT_DELAY, callbacks);
245
- }
246
-
247
- const result = await attemptConnectionICE(
248
- state,
249
- { ...options },
250
- 0,
251
- useStun,
252
- INITIAL_RETRY_DELAY,
253
- INITIAL_RETRY_DELAY,
254
- CONNECTION_TIMEOUT,
255
- MAX_RETRY_DELAY,
256
- callbacks
257
- );
258
-
259
- // Return the persistent data channel
260
- return {
261
- dataChannel: persistentChannel,
262
- _rawPeerConnection: result._rawPeerConnection,
263
- _rawDataChannel: result._rawDataChannel,
264
- close: () => {
265
- isShuttingDown = true;
266
- if (result.close) {
267
- result.close();
268
- }
269
- },
270
- };
271
- }
272
-
273
- const createPersistentDataChannel = (): {
274
- persistentChannel: PhygridDataChannel;
275
- setupDataChannel: (
276
- dc: RTCDataChannel,
277
- isReconnecting: boolean,
278
- isShuttingDown: boolean,
279
- triggerReconnect: (attempt: number) => void
280
- ) => void;
281
- setCleanupFn: (fn: () => void) => void;
282
- } => {
283
- const messageListeners: ((data: any) => void)[] = [];
284
- const closeListeners: (() => void)[] = [];
285
-
286
- let currentDataChannel: RTCDataChannel | null = null;
287
- let activeDataChannel: RTCDataChannel | null = null;
288
- let reconnectAttempts = 0;
289
- let cleanupFn: (() => void) | null = null;
290
-
291
- const setCleanupFn = (fn: () => void) => {
292
- cleanupFn = fn;
293
- };
294
-
295
- const persistentChannel: PhygridDataChannel = {
296
- send: (data: any) => {
297
- if (activeDataChannel && activeDataChannel.readyState === 'open') {
298
- try {
299
- // Convert data to string if it's an object
300
- if (typeof data === 'object' && data !== null) {
301
- activeDataChannel.send(JSON.stringify(data));
302
- } else {
303
- activeDataChannel.send(data);
304
- }
305
- } catch (error) {
306
- console.error('Error sending data:', error);
307
- }
308
- } else {
309
- console.warn(
310
- 'Channel not ready, dropping message. State:',
311
- activeDataChannel ? activeDataChannel.readyState : 'no channel'
312
- );
313
- }
314
- },
315
- onMessage: (callback: (data: any) => void) => {
316
- console.log('Adding message listener to persistent channel');
317
- messageListeners.push(callback);
318
- },
319
- offMessage: (callback: (data: any) => void) => {
320
- const index = messageListeners.indexOf(callback);
321
- if (index !== -1) {
322
- messageListeners.splice(index, 1);
323
- }
324
- },
325
- onClose: (callback: () => void) => {
326
- closeListeners.push(callback);
327
- },
328
- offClose: (callback: () => void) => {
329
- const index = closeListeners.indexOf(callback);
330
- if (index !== -1) {
331
- closeListeners.splice(index, 1);
332
- }
333
- },
334
- close: () => {
335
- if (currentDataChannel && currentDataChannel.readyState !== 'closed') {
336
- currentDataChannel.close();
337
- }
338
- if (cleanupFn) cleanupFn();
339
- },
340
- };
341
-
342
- const setupDataChannel = (
343
- dc: RTCDataChannel,
344
- isReconnecting: boolean,
345
- isShuttingDown: boolean,
346
- triggerReconnect: (attempt: number) => void
347
- ) => {
348
- console.log('Setting up data channel with listeners');
349
- currentDataChannel = dc;
350
- activeDataChannel = dc;
351
-
352
- dc.onopen = () => {
353
- console.log('Data channel opened in setupDataChannel, readyState:', dc.readyState);
354
- currentDataChannel = dc;
355
- activeDataChannel = dc;
356
- };
357
-
358
- dc.onmessage = event => {
359
- console.log('Received message in data channel:', typeof event.data);
360
- let parsedData = event.data;
361
-
362
- // Try to parse JSON if it's a string
363
- if (typeof event.data === 'string') {
364
- try {
365
- parsedData = JSON.parse(event.data);
366
- } catch (e) {
367
- // Not JSON, use as is
368
- parsedData = event.data;
369
- }
370
- }
371
-
372
- // Forward to all listeners
373
- if (messageListeners.length === 0) {
374
- console.warn('No message listeners registered for data channel');
375
- }
376
-
377
- messageListeners.forEach(listener => {
378
- try {
379
- listener(parsedData);
380
- } catch (error) {
381
- console.error('Error handling message:', error);
382
- }
383
- });
384
- };
385
-
386
- dc.onclose = () => {
387
- console.log('Data channel closed');
388
-
389
- // Notify all close listeners
390
- closeListeners.forEach(listener => {
391
- try {
392
- listener();
393
- } catch (error) {
394
- console.error('Error in close listener:', error);
395
- }
396
- });
397
-
398
- // Only trigger reconnect if not explicitly shutting down
399
- if (!isShuttingDown && !isReconnecting) {
400
- console.log('Data channel closed unexpectedly, triggering reconnect');
401
- triggerReconnect(reconnectAttempts++);
402
- }
403
- activeDataChannel = null;
404
- };
405
-
406
- dc.onerror = error => {
407
- console.error('Data channel error:', error);
408
- // Errors might also require reconnection
409
- if (!isShuttingDown && !isReconnecting) {
410
- console.log('Data channel error, triggering reconnect');
411
- triggerReconnect(reconnectAttempts++);
412
- }
413
- };
414
- };
415
-
416
- return {
417
- persistentChannel,
418
- setupDataChannel,
419
- setCleanupFn,
420
- };
421
- };