@reactoo/watchtogether-sdk-js 2.8.48 → 2.8.52

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.
@@ -1,6 +1,6 @@
1
1
  import emitter from './wt-emitter';
2
2
  import {decodeJanusDisplay, generateUUID} from "../models/utils";
3
- import Worker from './wt-iot-worker.worker.js';
3
+ import Worker from './wt-iot-worker5.worker.js';
4
4
 
5
5
  class Iot {
6
6
  constructor(enableDebugFlag) {
@@ -10,7 +10,6 @@ class Iot {
10
10
  this.credentialsExpirationCheckIntervalId = null;
11
11
  this.currentCredentialsExpirationStamp = null;
12
12
  this.lastConnectParams = null;
13
- this.subscribedTopics = new Set();
14
13
  this.abortController = null;
15
14
  this.worker = null;
16
15
 
@@ -33,131 +32,123 @@ class Iot {
33
32
 
34
33
  terminateWorker() {
35
34
  this.log('iot terminateWorker');
35
+ this.worker.onmessage = null;
36
36
  this.worker?.terminate();
37
+ this.worker = null;
37
38
  }
38
39
 
39
- connect(apiMqttUrl, apiMqttClientId, region, accessKeyId, secretAccessKey, sessionToken, expiration) {
40
- this.log('iot connect called, we disconnect first just to be sure');
41
- return this.disconnect()
42
- .catch(() => {
43
- // we dont care if disconnect fails
44
- return Promise.resolve();
45
- })
46
- .then(() => {
47
- this.log('iot connect');
48
-
49
- if(!this.worker) {
50
- this.log('Worker not initialized');
51
- return Promise.reject(new Error('Worker not initialized', {cause: -1}));
52
- }
40
+ async connect(apiMqttUrl, apiMqttClientId, region, accessKeyId, secretAccessKey, sessionToken, expiration) {
53
41
 
54
- this.abortController = new AbortController();
55
- this.startCredentialsExpirationCheck(expiration);
56
- this.lastConnectParams = { apiMqttUrl, apiMqttClientId, region, accessKeyId, secretAccessKey, sessionToken, expiration };
57
- return new Promise((resolve, reject) => {
58
- const stamp = new Date().getTime() +'_'+ generateUUID();
59
-
60
- const handleConnectResult = (event) => {
61
-
62
- if(event.stamp !== stamp) {
63
- this.log('connect event stamp mismatch, ignoring', event.stamp, stamp);
64
- return;
65
- }
66
-
67
- clearTimeout(timeoutId);
68
- this.off('worker:connect_result', handleConnectResult);
69
- if (event.success) {
70
- resolve();
71
- } else {
72
- reject(new Error(event.error));
73
- }
74
- };
75
-
76
- const timeoutId = setTimeout(() => {
77
- this.off('worker:connect_result', handleConnectResult);
78
- reject(new Error('Connection timeout'));
79
- }, 10000);
80
-
81
- this.on('worker:connect_result', handleConnectResult);
82
-
83
- this.worker?.postMessage({
84
- type: 'connect',
85
- params: this.lastConnectParams,
86
- stamp: stamp
87
- });
88
- });
89
- });
90
- }
42
+ await this.disconnect(false).catch(() => {}); // ignore disconnect errors
91
43
 
92
- disconnect() {
93
- this.log('iot disconnect');
94
- this.stopCredentialsExpirationCheck();
95
- this.abortController?.abort();
44
+ this.log('iot connect');
96
45
 
97
- if(!this.worker) {
98
- this.log('Worker not initialized');
99
- return Promise.reject(new Error('Worker not initialized', {cause: -1}));
100
- }
46
+ this.abortController = new AbortController();
47
+ this.lastConnectParams = { apiMqttUrl, apiMqttClientId, region, accessKeyId, secretAccessKey, sessionToken, expiration };
101
48
 
102
- return new Promise((resolve, reject) => {
49
+ this.initWorker();
50
+ this.startCredentialsExpirationCheck(expiration);
103
51
 
104
- const stamp = new Date().getTime()+'_'+ generateUUID();
52
+ return new Promise((resolve, reject) => {
53
+ const cleanup = () => {
54
+ clearTimeout(timeoutId);
55
+ this.abortController.signal.removeEventListener('abort', onFailure);
56
+ this.off('connectionSuccess', onSuccess);
57
+ this.off('connectionFailure', onFailure);
58
+ };
59
+ const onSuccess = (event) => {
60
+ cleanup();
61
+ resolve();
62
+ };
63
+ const onFailure = (event) => {
64
+ cleanup();
65
+ reject(event?.error);
66
+ };
105
67
 
106
- const handleDisconnectResult = (event) => {
107
- if(event.stamp !== stamp) {
108
- this.log('disconnect event stamp mismatch, ignoring', event.stamp, stamp);
109
- return;
110
- }
111
- clearTimeout(timeoutId);
112
- this.off('worker:disconnect_result', handleDisconnectResult);
113
- if (event.success) {
114
- resolve();
115
- } else {
116
- reject(new Error(event.error));
117
- }
118
- };
68
+ const timeoutId = setTimeout(() => {
69
+ onFailure({ error: 'Connection timeout' });
70
+ } , 10000);
71
+ this.abortController.signal.addEventListener('abort', onFailure);
119
72
 
120
- const timeoutId = setTimeout(() => {
121
- this.off('worker:disconnect_result', handleDisconnectResult);
122
- reject(new Error('Disconnect timeout'));
123
- }, 5000);
73
+ this.once('connectionSuccess', onSuccess);
74
+ this.once('connectionFailure', onFailure);
124
75
 
125
- this.on('worker:disconnect_result', handleDisconnectResult);
126
- this.worker?.postMessage({ type: 'disconnect', stamp: stamp });
127
- });
76
+ this.worker?.postMessage({
77
+ type: 'connect',
78
+ params: this.lastConnectParams,
79
+ });
80
+
81
+ });
128
82
  }
129
83
 
130
- isConnected() {
84
+ disconnect(waitForComplete = true) {
85
+ this.log('iot disconnect');
86
+
87
+ this.stopCredentialsExpirationCheck();
88
+ this.abortController?.abort();
89
+
90
+ if(!this.worker) {
91
+ this.log('Worker not initialized, nothing to disconnect');
92
+ return Promise.resolve();
93
+ }
94
+
95
+ return new Promise((resolve, reject) => {
96
+ const cleanup = () => {
97
+ clearTimeout(timeoutId);
98
+ this.off('stopped', onSuccess);
99
+ if(waitForComplete) {
100
+ this.terminateWorker();
101
+ }
102
+ };
103
+ const onSuccess = (event) => {
104
+ cleanup();
105
+ resolve();
106
+ };
107
+ const onFailure = (event) => {
108
+ cleanup();
109
+ reject(event?.error);
110
+ };
111
+
112
+ const timeoutId = setTimeout(() => {
113
+ onFailure({ error: 'Timed out' });
114
+ } , 5000);
115
+
116
+ this.once('stopped', onSuccess);
117
+
118
+ this.worker?.postMessage({ type: 'disconnect'});
119
+ if(!waitForComplete) {
120
+ cleanup();
121
+ resolve();
122
+ }
123
+ });
131
124
 
125
+ }
126
+
127
+ isConnected() {
132
128
  if(!this.worker) {
133
129
  this.log('Worker not initialized');
134
130
  return Promise.reject(new Error('Worker not initialized', {cause: -1}));
135
131
  }
136
-
137
132
  return new Promise((resolve) => {
133
+ const stamp = new Date().getTime()+'_'+ generateUUID();
138
134
  const handleIsConnectedResult = (event) => {
135
+ if(event.stamp !== stamp) return
139
136
  this.off('worker:is_connected_result', handleIsConnectedResult);
140
137
  resolve(event.connected);
141
138
  };
142
-
143
139
  this.on('worker:is_connected_result', handleIsConnectedResult);
144
- this.worker?.postMessage({ type: 'is_connected' });
140
+ this.worker?.postMessage({ type: 'isConnected' });
145
141
  });
146
142
  }
147
143
 
148
144
  clearTopics() {
149
- this.subscribedTopics.clear();
150
- this.worker?.postMessage({ type: 'clear_topics' });
145
+ this.worker?.postMessage({ type: 'clearTopics' });
151
146
  }
152
147
 
153
148
  subscribe(topic) {
154
149
  this.log('iot subscribe', topic);
155
150
  if (typeof topic === 'string' && topic.trim() !== '') {
156
151
 
157
- if(!this.subscribedTopics.has(topic)) {
158
- this.subscribedTopics.add(topic);
159
- }
160
-
161
152
  if(!this.worker) {
162
153
  this.log('Worker not initialized');
163
154
  return Promise.reject(new Error('Worker not initialized', {cause: -1}));
@@ -168,7 +159,7 @@ class Iot {
168
159
  const handleSubscribeResult = (event) => {
169
160
 
170
161
  if(event.stamp !== stamp) {
171
- this.log('subscribe event stamp mismatch, ignoring', event.stamp, stamp);
162
+ // ignoring, not our result
172
163
  return;
173
164
  }
174
165
 
@@ -192,7 +183,7 @@ class Iot {
192
183
  unsubscribe(topic) {
193
184
  this.log('iot unsubscribe', topic);
194
185
  if (typeof topic === 'string' && topic.trim() !== '') {
195
- this.subscribedTopics.delete(topic);
186
+
196
187
  if(!this.worker) {
197
188
  this.log('Worker not initialized');
198
189
  return Promise.reject(new Error('Worker not initialized', {cause: -1}));
@@ -205,7 +196,7 @@ class Iot {
205
196
  const handleUnsubscribeResult = (event) => {
206
197
 
207
198
  if(event.stamp !== stamp) {
208
- this.log('unsubscribe event stamp mismatch, ignoring', event.stamp, stamp);
199
+ // ignoring, not our result
209
200
  return;
210
201
  }
211
202
 
@@ -237,22 +228,19 @@ class Iot {
237
228
  }
238
229
 
239
230
  handleWorkerMessage(event) {
240
- const { type, data, connectionId } = event.data;
231
+ const { type, data } = event.data;
241
232
  switch (type) {
242
233
  case 'message':
243
234
  this.handleMessage(data.topic, new Uint8Array(data.payload));
244
235
  break;
245
- case 'connect':
246
- case 'disconnect':
247
236
  case 'error':
248
- case 'interrupt':
249
- case 'resume':
250
- case 'connection_success':
251
- case 'connection_failure':
252
- this.emit(type, data, connectionId);
237
+ case 'attemptingConnect':
238
+ case 'connectionSuccess':
239
+ case 'connectionFailure':
240
+ case 'disconnection':
241
+ case 'stopped':
242
+ this.emit(type, data);
253
243
  break;
254
- case 'connect_result':
255
- case 'disconnect_result':
256
244
  case 'is_connected_result':
257
245
  case 'subscribe_result':
258
246
  case 'unsubscribe_result':
@@ -372,11 +360,11 @@ class Iot {
372
360
  this.credentialsExpirationCheckIntervalId = null;
373
361
  }
374
362
 
375
- updateWebSocketCredentials(accessKeyId, secretAccessKey, sessionToken, expiration) {
363
+ updateWebSocketCredentials(accessKeyId, secretAccessKey, sessionToken, expiration, subscribedTopics) {
376
364
  this.log('iot updateWebSocketCredentials');
377
365
  this.lastConnectParams = {...this.lastConnectParams, accessKeyId, secretAccessKey, sessionToken, expiration };
378
- const currentTopics = new Set(this.subscribedTopics);
379
- return this.connect(
366
+ const currentTopics = new Set(subscribedTopics);
367
+ return this.connect(
380
368
  this.lastConnectParams.apiMqttUrl,
381
369
  this.lastConnectParams.apiMqttClientId,
382
370
  this.lastConnectParams.region,
@@ -386,22 +374,26 @@ class Iot {
386
374
  expiration
387
375
  ).then(() => {
388
376
  // Resubscribe to topics
389
- currentTopics.forEach(topic => this.subscribe(topic));
377
+ currentTopics.forEach(topic => this.subscribe(topic).catch(() => {}));
390
378
  return true;
391
379
  });
392
380
  }
393
381
 
394
- checkConnection() {
382
+ checkConnection(subscribedTopics) {
395
383
  return new Promise((resolve, reject) => {
396
- if (this.subscribedTopics.size === 0) {
384
+ if (subscribedTopics.size === 0) {
397
385
  reject(new Error('No subscribed topics available for connection check', {cause: -1}));
398
386
  return;
399
387
  }
400
- let suitableTopic = Array.from(this.subscribedTopics).find(topic => topic.indexOf('user') > -1);
388
+ let suitableTopic = Array.from(subscribedTopics).find(topic => topic.indexOf('user') > -1);
401
389
  if (!suitableTopic) {
402
390
  reject(new Error('No suitable topic found for connection check', {cause: -1}));
403
391
  return;
404
392
  }
393
+ if(!navigator.onLine) {
394
+ reject(new Error('No internet connection', {cause: -1}));
395
+ return;
396
+ }
405
397
  const testMessage = {
406
398
  type: 'keep_alive',
407
399
  timestamp: Date.now()
@@ -1195,7 +1195,7 @@ class RoomSession {
1195
1195
  this.wsOpen = false;
1196
1196
  this.emit('webSocketsConnectionClosed');
1197
1197
 
1198
- if (!this.isConnected || this.isConnecting || this.isDisconnecting) {
1198
+ if (!this.isSupposeToBeConnected || this.isRestarting) {
1199
1199
  return;
1200
1200
  }
1201
1201
 
@@ -1,203 +0,0 @@
1
- import { mqtt, iot } from 'aws-iot-device-sdk-v2';
2
- console.log('Worker: Starting up');
3
-
4
- let connection = null;
5
- let currentConnectionId = 1;
6
-
7
- self.onmessage = function(event) {
8
- const { type, params, topic, message, stamp } = event.data;
9
- console.log(`Worker: Received message of type: ${type}`);
10
-
11
- switch (type) {
12
- case 'connect':
13
- connect(params, stamp);
14
- break;
15
- case 'disconnect':
16
- disconnect(stamp);
17
- break;
18
- case 'is_connected':
19
- isConnected();
20
- break;
21
- case 'clear_topics':
22
- // No action needed in the worker
23
- break;
24
- case 'subscribe':
25
- subscribe(topic, stamp);
26
- break;
27
- case 'unsubscribe':
28
- unsubscribe(topic, stamp);
29
- break;
30
- case 'send':
31
- send(topic, message);
32
- break;
33
- }
34
- };
35
-
36
- function connect(params, stamp) {
37
- console.log('Worker: Attempting to connect');
38
- const { apiMqttUrl, apiMqttClientId, region, accessKeyId, secretAccessKey, sessionToken } = params;
39
-
40
- const configBuilder = iot.AwsIotMqttConnectionConfigBuilder.new_with_websockets();
41
-
42
- configBuilder.with_clean_session(true);
43
- configBuilder.with_client_id(apiMqttClientId);
44
- configBuilder.with_endpoint(apiMqttUrl);
45
- configBuilder.with_credentials(region, accessKeyId, secretAccessKey, sessionToken);
46
- configBuilder.with_keep_alive_seconds(30);
47
- configBuilder.with_ping_timeout_ms(3000);
48
- configBuilder.with_reconnect_max_sec(5);
49
- configBuilder.with_reconnect_min_sec(1);
50
-
51
- const config = configBuilder.build();
52
- const client = new mqtt.MqttClient();
53
-
54
- connection = client.new_connection(config);
55
- // increment connection id
56
- currentConnectionId++;
57
-
58
- setupConnectionListeners(currentConnectionId);
59
-
60
- connection.connect()
61
- .then(() => {
62
- console.log('Worker: Connection successful');
63
- self.postMessage({ type: 'connect_result', data: {success: true, connectionId: currentConnectionId, stamp} });
64
- })
65
- .catch((error) => {
66
- console.error('Worker: Connection failed', error);
67
- self.postMessage({ type: 'connect_result', data: {success: false, error: error.message, stamp} });
68
- });
69
- }
70
-
71
- function disconnect(stamp) {
72
- if (connection) {
73
- const connectionId = currentConnectionId;
74
- connection.disconnect()
75
- .then(() => {
76
- if(connectionId !== currentConnectionId) {
77
- console.log('Worker: Connection Id mismatch, ignoring disconnect result', connectionId, currentConnectionId);
78
- return;
79
- }
80
- self.postMessage({ type: 'disconnect_result', data: {success: true, stamp} });
81
- })
82
- .catch((error) => {
83
- if(connectionId !== currentConnectionId) {
84
- console.log('Worker: Connection Id mismatch, ignoring disconnect result', connectionId, currentConnectionId);
85
- return;
86
- }
87
- self.postMessage({ type: 'disconnect_result', data: { success: false, error: error.message, stamp} });
88
- });
89
- connection = null;
90
- } else {
91
- self.postMessage({ type: 'disconnect_result', data: {success: true, stamp} });
92
- }
93
- }
94
-
95
- function isConnected() {
96
- const connected = connection && connection.currentState === 0 && connection.desiredState === 0;
97
- self.postMessage({ type: 'is_connected_result', data:{connected} });
98
- }
99
-
100
- function subscribe(topic, stamp) {
101
- if (connection && connection.currentState === 0 && connection.desiredState === 0) {
102
- connection.subscribe(topic, mqtt.QoS.AtLeastOnce)
103
- .then(() => {
104
- self.postMessage({ type: 'subscribe_result', data: {success: true, stamp} });
105
- })
106
- .catch((error) => {
107
- self.postMessage({ type: 'subscribe_result', data: {success: false, error: error.message, stamp} });
108
- });
109
- } else {
110
- self.postMessage({ type: 'subscribe_result', data: {success: false, error: 'Not connected', stamp} });
111
- }
112
- }
113
-
114
- function unsubscribe(topic, stamp) {
115
- if (connection && connection.currentState === 0 && connection.desiredState === 0) {
116
- connection.unsubscribe(topic)
117
- .then(() => {
118
- self.postMessage({ type: 'unsubscribe_result', data: {success: true, stamp} });
119
- })
120
- .catch((error) => {
121
- self.postMessage({ type: 'unsubscribe_result', data: {success: false, error: error.message, stamp} });
122
- });
123
- } else {
124
- self.postMessage({ type: 'unsubscribe_result', data: {success: false, error: 'Not connected', stamp} });
125
- }
126
- }
127
-
128
- function send(topic, message) {
129
- if (connection && connection.currentState === 0 && connection.desiredState === 0) {
130
- connection.publish(topic, message, mqtt.QoS.AtLeastOnce, false);
131
- } else {
132
- console.error('Cannot send message: Not connected');
133
- }
134
- }
135
-
136
- function setupConnectionListeners(connectionId) {
137
- connection.on('connect', () => {
138
- if(connectionId !== currentConnectionId) {
139
- console.log('Worker: Connection Id mismatch, ignoring connect event', connectionId, currentConnectionId);
140
- return;
141
- }
142
- self.postMessage({ type: 'connect', connectionId: connectionId});
143
- });
144
-
145
- connection.on('disconnect', () => {
146
- if(connectionId !== currentConnectionId) {
147
- console.log('Worker: Connection Id mismatch, ignoring disconnect event', connectionId, currentConnectionId);
148
- return;
149
- }
150
- self.postMessage({ type: 'disconnect', connectionId: connectionId });
151
- });
152
-
153
- connection.on('error', (error) => {
154
- if(connectionId !== currentConnectionId) {
155
- console.log('Worker: Connection Id mismatch, ignoring error event', connectionId, currentConnectionId);
156
- return;
157
- }
158
- self.postMessage({ type: 'error', data: error });
159
- });
160
-
161
- connection.on('interrupt', (error) => {
162
- if(connectionId !== currentConnectionId) {
163
- console.log('Worker: Connection Id mismatch, ignoring interrupt event', connectionId, currentConnectionId);
164
- return;
165
- }
166
- self.postMessage({ type: 'interrupt', data: error, connectionId: connectionId });
167
- });
168
-
169
- connection.on('resume', (error) => {
170
- if(connectionId !== currentConnectionId) {
171
- console.log('Worker: Connection Id mismatch, ignoring resume event', connectionId, currentConnectionId);
172
- return;
173
- }
174
- self.postMessage({ type: 'resume', data: error, connectionId: connectionId });
175
- });
176
-
177
- connection.on('message', (topic, payload) => {
178
- if(connectionId !== currentConnectionId) {
179
- console.log('Worker: Connection Id mismatch, ignoring message event', connectionId, currentConnectionId);
180
- return;
181
- }
182
- self.postMessage({ type: 'message', data: { topic, payload }, connectionId: connectionId });
183
- });
184
-
185
- connection.on('connection_success', (error) => {
186
- if(connectionId !== currentConnectionId) {
187
- console.log('Worker: Connection Id mismatch, ignoring connection_success event', connectionId, currentConnectionId);
188
- return;
189
- }
190
- self.postMessage({ type: 'connection_success', data: error, connectionId: connectionId });
191
- });
192
-
193
- connection.on('connection_failure', (error) => {
194
- if(connectionId !== currentConnectionId) {
195
- console.log('Worker: Connection Id mismatch, ignoring connection_failure event', connectionId, currentConnectionId);
196
- return;
197
- }
198
- self.postMessage({ type: 'connection_failure', data: error, connectionId: connectionId });
199
- });
200
- }
201
-
202
- // Add this at the end of the file
203
- console.log('Worker: Setup complete');