@jibb-open/jssdk 3.5.5

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 (46) hide show
  1. package/.babelrc +31 -0
  2. package/README.md +3 -0
  3. package/package.json +72 -0
  4. package/src/api/admin.js +333 -0
  5. package/src/api/auth.js +208 -0
  6. package/src/api/eventbus.js +246 -0
  7. package/src/api/index.js +26 -0
  8. package/src/api/meeting.js +421 -0
  9. package/src/api/recording.js +225 -0
  10. package/src/api/superadmin.js +84 -0
  11. package/src/api/user.js +46 -0
  12. package/src/api/webexbot.js +32 -0
  13. package/src/api/whiteboard.js +175 -0
  14. package/src/config.js +12 -0
  15. package/src/examples/browser/462.jibb.js +1 -0
  16. package/src/examples/browser/index.html +13 -0
  17. package/src/examples/browser/jibb.js +2 -0
  18. package/src/examples/browser/startSession.js +112 -0
  19. package/src/examples/examples.js +5 -0
  20. package/src/examples/webexDevicesMacros/cameraPresets/jibb.js +338 -0
  21. package/src/examples/webexDevicesMacros/simplestExample/jibb.js +212 -0
  22. package/src/examples/webexDevicesMacros/webexDevice JSSDK/jibb_WebexXapi.js +2 -0
  23. package/src/examples/webexDevicesMacros/withCameraControl/jibb.js +303 -0
  24. package/src/index.webex-devices.js +13 -0
  25. package/src/post-processing.js +39 -0
  26. package/src/types/exceptions.js +48 -0
  27. package/src/types/jibb.pb.js +1426 -0
  28. package/src/types/proto.js +7 -0
  29. package/src/types/types.js +64 -0
  30. package/src/utils/cached_variable.js +23 -0
  31. package/src/utils/future.js +24 -0
  32. package/src/utils/http/http.axios.js +34 -0
  33. package/src/utils/http/http.xapi.js +87 -0
  34. package/src/utils/http/index.js +8 -0
  35. package/src/utils/index.js +5 -0
  36. package/src/utils/logger/index.js +11 -0
  37. package/src/utils/logger/logger.empty.js +25 -0
  38. package/src/utils/logger/logger.pino.js +15 -0
  39. package/src/ws/connection_base.js +81 -0
  40. package/src/ws/eventbus.js +363 -0
  41. package/src/ws/index.js +15 -0
  42. package/src/ws/ipsa.js +238 -0
  43. package/src/ws/meeting.js +170 -0
  44. package/src/ws/observable_connection.js +84 -0
  45. package/src/ws/retry_connection.js +82 -0
  46. package/webpack.config.cjs +144 -0
