@reactoo/watchtogether-sdk-js 2.7.21 → 2.7.24

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,189 +1,172 @@
1
-
2
- import {device} from 'aws-iot-device-sdk';
3
1
  import emitter from './wt-emitter';
4
- import {decodeJanusDisplay} from "./wt-utils";
2
+ import { decodeJanusDisplay } from "./wt-utils";
3
+ import { mqtt, iot } from 'aws-iot-device-sdk-v2';
5
4
 
6
5
  class Iot {
7
-
8
6
  constructor(enableDebugFlag) {
9
7
  Object.assign(this, emitter());
10
- this.device = null;
11
8
  this.decoder = new TextDecoder('utf-8');
12
9
  this.connectionActive = false;
13
10
  this.log = Iot.noop;
14
- this.debugFlag = enableDebugFlag;
11
+ // Remove: this.debugFlag = enableDebugFlag;
15
12
  this.credentialsExpirationCheckIntervalId = null;
16
- this.currentCredentialsExpirationStamp = null
17
- if(enableDebugFlag) {
13
+ this.currentCredentialsExpirationStamp = null;
14
+ this.lastConnectParams = null;
15
+ this.connection = null;
16
+ this.subscribedTopics = new Set();
17
+
18
+ if (enableDebugFlag) {
18
19
  this.enableDebug();
19
20
  }
20
21
  }
21
-
22
+
22
23
  static noop() {}
23
-
24
+
24
25
  enableDebug() {
25
26
  this.log = console.log.bind(console);
26
27
  }
27
-
28
- startCredentialsExpirationCheck(expiration) {
29
- this.stopCredentialsExpirationCheck();
30
- this.currentCredentialsExpirationStamp = new Date(expiration).getTime();
31
- this.credentialsExpirationCheckIntervalId = setInterval(() => {
32
- const curentTimeStamp = new Date().getTime();
33
- if(this.currentCredentialsExpirationStamp - curentTimeStamp <= 300000) {
34
- this.emit('updateCredentials');
35
- }
36
- }, 5000);
37
- }
38
-
39
- stopCredentialsExpirationCheck() {
40
- clearInterval(this.credentialsExpirationCheckIntervalId);
41
- this.credentialsExpirationCheckIntervalId = null;
42
- }
43
-
44
- updateWebSocketCredentials(accessKeyId, secretAccessKey, sessionToken, expiration) {
45
- this.log('iot updateWebSocketCredentials');
46
- if(this.device) {
47
- this.device.updateWebSocketCredentials(accessKeyId, secretAccessKey, sessionToken);
48
- this.startCredentialsExpirationCheck(expiration);
49
- }
50
- }
51
-
52
- connect(apiMqttUrl, apiMqttClientId, region, accessKeyId, secretAccessKey, sessionToken, expiration, forceDisconnect = false) {
53
- this.log('iot connect');
54
- return this.disconnect(forceDisconnect).then(() => {
55
- return new Promise((resolve, reject) => {
56
- this.device = device({
57
- protocol:'wss',
58
- clientId: apiMqttClientId,
59
- region,
60
- host: apiMqttUrl,
61
- accessKeyId: accessKeyId,
62
- secretKey: secretAccessKey,
63
- sessionToken: sessionToken,
64
- keepalive: 30,
65
- maximumReconnectTimeMs: 8000,
66
- enableMetrics: false,
67
- debug: this.debugFlag,
68
- autoResubscribe: true
28
+
29
+ connect(apiMqttUrl, apiMqttClientId, region, accessKeyId, secretAccessKey, sessionToken, expiration) {
30
+ this.log('iot connect called, we disconnect first just to be sure');
31
+ return this.disconnect()
32
+ .finally(() => {
33
+ this.log('iot connect');
34
+ this.lastConnectParams = { apiMqttUrl, apiMqttClientId, region, accessKeyId, secretAccessKey, sessionToken, expiration };
35
+
36
+ const configBuilder = iot.AwsIotMqttConnectionConfigBuilder.new_with_websockets();
37
+
38
+ configBuilder.with_clean_session(true);
39
+ configBuilder.with_client_id(apiMqttClientId);
40
+ configBuilder.with_endpoint(apiMqttUrl);
41
+ configBuilder.with_credentials(region, accessKeyId, secretAccessKey, sessionToken);
42
+ configBuilder.with_keep_alive_seconds(30);
43
+ configBuilder.with_ping_timeout_ms(3000);
44
+ configBuilder.with_reconnect_max_sec(5);
45
+ configBuilder.with_reconnect_min_sec(1);
46
+
47
+ const config = configBuilder.build();
48
+
49
+ const client = new mqtt.MqttClient();
50
+ this.connection = client.new_connection(config);
51
+
52
+ this.connection.on('connect', () => {
53
+ this.connectionActive = true;
54
+ this.emit('connect');
69
55
  });
70
-
71
- this.startCredentialsExpirationCheck(expiration);
72
-
73
- let __s = () => {
74
- this.device?.off('connect', __s);
75
- this.device?.off('error', __e);
76
- resolve(this.device)
77
- };
78
-
79
- let __e = (e) => {
80
- this.device?.off('connect', __s);
81
- this.device?.off('error', __e);
82
- reject(e);
83
- };
84
-
85
- this.device.once('connect', __s);
86
- this.device.once('error', __e);
87
-
88
- this.device.on('message', this.__messageCb.bind(this));
89
- this.device.on('connect', this.__connectCb.bind(this));
90
- this.device.on('reconnect', this.__reconnectCb.bind(this));
91
- this.device.on('error', this.__failureCb.bind(this));
92
- this.device.on('close', this.__closeCb.bind(this));
93
- this.device.on('offline', this.__offlineCb.bind(this));
94
-
95
- })
96
- })
97
-
56
+
57
+ this.connection.on('disconnect', () => {
58
+ this.connectionActive = false;
59
+ this.emit('disconnect');
60
+ });
61
+
62
+ this.connection.on('error', (error) => {
63
+ this.connectionActive = false;
64
+ this.emit('error', error);
65
+ });
66
+
67
+ this.connection.on('interrupt', (error) => {
68
+ this.connectionActive = false;
69
+ this.emit('interrupt', error);
70
+ });
71
+
72
+ this.connection.on('resume', (error) => {
73
+ this.connectionActive = true;
74
+ this.emit('resume', error);
75
+ });
76
+
77
+ this.connection.on('message', (topic, payload) => {
78
+ this.handleMessage(topic, new Uint8Array(payload));
79
+ });
80
+
81
+ this.connection.on('connection_success', (error) => {
82
+ this.connectionActive = true;
83
+ this.emit('connection_success', error);
84
+ });
85
+
86
+ this.connection.on('connection_failure', (error) => {
87
+ this.connectionActive = false;
88
+ this.emit('connection_failure', error);
89
+ });
90
+
91
+ this.connection.on('closed', (error) => {
92
+ this.connectionActive = false;
93
+ this.emit('closed', error);
94
+ });
95
+
96
+ return this.connection.connect()
97
+ .then(() => {
98
+ this.startCredentialsExpirationCheck(expiration);
99
+ });
100
+
101
+ });
98
102
  }
99
-
100
- disconnect(force = false) {
101
-
103
+
104
+
105
+ disconnect() {
106
+ this.log('iot disconnect');
102
107
  this.stopCredentialsExpirationCheck();
103
-
104
- return new Promise((resolve, reject) => {
105
- if(!this.device) {
106
- resolve();
107
- return;
108
- }
109
- let __i = null;
110
- let __c = () => {
111
- clearTimeout(__i);
112
- this.device = null;
113
- resolve();
114
- };
115
- __i = setTimeout(__c, 4000);
116
- this.device.off('message', this.__messageCb.bind(this));
117
- this.device.off('connect', this.__connectCb.bind(this));
118
- this.device.off('reconnect', this.__reconnectCb.bind(this));
119
- this.device.off('error', this.__failureCb.bind(this));
120
- this.device.off('close', this.__closeCb.bind(this));
121
- this.device.off('offline', this.__offlineCb.bind(this));
122
- this.device.end(force, __c);
123
- });
108
+ if (this.connection) {
109
+ this.connectionActive = false;
110
+ return this.connection.disconnect();
111
+ } else return Promise.resolve();
124
112
  }
125
-
113
+
126
114
  isConnected() {
127
115
  return this.connectionActive;
128
116
  }
129
-
117
+
130
118
  subscribe(topic) {
131
119
  this.log('iot subscribe', topic);
132
- return this.device && this.device.subscribe(topic);
120
+ if (this.connection && typeof topic === 'string' && topic.trim() !== '') {
121
+ this.connection.subscribe(topic, mqtt.QoS.AtLeastOnce);
122
+ this.subscribedTopics.add(topic);
123
+ } else {
124
+ this.log('Invalid topic or not connected:', topic);
125
+ }
133
126
  }
134
-
127
+
135
128
  unsubscribe(topic) {
136
129
  this.log('iot unsubscribe', topic);
137
- return this.device && this.device.unsubscribe(topic);
130
+ if (this.connection && typeof topic === 'string' && topic.trim() !== '') {
131
+ this.connection.unsubscribe(topic);
132
+ this.subscribedTopics.delete(topic);
133
+ } else {
134
+ this.log('Invalid topic or not connected:', topic);
135
+ }
138
136
  }
139
-
137
+
140
138
  send(topic, message) {
141
139
  this.log('iot send', topic, message);
142
140
  let msg = typeof message === 'object' ? JSON.stringify(message) : message;
143
- return this.device && this.device.publish(topic, msg);
144
- }
145
-
146
- __reconnectCb() {
147
- this.log('iot reconnect');
148
- this.emit('reconnect');
149
- }
150
- __connectCb() {
151
- this.log('iot connect');
152
- this.connectionActive = true;
153
- this.emit('connect');
154
- }
155
- __failureCb(err) {
156
- this.log('iot failure');
157
- this.emit('error', err);
158
- }
159
- __closeCb(responseObject) {
160
- this.log('iot close');
161
- this.connectionActive = false;
162
- this.emit('close');
163
- }
164
- __offlineCb(responseObject) {
165
- this.log('iot offline');
166
- this.emit('offline');
141
+ if (this.connection) {
142
+ this.connection.publish(topic, msg, mqtt.QoS.AtLeastOnce, false);
143
+ }
167
144
  }
168
-
169
- __messageCb(t, message, packet) {
170
- const topic = t.split('/');
171
- let payload = JSON.parse(this.decoder.decode(message));
172
145
 
173
- if(payload.display) {
174
- const decodedDisplay = decodeJanusDisplay(payload.display);
146
+ handleMessage(topic, payload) {
147
+ const topicParts = topic.split('/');
148
+ let message;
149
+ try {
150
+ message = JSON.parse(this.decoder.decode(payload));
151
+ } catch (error) {
152
+ this.log('Error parsing message:', error);
153
+ return;
154
+ }
155
+
156
+ if(message.display) {
157
+ const decodedDisplay = decodeJanusDisplay(message.display);
175
158
  if(decodedDisplay.userId) {
176
- payload = {...payload, userId: decodedDisplay.userId, role: decodedDisplay.role, start: decodedDisplay.start};
159
+ message = {...message, userId: decodedDisplay.userId, role: decodedDisplay.role, start: decodedDisplay.start};
177
160
  }
178
161
  }
179
162
 
180
- if(topic[0] === 'user') { // user
181
- const userId = topic[1].replace("_", ':');
182
- this.emit('message', {userId, ...payload, event: payload.event ? `user:${payload.event}` : 'user'});
183
- } else if(topic[0] === 'wt') {
184
- const event = payload.event;
185
- const roomId = topic[2];
186
- if(topic[1] === 'room') { // room
163
+ if(topicParts[0] === 'user') { // user
164
+ const userId = topicParts[1].replace("_", ':');
165
+ this.emit('message', {userId, ...message, event: message.event ? `user:${message.event}` : 'user'});
166
+ } else if(topicParts[0] === 'wt') {
167
+ const event = message.event;
168
+ const roomId = topicParts[2];
169
+ if(topicParts[1] === 'room') { // room
187
170
  if(
188
171
  event === 'message' ||
189
172
  event === 'template_updated' ||
@@ -221,36 +204,123 @@ class Iot {
221
204
  event === 'handsCleared' ||
222
205
  event === 'volume_set'
223
206
  ) {
224
- this.emit('message', {event, ...payload, roomId})
207
+ this.emit('message', {event, ...message, roomId})
225
208
  }
226
209
  else if(event === 'joined' || event === 'leaving') {
227
- this.emit('message', {event, ...payload, isObserver:!!payload.isObserver, roomId});
210
+ this.emit('message', {event, ...message, isObserver:!!message.isObserver, roomId});
228
211
  }
229
212
  }
230
- else if(topic[1] === 'instanceroom') { // instance
213
+ else if(topicParts[1] === 'instanceroom') { // instance
231
214
  if(event === 'add_room' || event === 'remove_room' || event === 'set_room' || event === "instance_homepage_changed" || event === 'instance_settings_changed') {
232
- this.emit('message', {event, ...payload});
215
+ this.emit('message', {event, ...message});
233
216
  }
234
217
  }
235
- else if(topic[1] === 'externalmix') {
236
- const event = payload.event;
237
- this.emit('message', {event, ...payload});
218
+ else if(topicParts[1] === 'externalmix') {
219
+ const event = message.event;
220
+ this.emit('message', {event, ...message});
238
221
  }
239
- else if(topic[1] === 'asset') {
240
- this.emit('message', {event: 'asset', assetId: topic[2], ...payload});
222
+ else if(topicParts[1] === 'asset') {
223
+ this.emit('message', {event: 'asset', assetId: topicParts[2], ...message});
241
224
  }
242
- } else if(topic[0] === 'wtr' || topic[0] === 'gwtr') {
243
- const recorderId = topic[1];
244
- const sessionId = topic[2];
245
- if(topic[3] === 'control') {
246
- this.emit('message', {event: 'recorder_control', ...payload, recorderId, sessionId});
225
+ } else if(topicParts[0] === 'wtr' || topicParts[0] === 'gwtr') {
226
+ const recorderId = topicParts[1];
227
+ const sessionId = topicParts[2];
228
+ if(topicParts[3] === 'control') {
229
+ this.emit('message', {event: 'recorder_control', ...message, recorderId, sessionId});
247
230
  } // recorder control
248
- else if(topic[3] === 'monitor') {
249
- this.emit('message', {event: 'recorder_monitor', ...payload, recorderId, sessionId});
231
+ else if(topicParts[3] === 'monitor') {
232
+ this.emit('message', {event: 'recorder_monitor', ...message, recorderId, sessionId});
250
233
  } // recorder monitor
251
234
  }
252
235
  }
253
- }
254
236
 
237
+ startCredentialsExpirationCheck(expiration) {
238
+ this.stopCredentialsExpirationCheck();
239
+ this.currentCredentialsExpirationStamp = new Date(expiration).getTime();
240
+ this.credentialsExpirationCheckIntervalId = setInterval(() => {
241
+ const currentTimeStamp = new Date().getTime();
242
+ if(this.currentCredentialsExpirationStamp - currentTimeStamp <= 300000) {
243
+ this.emit('updateCredentials');
244
+ }
245
+ }, 5000);
246
+ }
247
+
248
+ stopCredentialsExpirationCheck() {
249
+ clearInterval(this.credentialsExpirationCheckIntervalId);
250
+ this.credentialsExpirationCheckIntervalId = null;
251
+ }
252
+
253
+ updateWebSocketCredentials(accessKeyId, secretAccessKey, sessionToken, expiration) {
254
+ this.log('iot updateWebSocketCredentials');
255
+ this.lastConnectParams = {...this.lastConnectParams, accessKeyId, secretAccessKey, sessionToken, expiration };
256
+ const currentTopics = new Set(this.subscribedTopics);
257
+ return this.disconnect()
258
+ .then(() => this.connect(
259
+ this.lastConnectParams.apiMqttUrl,
260
+ this.lastConnectParams.apiMqttClientId,
261
+ this.lastConnectParams.region,
262
+ accessKeyId,
263
+ secretAccessKey,
264
+ sessionToken,
265
+ expiration
266
+ ))
267
+ .then(() => {
268
+ // Resubscribe to topics
269
+ currentTopics.forEach(topic => this.subscribe(topic));
270
+ this.startCredentialsExpirationCheck(expiration);
271
+ })
272
+ }
273
+
274
+ checkConnection() {
275
+ return new Promise((resolve, reject) => {
276
+ if (!this.connection || !this.connectionActive) {
277
+ reject(new Error('Not connected'));
278
+ return;
279
+ }
280
+
281
+ if (this.subscribedTopics.size === 0) {
282
+ reject(new Error('No subscribed topics available for connection check'));
283
+ return;
284
+ }
285
+
286
+ // Find a suitable topic for the connection check
287
+ const suitableTopic = Array.from(this.subscribedTopics).find(topic => topic !== 'wt/instanceroom/reactooDemo');
288
+
289
+ if (!suitableTopic) {
290
+ reject(new Error('No suitable topic found for connection check'));
291
+ return;
292
+ }
293
+
294
+ const testMessage = {
295
+ type: 'connection_check',
296
+ timestamp: Date.now()
297
+ };
298
+
299
+ const timeoutId = setTimeout(() => {
300
+ this.off('message', checkMessageHandler);
301
+ reject(new Error('Connection check timeout'));
302
+ }, 5000); // 5 seconds timeout
303
+
304
+ const checkMessageHandler = (message) => {
305
+ if (message.type === 'connection_check' && message.timestamp === testMessage.timestamp) {
306
+ clearTimeout(timeoutId);
307
+ this.off('message', checkMessageHandler);
308
+ resolve();
309
+ }
310
+ };
311
+
312
+ this.on('message', checkMessageHandler);
313
+
314
+ try {
315
+ this.send(suitableTopic, testMessage);
316
+ } catch (error) {
317
+ clearTimeout(timeoutId);
318
+ this.off('message', checkMessageHandler);
319
+ reject(new Error(`Publish error: ${error.message}`));
320
+ }
321
+ });
322
+ }
323
+ }
255
324
 
256
325
  export default Iot;
326
+
@@ -95,10 +95,10 @@ class RoomSession {
95
95
  "watchparty": ['participant', 'talkback'],
96
96
  "studio": ['participant', 'talkback', 'host', 'observer'],
97
97
  "commentary": ['participant', 'talkback', 'host'],
98
- "intercom": ['host', 'talkback', 'observer', 'observerSolo1', 'observerSolo2', 'observerSolo3'],
99
- "videowall": ['host', 'talkback', 'observer', 'observerSolo1', 'observerSolo2', 'observerSolo3'],
100
- "videowall-queue": ['host', 'talkback', 'observer', 'observerSolo1', 'observerSolo2', 'observerSolo3'],
101
- "videowall-queue-video": ['host', 'talkback', 'observer', 'observerSolo1', 'observerSolo2', 'observerSolo3']
98
+ "intercom": ['host', 'talkback', 'observer', 'observerSolo1', 'observerSolo2', 'observerSolo3', 'observerSolo4', 'observerSolo5'],
99
+ "videowall": ['host', 'talkback', 'observer', 'observerSolo1', 'observerSolo2', 'observerSolo3', 'observerSolo4', 'observerSolo5'],
100
+ "videowall-queue": ['host', 'talkback', 'observer', 'observerSolo1', 'observerSolo2', 'observerSolo3', 'observerSolo4', 'observerSolo5'],
101
+ "videowall-queue-video": ['host', 'talkback', 'observer', 'observerSolo1', 'observerSolo2', 'observerSolo3', 'observerSolo4', 'observerSolo5']
102
102
  },
103
103
  monitor: {
104
104
  "watchparty": ['participant', 'host'],
@@ -154,6 +154,24 @@ class RoomSession {
154
154
  "videowall-queue": ['participant'],
155
155
  "videowall-queue-video": ['participant'],
156
156
  },
157
+ observerSolo4: {
158
+ "watchparty": ['participant'],
159
+ "studio": ['participant'],
160
+ "commentary": ['participant'],
161
+ "intercom": ['participant'],
162
+ "videowall": ['participant'],
163
+ "videowall-queue": ['participant'],
164
+ "videowall-queue-video": ['participant'],
165
+ },
166
+ observerSolo5: {
167
+ "watchparty": ['participant'],
168
+ "studio": ['participant'],
169
+ "commentary": ['participant'],
170
+ "intercom": ['participant'],
171
+ "videowall": ['participant'],
172
+ "videowall-queue": ['participant'],
173
+ "videowall-queue-video": ['participant'],
174
+ },
157
175
  host: {
158
176
  "watchparty": [],
159
177
  "studio": [],
@@ -324,6 +342,8 @@ class RoomSession {
324
342
  case 'observerSolo1':
325
343
  case 'observerSolo2':
326
344
  case 'observerSolo3':
345
+ case 'observerSolo4':
346
+ case 'observerSolo5':
327
347
  return 'addRemoteObserver';
328
348
  case 'host':
329
349
  return 'addRemoteInstructor';
@@ -361,6 +381,8 @@ class RoomSession {
361
381
  case 'observerSolo1':
362
382
  case 'observerSolo2':
363
383
  case 'observerSolo3':
384
+ case 'observerSolo4':
385
+ case 'observerSolo5':
364
386
  return 'removeRemoteObserver';
365
387
  case 'host':
366
388
  return 'removeRemoteInstructor';
package/webpack.config.js CHANGED
@@ -1,75 +1,74 @@
1
1
  /* global __dirname, require, module*/
2
2
 
3
- var PACKAGE = require('./package.json');
4
- var banner = ' ' + PACKAGE.name + ' \n @version ' + PACKAGE.version;
5
-
6
3
  const path = require('path');
7
4
  const webpack = require('webpack');
8
- const env = require('yargs').argv.env; // use --env with webpack 2
9
- const pkg = require('./package.json');
10
5
  const TerserPlugin = require('terser-webpack-plugin');
6
+ const pkg = require('./package.json');
11
7
 
12
- let libraryName = 'watchtogether-sdk';
13
-
14
- let outputFile, mode;
15
-
16
- if (env === 'build') {
17
- mode = 'production';
18
- outputFile = libraryName + '.min.js';
19
- } else {
20
- mode = 'development';
21
- outputFile = libraryName + '.js';
22
- }
8
+ const banner = ` ${pkg.name} \n @version ${pkg.version}`;
9
+ const libraryName = 'watchtogether-sdk';
23
10
 
11
+ module.exports = (env, argv) => {
12
+ const mode = argv.mode || 'development';
13
+ const outputFile = mode === 'production' ? `${libraryName}.min.js` : `${libraryName}.js`;
24
14
 
25
- module.exports = {
26
- mode: mode,
27
- entry: __dirname + '/src/index.js',
28
- plugins: [
29
- new webpack.BannerPlugin(banner)
30
- ],
31
- output: {
32
- jsonpFunction: 'watchtogethersdkloader',
33
- path: path.resolve(__dirname, 'dist'),
34
- filename: outputFile,
35
- library: 'WatchTogetherSDK',
36
- libraryTarget: 'umd',
37
- libraryExport: "default",
38
- umdNamedDefine: true,
39
- globalObject: "typeof self !== 'undefined' ? self : this"
40
- },
41
- module: {
42
- rules: [
43
- {
44
- test: /\.(js)$/,
45
- use: 'babel-loader'
46
- }
47
- ]
48
- },
49
- optimization: {
50
- minimizer: [new TerserPlugin({
51
- parallel: 2,
52
- terserOptions: {
53
- warnings: false,
54
- parse: {},
55
- compress: {},
56
- mangle: true,
57
- output: {
58
- comments: /@version/i
15
+ return {
16
+ mode,
17
+ entry: './src/index.js',
18
+ output: {
19
+ path: path.resolve(__dirname, 'dist'),
20
+ filename: outputFile,
21
+ library: 'WatchTogetherSDK',
22
+ libraryTarget: 'umd',
23
+ libraryExport: 'default',
24
+ umdNamedDefine: true,
25
+ globalObject: "typeof self !== 'undefined' ? self : this"
26
+ },
27
+ // Remove the experiments section
28
+ module: {
29
+ rules: [
30
+ {
31
+ test: /\.js$/,
32
+ exclude: /node_modules/,
33
+ use: {
34
+ loader: 'babel-loader'
35
+ }
59
36
  },
60
- toplevel: false,
61
- nameCache: null,
62
- ie8: false,
63
- keep_fnames: true,
64
- },
65
- })],
66
- },
67
- resolve: {
68
- modules: ['node_modules', path.resolve('./src')],
69
- extensions: ['.json', '.js']
70
- },
71
- node: {
72
- fs: 'empty',
73
- tls: 'empty'
74
- }
37
+ {
38
+ test: /\.worker\.js$/,
39
+ use: { loader: 'worker-loader' }
40
+ }
41
+ ]
42
+ },
43
+ plugins: [
44
+ new webpack.BannerPlugin(banner),
45
+ new webpack.ProvidePlugin({
46
+ process: 'process/browser',
47
+ }),
48
+ ],
49
+ optimization: {
50
+ minimizer: [
51
+ new TerserPlugin({
52
+ terserOptions: {
53
+ output: {
54
+ comments: /@version/i
55
+ }
56
+ },
57
+ extractComments: false
58
+ })
59
+ ]
60
+ },
61
+ resolve: {
62
+ modules: ['node_modules', path.resolve('./src')],
63
+ extensions: ['.js', '.json'],
64
+ fallback: {
65
+ "util": require.resolve("util/"),
66
+ "stream": require.resolve("stream-browserify"),
67
+ "buffer": require.resolve("buffer/"),
68
+ "crypto": require.resolve("crypto-browserify"),
69
+ "process": require.resolve("process/browser"),
70
+ }
71
+ },
72
+ devtool: mode === 'development' ? 'source-map' : false,
73
+ };
75
74
  };