@@ -0,0 +1,363 @@
1
+ import {Config} from "../config.js";
2
+ import {logger} from "../utils/logger/index.js"
3
+ import {InvalidArgumentError} from '../types/exceptions.js';
4
+ import {RetryConnection} from "./retry_connection.js";
5
+ import {ConnectionStatus} from "./connection_base.js";
6
+ import {UserClaims} from "../types/types.js";
7
+ import {types, cilix} from "../types/proto.js"
8
+ import {Auth} from "../api/auth.js";
9
+
10
+ const ClientType = types.ClientType
11
+ const ErrorCode = types.Code
12
+ const BusMessage = cilix.BusMessage
13
+
14
+
15
+ function getClientNameByType(clientType) {
16
+ let found = Object.entries(ClientType).find(([k, v]) => {
17
+ return v == clientType
18
+ });
19
+ if (!found)
20
+ throw new InvalidArgumentError(`Invalid client type: ${clientType}`)
21
+ let name = found[0].toLowerCase()
22
+ return name
23
+ }
24
+
25
+ class WSConnection extends RetryConnection {
26
+ #expiryTimer
27
+
28
+ constructor(clientType, name) {
29
+ let clientName = getClientNameByType(clientType).toLowerCase()
30
+ super(`eventbus-${clientName}`)
31
+ this.clientType = clientType
32
+ this.socket = null
33
+ this.userToken = null
34
+ this.clientName = clientName
35
+ this.name = name
36
+ this.#expiryTimer = null
37
+ }
38
+
39
+ async start() {
40
+ this.userToken = await Auth.getUserToken()
41
+ this.userClaims = new UserClaims(this.userToken)
42
+ let seconds = this.userClaims.getSecondsUntilExpiry()
43
+ if (seconds <= 0) {
44
+ this.onErrorMessage(ErrorCode.ERR_TIMEOUT, "token expired");
45
+ return
46
+ }
47
+ this.#expiryTimer = setTimeout(() => {
48
+ this.stop()
49
+ this.onErrorMessage(ErrorCode.ERR_TIMEOUT, "token expired");
50
+ }, seconds)
51
+ super.start()
52
+ }
53
+
54
+ async stop() {
55
+ clearTimeout(this.#expiryTimer)
56
+ super.stop()
57
+ }
58
+
59
+ disconnect() {
60
+ if (this.socket != null) {
61
+ this.socket.close();
62
+ this.socket = null;
63
+ //this.fireOnError(ErrorLevel.WARNING, ErrorCode.ERR_CLOSED, "closed") //this one fired after diconnection error causes multiple connection (it call retray connection again)
64
+ logger.info("eventbus disconnect ", this.clientName);
65
+ }
66
+
67
+ super.disconnect()
68
+ }
69
+
70
+ async write(msg) {
71
+ await super.waitForConnection()
72
+ logger.debug(`${this.getName()}.write: `, BusMessage.toObject(msg))
73
+ let data = BusMessage.encode(msg).finish();
74
+ this.socket.send(data)
75
+ }
76
+
77
+ writeObject(msg) {
78
+ let err = BusMessage.verify(msg)
79
+ if (err)
80
+ throw new Error(err)
81
+
82
+ let request = BusMessage.fromObject(msg)
83
+ return this.write(request)
84
+ }
85
+
86
+ async connect() {
87
+ if (this.getConnectionStatus() != ConnectionStatus.DISCONNECTED)
88
+ return
89
+
90
+ super.connect()
91
+ let self = this;
92
+
93
+ try {
94
+ let c = new UserClaims(this.userToken)
95
+ if (c.isExpired()) {
96
+ self.onErrorMessage(ErrorCode.ERR_TIMEOUT, "token expired");
97
+ return
98
+ }
99
+ } catch (err) {
100
+ logger.error(e)
101
+ self.onErrorMessage(ErrorCode.ERR_UNAUTHORIZED, "unauthorized");
102
+ return
103
+ }
104
+
105
+ let base = new URL(Config.apiBaseURL)
106
+ let uri = `wss://${base.hostname}/ws/eventbus/${this.clientName}?user_token=${this.userToken}&name=${this.name}`;
107
+
108
+ this.socket = new WebSocket(uri);
109
+ this.socket.binaryType = "arraybuffer"
110
+ this.socket.addEventListener("open", () => {
111
+ self.onConnected()
112
+ });
113
+ this.socket.addEventListener("close", () => {
114
+ self.onDisconnected()
115
+ });
116
+ this.socket.addEventListener("message", (event) => {
117
+ self.#handleMessage(event.data)
118
+ });
119
+ this.socket.addEventListener("error", (err) => {
120
+ self.onWarningMessage(ErrorCode.ERR_IO, err);
121
+ });
122
+ }
123
+
124
+ async #handleMessage(msg) {
125
+ try {
126
+ if (msg instanceof ArrayBuffer) {
127
+ let buffer = new Uint8Array(msg)
128
+ let response = BusMessage.decode(buffer)
129
+ buffer = null
130
+ logger.debug(`${this.getName()}.read: `, response)
131
+ this.onMessage(response)
132
+ return
133
+ }
134
+ } catch (err) {
135
+ logger.error(err)
136
+ }
137
+
138
+ try {
139
+ let Error = JSON.parse(msg);
140
+ switch (Error.code) {
141
+ case ErrorCode.ERR_TOO_MANY_CONNECTIONS:
142
+ case ErrorCode.ERR_UNAUTHORIZED:
143
+ case ErrorCode.ERR_BAD_REQUEST:
144
+ case ErrorCode.ERR_TIMEOUT:
145
+ this.onErrorMessage(Error.code, Error.reason)
146
+ break
147
+ default:
148
+ this.onInfoMessage(Error.code, Error.reason)
149
+ break
150
+ }
151
+ } catch (e) {
152
+ this.onWarningMessage(ErrorCode.ERR_IO, e)
153
+ }
154
+ }
155
+
156
+
157
+
158
+ onMessage(response) {
159
+ logger.debug(response)
160
+ }
161
+ }
162
+
163
+ class EventBusConnection extends WSConnection {
164
+ static instances = new Map()
165
+
166
+ static getInstance(clientType = ClientType.WEBAPP, name = "") {
167
+ let c = EventBusConnection.instances.get(clientType)
168
+ if (!c) {
169
+ c = new EventBusConnection(clientType, name)
170
+ EventBusConnection.instances.set(clientType, c)
171
+ }
172
+ return c
173
+ }
174
+
175
+ constructor(clientType = ClientType.WEBAPP, name = "") {
176
+ super(clientType, name)
177
+ }
178
+
179
+ async onMessage(msg) {
180
+ if (!msg || typeof msg !== 'object')
181
+ return
182
+
183
+ try {
184
+ msg.clientConnected && this.#onClientConnected(msg)
185
+ msg.clientDisconnected && this.#onClientDisconnected(msg)
186
+ msg.cameraListRequest && this.#onCameraListRequest(msg)
187
+ msg.cameraListResponse && this.#onCameraListResponse(msg)
188
+ msg.runtimeConfigRequest && this.#onRuntimeConfigRequest(msg)
189
+ msg.startRequest && this.#onStartRequest(msg)
190
+ msg.stopRequest && this.#onStopRequest(msg)
191
+ msg.previewRequest && this.#onPreviewRequest(msg)
192
+ msg.previewResponse && this.#onPreviewResponse(msg)
193
+ msg.ipsaResponse && this.#onIPSAResponse(msg)
194
+ msg.tooManyPublishers && this.#onTooManyPublishers(msg)
195
+ msg.recordingStarted && this.#onRecordingStarted(msg)
196
+ msg.recordingStopped && this.#onRecordingStopped(msg)
197
+ } catch (e) {
198
+ logger.error(e);
199
+ }
200
+ }
201
+
202
+ stopStream(clientId) {
203
+ return this.writeObject({
204
+ stopRequest: {
205
+ },
206
+ dst: {
207
+ id: clientId,
208
+ }
209
+ })
210
+ }
211
+
212
+ startStream({
213
+ appConfig,
214
+ config,
215
+ runtimeConfig,
216
+ camera,
217
+ sipUri,
218
+ clientId
219
+ }) {
220
+ return this.writeObject({
221
+ startRequest: {
222
+ config,
223
+ appConfig,
224
+ runtimeConfig,
225
+ camera,
226
+ sipUri,
227
+ },
228
+ dst: {
229
+ id: clientId,
230
+ }
231
+ })
232
+ }
233
+
234
+ requestCameraPreview(clientId, sourceId) {
235
+ return this.writeObject({
236
+ previewRequest: {
237
+ source: {id: sourceId, }
238
+ },
239
+ dst: {
240
+ id: clientId,
241
+ }
242
+ });
243
+ }
244
+
245
+ setRuntimeConfig(clientId, cfg) {
246
+ return this.writeObject({
247
+ runtimeConfigRequest: cfg,
248
+ dst: {
249
+ id: clientId,
250
+ }
251
+ });
252
+ }
253
+
254
+ getCameraList(clientId) {
255
+ let request = {
256
+ cameraListRequest: {
257
+ },
258
+ dst: {
259
+ id: clientId
260
+ }
261
+ }
262
+ return this.write(request);
263
+ }
264
+
265
+ sendCameraList(camList, src, dst) {
266
+ let response = {
267
+ cameraListResponse: {
268
+ items: camList
269
+ },
270
+ src: src,
271
+ dst: dst
272
+ };
273
+ return this.writeObject(response);
274
+ }
275
+
276
+ getClientStatusList() {
277
+ let request = {
278
+ clientStatusRequest: {
279
+ }
280
+ }
281
+
282
+ return this.writeObject(request);
283
+ }
284
+
285
+ #sendPreview(image, dst) {
286
+ let response = {
287
+ previewResponse: {
288
+ image: image
289
+ },
290
+ dst: dst,
291
+ };
292
+ return this.write(response);
293
+ }
294
+
295
+ async #onCameraListRequest(msg) {
296
+ let response = await this.callSubscribers('onCameraListRequest', msg.cameraListRequest)
297
+ this.sendCameraList(response, msg.dst, msg.src);
298
+ }
299
+
300
+ #onCameraListResponse(msg) {
301
+ this.notify('onCameraListResponse', msg.cameraListResponse)
302
+ }
303
+
304
+ #onClientConnected(msg) {
305
+ logger.info(`${this.getName()}: client connected: `, msg.src)
306
+ this.notify('onClientConnected', msg.src)
307
+ }
308
+
309
+ #onClientDisconnected(msg) {
310
+ logger.warn(`${this.getName()}: client disconnected: `, msg.src)
311
+ this.notify('onClientDisconnected', msg.src)
312
+ }
313
+
314
+ #onRuntimeConfigRequest(msg) {
315
+ this.notify('onRuntimeConfigRequest', msg.runtimeConfigRequest)
316
+ }
317
+
318
+ #onStopRequest(msg) {
319
+ this.notify('onStopRequest', msg.stopRequest)
320
+ }
321
+
322
+ async #onPreviewRequest(msg) {
323
+ let response = await this.callSubscribers('onPreviewRequest', msg.previewRequest)
324
+ this.#sendPreview(response, msg.src);
325
+ }
326
+
327
+ #onPreviewResponse(msg) {
328
+ this.notify('onPreviewResponse', msg.previewResponse)
329
+ }
330
+
331
+ #onStartRequest(msg) {
332
+ let req = msg.startRequest
333
+ if (!req?.camera?.id && !req?.sipUri && req?.inputSource?.id) {
334
+ req.camera = {
335
+ id: req.inputSource.id
336
+ };
337
+ }
338
+ this.notify('onStartRequest', req)
339
+ }
340
+
341
+ #onCornersReceived(msg) {
342
+ this.notify('onCornersReceived', msg.corners.corners)
343
+ }
344
+
345
+
346
+ #onIPSAResponse(msg) {
347
+ msg && this.notify('onIPSAResponse', msg.ipsaResponse)
348
+ }
349
+
350
+ #onTooManyPublishers(msg) {
351
+ this.notify('onTooManyPublishers', msg.tooManyPublishers)
352
+ }
353
+
354
+ #onRecordingStarted(msg) {
355
+ this.notify('onRecordingStarted', msg.recordingStarted)
356
+ }
357
+
358
+ #onRecordingStopped(msg) {
359
+ this.notify('onRecordingStopped', msg.recordingStopped)
360
+ }
361
+ }
362
+
363
+ export {EventBusConnection}
@@ -0,0 +1,15 @@
1
+ import {EventBusConnection} from "./eventbus.js";
2
+ import {IPSA} from "./ipsa.js";
3
+ import {MeetingConnection} from "./meeting.js";
4
+
5
+ if (globalThis?.WebSocket == undefined) {
6
+ import("isomorphic-ws").then((webSocket) => {
7
+ globalThis.WebSocket = webSocket.default;
8
+ });
9
+ }
10
+
11
+ export {
12
+ MeetingConnection,
13
+ EventBusConnection,
14
+ IPSA,
15
+ }
package/src/ws/ipsa.js ADDED
@@ -0,0 +1,238 @@
1
+ import {Config as SDKConfig} from "../config.js";
2
+ import {logger} from "../utils/logger/index.js"
3
+ import {RetryConnection} from "./retry_connection.js";
4
+ import {ConnectionStatus} from "./connection_base.js";
5
+ import {UserClaims, MeetingClaims} from "../types/types.js";
6
+ import {ipsa, types} from "../types/proto.js"
7
+ const ErrorCode = types.Code
8
+
9
+ class IPSAClass extends RetryConnection {
10
+ #expiryTimer
11
+
12
+ constructor() {
13
+ super('ipsa')
14
+ this.socket = null
15
+ this.onInputImage = null
16
+ this.onError = null
17
+ this.onResponse = null
18
+ this.userToken = null
19
+ this.#expiryTimer = null
20
+ this.#clear()
21
+ }
22
+
23
+ start({userToken, request, onInputImage, onError, onResponse}) {
24
+ if (!this.isStarted()) {
25
+ this.setRuntimeConfig(request.runtimeConfig)
26
+ this.setAppConfig(request.appConfig)
27
+ this.#setConfig(request.config)
28
+ this.onInputImage = onInputImage
29
+ this.onError = onError
30
+ this.onResponse = onResponse
31
+ this.userToken = userToken
32
+
33
+ let meetingClaims = new MeetingClaims(this.appConfigRequest.appConfig.meetingToken)
34
+ let userClaims = new UserClaims(this.userToken)
35
+ let seconds = Math.min(userClaims.getSecondsUntilExpiry(), meetingClaims.getSecondsUntilExpiry())
36
+ if (seconds <= 0) {
37
+ this.onErrorMessage(ErrorCode.ERR_TIMEOUT, "token expired");
38
+ return
39
+ } else {
40
+ this.#expiryTimer = setTimeout(() => {
41
+ this.stop()
42
+ this.onErrorMessage(ErrorCode.ERR_TIMEOUT, "token expired");
43
+ }, seconds)
44
+ }
45
+ }
46
+ super.start()
47
+ }
48
+
49
+ stop() {
50
+ clearTimeout(this.#expiryTimer)
51
+ this.#clear()
52
+ super.stop()
53
+ }
54
+
55
+ #clear() {
56
+ this.appConfigRequest = null
57
+ this.configRequest = null
58
+ this.runtimeConfigRequest = null
59
+ this.requestTimes = new Array(8)
60
+ this.responseTimes = new Array(8)
61
+ this.responseTimes.fill(1000)
62
+ this.requestId = 0
63
+ }
64
+
65
+ disconnect() {
66
+ super.disconnect()
67
+ if (this.socket != null) {
68
+ this.socket.close();
69
+ this.socket = null;
70
+ // this.fireOnError(ErrorLevel.ERROR, ErrorCode.ERR_CLOSED, "closed") //this one fired after diconnection error causes multiple connection (it call retray connection again)
71
+ }
72
+ }
73
+
74
+ async #write(msg) {
75
+ let err = ipsa.Request.verify(msg)
76
+ if (err)
77
+ throw new Error("invalid message: " + err)
78
+
79
+ if (msg) {
80
+ let data = ipsa.Request.encode(msg).finish()
81
+ this.socket.send(data)
82
+ }
83
+ }
84
+
85
+ async connect() {
86
+ if (this.getConnectionStatus() != ConnectionStatus.DISCONNECTED)
87
+ return
88
+
89
+ super.connect()
90
+
91
+ let meetingId = this.appConfigRequest.appConfig.meetingId
92
+ let meetingToken = this.appConfigRequest.appConfig.meetingToken
93
+
94
+ try {
95
+ let c = new UserClaims(this.userToken)
96
+ if (c.isExpired()) {
97
+ this.onErrorMessage(ErrorCode.ERR_TIMEOUT, "token expired");
98
+ return
99
+ }
100
+ } catch (err) {
101
+ this.onErrorMessage(ErrorCode.ERR_UNAUTHORIZED, "unauthorized");
102
+ return
103
+ }
104
+
105
+ logger.info(`Connecting IPSA to meeting ${meetingId}...`);
106
+ let base = new URL(SDKConfig.apiBaseURL)
107
+ let uri = `wss://${base.hostname}/ws/ipsa/${meetingId}?user_token=${this.userToken}&meeting_token=${meetingToken}`;
108
+
109
+ this.socket = new WebSocket(uri);
110
+ this.socket.binaryType = "arraybuffer"
111
+ this.socket.addEventListener("open", async () => {
112
+ try {
113
+ await this.#write(this.configRequest)
114
+ await this.#write(this.runtimeConfigRequest)
115
+ await this.#write(this.appConfigRequest)
116
+ this.onConnected()
117
+ } catch (err) {
118
+ this.onWarningMessage(ErrorCode.ERR_IO, err)
119
+ this.disconnect()
120
+ return
121
+ }
122
+
123
+ let loopFunction = () => {
124
+ if (!this.isConnected() || !this.isStarted())
125
+ return
126
+
127
+ let wait = Math.max(500, this.getAverageResponseTime())
128
+ logger.debug("Next IPSA request: ", wait)
129
+ setTimeout(loopFunction, wait)
130
+
131
+ this.#writeInputImage().catch(err => logger.error(err))
132
+ }
133
+
134
+ loopFunction()
135
+ });
136
+ this.socket.addEventListener("close", () => {
137
+ this.onDisconnected()
138
+ });
139
+ this.socket.addEventListener("message", (event) => {
140
+ this.#handleMessage(event.data);
141
+ });
142
+ this.socket.addEventListener("error", (err) => {
143
+ this.onWarningMessage(ErrorCode.ERR_IO, err);
144
+ });
145
+ }
146
+
147
+ getAverageResponseTime() {
148
+ let sum = this.responseTimes.reduce((a, b) => {return a + b})
149
+ return sum / this.responseTimes.length
150
+ }
151
+
152
+ async #handleMessage(msg) {
153
+ try {
154
+ if (msg instanceof ArrayBuffer) {
155
+ let buffer = new Uint8Array(msg)
156
+ let response = ipsa.Response.decode(buffer)
157
+ this.#onResponse(response);
158
+ return
159
+ }
160
+ } catch (err) {
161
+ logger.error(err)
162
+ }
163
+
164
+ try {
165
+ msg = JSON.parse(msg);
166
+
167
+ switch (msg.code) {
168
+ case ErrorCode.ERR_TOO_MANY_CONNECTIONS:
169
+ case ErrorCode.ERR_UNAUTHORIZED:
170
+ case ErrorCode.ERR_BAD_REQUEST:
171
+ this.onErrorMessage(msg?.code, msg?.reason)
172
+ this.stop()
173
+ break
174
+ default:
175
+ this.onInfoMessage(msg?.code, msg?.reason)
176
+ break
177
+ }
178
+ } catch (e) {
179
+ this.onWarningMessage(ErrorCode.ERR_IO, e)
180
+ }
181
+ }
182
+
183
+ #onResponse(response) {
184
+ if (response.id > 100) {
185
+ let id = response.id - 100
186
+ this.responseTimes[id] = performance.now() - this.requestTimes[id]
187
+ }
188
+
189
+ if (this.onResponse) {
190
+ this.onResponse(response)
191
+ }
192
+ }
193
+
194
+ async #writeInputImage() {
195
+ let img = await this.onInputImage()
196
+ this.requestId = (this.requestId + 1) % this.requestTimes.length
197
+ this.requestTimes[this.requestId] = performance.now()
198
+
199
+ if (this.socket.bufferedAmount > 2 * img.length) {
200
+ logger.warn("ipsa: low internet speed connection")
201
+ return
202
+ }
203
+
204
+ let request = ipsa.Request.create({
205
+ id: this.requestId + 100,
206
+ ipsa: {
207
+ data: img,
208
+ }
209
+ })
210
+ this.#write(request)
211
+ }
212
+
213
+
214
+ #setConfig(cfg) {
215
+ let request = ipsa.Request.create({config: cfg})
216
+ this.configRequest = request
217
+ if (this.isConnected())
218
+ this.#write(request)
219
+ }
220
+
221
+ setRuntimeConfig(cfg) {
222
+ let request = ipsa.Request.create({runtimeConfig: cfg})
223
+ this.runtimeConfigRequest = request
224
+ if (this.isConnected())
225
+ this.#write(request)
226
+ }
227
+
228
+ setAppConfig(cfg) {
229
+ let request = ipsa.Request.create({appConfig: cfg})
230
+ this.appConfigRequest = request
231
+ if (this.isConnected())
232
+ this.#write(request)
233
+ }
234
+ }
235
+
236
+
237
+ export let IPSA = new IPSAClass()
238
+