@stream-io/video-client 0.0.1-alpha.137 → 0.0.1-alpha.139

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/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  This file was generated using [@jscutlery/semver](https://github.com/jscutlery/semver).
4
4
 
5
+ ## [0.0.1-alpha.139](https://github.com/GetStream/stream-video-js/compare/client0.0.1-alpha.138...client0.0.1-alpha.139) (2023-05-15)
6
+
7
+
8
+ ### Features
9
+
10
+ * faster join flow ([#499](https://github.com/GetStream/stream-video-js/issues/499)) ([898341b](https://github.com/GetStream/stream-video-js/commit/898341b26495412acea7d9e08c0c8f9b0b54e3b3))
11
+
12
+
13
+
14
+ ## [0.0.1-alpha.138](https://github.com/GetStream/stream-video-js/compare/client0.0.1-alpha.137...client0.0.1-alpha.138) (2023-05-15)
15
+
16
+
17
+
5
18
  ## [0.0.1-alpha.137](https://github.com/GetStream/stream-video-js/compare/client0.0.1-alpha.136...client0.0.1-alpha.137) (2023-05-12)
6
19
 
7
20
 
@@ -6003,30 +6003,6 @@ function removeConnectionEventListeners(cb) {
6003
6003
  }
6004
6004
  }
6005
6005
 
6006
- const hostnameFromUrl = (url) => {
6007
- try {
6008
- const u = new URL(url);
6009
- return {
6010
- hostname: u.hostname,
6011
- port: u.port,
6012
- };
6013
- }
6014
- catch (e) {
6015
- console.warn(`Invalid URL. Can't extract hostname from it.`, e);
6016
- return {
6017
- hostname: url,
6018
- port: 3031,
6019
- };
6020
- }
6021
- };
6022
- const toURL = (url) => {
6023
- try {
6024
- return new URL(url);
6025
- }
6026
- catch (e) {
6027
- return null;
6028
- }
6029
- };
6030
6006
  /**
6031
6007
  * The client used for exchanging information with the SFU.
6032
6008
  */
@@ -6036,9 +6012,10 @@ class StreamSfuClient {
6036
6012
  *
6037
6013
  * @param dispatcher the event dispatcher to use.
6038
6014
  * @param url the URL of the SFU.
6015
+ * @param wsEndpoint the WebSocket endpoint of the SFU.
6039
6016
  * @param token the JWT token to use for authentication.
6040
6017
  */
6041
- constructor(dispatcher, url, token) {
6018
+ constructor(dispatcher, url, wsEndpoint, token) {
6042
6019
  /**
6043
6020
  * A buffer for ICE Candidates that are received before
6044
6021
  * the PeerConnections are ready to handle them.
@@ -6133,17 +6110,6 @@ class StreamSfuClient {
6133
6110
  }),
6134
6111
  ],
6135
6112
  });
6136
- // FIXME: OL: this should come from the coordinator API
6137
- const { hostname, port } = hostnameFromUrl(url);
6138
- let wsEndpoint = `ws://${hostname}:${port}/ws`;
6139
- if (!['localhost', '127.0.0.1'].includes(hostname)) {
6140
- const sfuUrl = toURL(url);
6141
- if (sfuUrl) {
6142
- sfuUrl.protocol = 'wss:';
6143
- sfuUrl.pathname = '/ws';
6144
- wsEndpoint = sfuUrl.toString();
6145
- }
6146
- }
6147
6113
  // Special handling for the ICETrickle kind of events.
6148
6114
  // These events might be triggered by the SFU before the initial RTC
6149
6115
  // connection is established. In that case, those events (ICE candidates)
@@ -7373,99 +7339,19 @@ const registerRingingCallEventHandlers = (call) => {
7373
7339
  };
7374
7340
  };
7375
7341
 
7376
- const toSeconds = (ms) => ms / 1000;
7377
7342
  /**
7378
- * Measures the latency of the current client to the given endpoint.
7343
+ * Collects all necessary information to join a call, talks to the coordinator
7344
+ * and returns the necessary information to join the call.
7379
7345
  *
7380
- * @param endpoint the endpoint.
7381
- * @param timeoutAfterMs the request cancellation period.
7382
- */
7383
- const measureResourceLoadLatencyTo = (endpoint, timeoutAfterMs = 1000) => __awaiter(void 0, void 0, void 0, function* () {
7384
- const start = Date.now();
7385
- const controller = new AbortController();
7386
- const abortTimeout = setTimeout(() => {
7387
- controller.abort();
7388
- }, timeoutAfterMs);
7389
- try {
7390
- const src = new URL(endpoint);
7391
- src.searchParams.set('r', `js_${Math.random() * 10000000}`);
7392
- yield fetch(src.toString(), {
7393
- signal: controller.signal,
7394
- });
7395
- const latency = Date.now() - start;
7396
- return toSeconds(latency);
7397
- }
7398
- catch (e) {
7399
- console.debug(`failed to measure latency to ${endpoint}`, e);
7400
- return -1; // indicate error in measurement
7401
- }
7402
- finally {
7403
- // clear timeout in case fetch completes before timeout
7404
- clearTimeout(abortTimeout);
7405
- }
7406
- });
7407
- /**
7408
- * Measures the latency of the current client to the given edges.
7409
- *
7410
- * All measurements run in parallel,
7411
- * and the whole process is limited by the given timeout.
7412
- *
7413
- * @param edges the edges to measure latency to.
7414
- * @param attempts the number of attempts to measure latency.
7415
- * @param attemptTimeoutAfterMs the request cancellation period per measurement.
7416
- * @param measureTimeoutAfterMs the hard-limit for the whole measure process.
7417
- */
7418
- const measureLatencyToEdges = (edges, { attempts = 3, attemptTimeoutAfterMs = 1000, measureTimeoutAfterMs = 1200, } = {}) => __awaiter(void 0, void 0, void 0, function* () {
7419
- const latencyByEdge = {};
7420
- const measurements = [];
7421
- const start = Date.now();
7422
- for (let attempt = 0; attempt < attempts; attempt++) {
7423
- for (const edge of edges) {
7424
- measurements.push(measureResourceLoadLatencyTo(edge.latency_url, attemptTimeoutAfterMs).then((latency) => {
7425
- var _a;
7426
- var _b;
7427
- ((_a = latencyByEdge[_b = edge.name]) !== null && _a !== void 0 ? _a : (latencyByEdge[_b] = [])).push(latency);
7428
- }));
7429
- }
7430
- }
7431
- yield Promise.race([
7432
- Promise.all(measurements),
7433
- new Promise((resolve) => setTimeout(resolve, measureTimeoutAfterMs)),
7434
- ]);
7435
- console.log(`finished measuring latency to ${edges.length} edges in ${Date.now() - start}ms.`);
7436
- return latencyByEdge;
7437
- });
7438
-
7439
- const getCascadingModeParams = () => {
7440
- var _a;
7441
- if (typeof window === 'undefined')
7442
- return null;
7443
- const params = new URLSearchParams((_a = window.location) === null || _a === void 0 ? void 0 : _a.search);
7444
- const cascadingEnabled = params.get('cascading') !== null;
7445
- if (cascadingEnabled) {
7446
- const rawParams = {};
7447
- params.forEach((value, key) => {
7448
- rawParams[key] = value;
7449
- });
7450
- return rawParams;
7451
- }
7452
- return null;
7453
- };
7454
- const watch = (httpClient, type, id, data) => __awaiter(void 0, void 0, void 0, function* () {
7455
- yield httpClient.connectionIdPromise;
7456
- // FIXME OL: remove this once cascading is enabled by default
7457
- const cascadingModeParams = getCascadingModeParams();
7458
- if (cascadingModeParams) {
7459
- return httpClient.doAxiosRequest('post', `/call/${type}/${id}/join`, data, {
7460
- params: Object.assign({}, cascadingModeParams),
7461
- });
7462
- }
7463
- return httpClient.post(`/call/${type}/${id}/join`, data);
7464
- });
7346
+ * @param httpClient the http client to use.
7347
+ * @param type the type of the call.
7348
+ * @param id the id of the call.
7349
+ * @param data the data for the call.
7350
+ */
7465
7351
  const join = (httpClient, type, id, data) => __awaiter(void 0, void 0, void 0, function* () {
7466
- const joinCallResponse = yield watch(httpClient, type, id, data);
7467
- const { call, edges, members } = joinCallResponse;
7468
- const { credentials } = yield getCallEdgeServer(httpClient, type, id, edges);
7352
+ yield httpClient.connectionIdPromise;
7353
+ const joinCallResponse = yield doJoin(httpClient, type, id, data);
7354
+ const { call, credentials, members } = joinCallResponse;
7469
7355
  return {
7470
7356
  connectionConfig: toRtcConfiguration(credentials.ice_servers),
7471
7357
  sfuServer: credentials.server,
@@ -7474,18 +7360,31 @@ const join = (httpClient, type, id, data) => __awaiter(void 0, void 0, void 0, f
7474
7360
  members,
7475
7361
  };
7476
7362
  });
7477
- const getCallEdgeServer = (httpClient, type, id, edges) => __awaiter(void 0, void 0, void 0, function* () {
7478
- const data = {
7479
- latency_measurements: yield measureLatencyToEdges(edges),
7480
- };
7363
+ const doJoin = (httpClient, type, id, data) => __awaiter(void 0, void 0, void 0, function* () {
7364
+ const location = yield getLocationHint();
7365
+ const request = Object.assign(Object.assign({}, data), { location });
7481
7366
  // FIXME OL: remove this once cascading is enabled by default
7482
7367
  const cascadingModeParams = getCascadingModeParams();
7483
7368
  if (cascadingModeParams) {
7484
- return httpClient.doAxiosRequest('post', `/call/${type}/${id}/get_edge_server`, data, {
7369
+ return httpClient.doAxiosRequest('post', `/call/${type}/${id}/join`, request, {
7485
7370
  params: Object.assign({}, cascadingModeParams),
7486
7371
  });
7487
7372
  }
7488
- return httpClient.post(`/call/${type}/${id}/get_edge_server`, data);
7373
+ return httpClient.post(`/call/${type}/${id}/join`, request);
7374
+ });
7375
+ const getLocationHint = () => __awaiter(void 0, void 0, void 0, function* () {
7376
+ const hintURL = `https://hint.stream-io-video.com/`;
7377
+ try {
7378
+ const response = yield fetch(hintURL, {
7379
+ method: 'HEAD',
7380
+ });
7381
+ const awsPop = response.headers.get('x-amz-cf-pop') || 'ERR';
7382
+ return awsPop.substring(0, 3); // AMS1-P2 -> AMS
7383
+ }
7384
+ catch (e) {
7385
+ console.error(`Failed to get location hint from ${hintURL}`, e);
7386
+ return 'ERR';
7387
+ }
7489
7388
  });
7490
7389
  const toRtcConfiguration = (config) => {
7491
7390
  if (!config || config.length === 0)
@@ -7499,6 +7398,21 @@ const toRtcConfiguration = (config) => {
7499
7398
  };
7500
7399
  return rtcConfig;
7501
7400
  };
7401
+ const getCascadingModeParams = () => {
7402
+ var _a;
7403
+ if (typeof window === 'undefined')
7404
+ return null;
7405
+ const params = new URLSearchParams((_a = window.location) === null || _a === void 0 ? void 0 : _a.search);
7406
+ const cascadingEnabled = params.get('cascading') !== null;
7407
+ if (cascadingEnabled) {
7408
+ const rawParams = {};
7409
+ params.forEach((value, key) => {
7410
+ rawParams[key] = value;
7411
+ });
7412
+ return rawParams;
7413
+ }
7414
+ return null;
7415
+ };
7502
7416
 
7503
7417
  /**
7504
7418
  * Creates a new StatsReporter instance that collects metrics about the ongoing call and reports them to the state store
@@ -8058,6 +7972,8 @@ class Call {
8058
7972
  const response = yield this.streamClient.get(this.streamClientBasePath);
8059
7973
  this.state.setMetadata(response.call);
8060
7974
  this.state.setMembers(response.members);
7975
+ this.watching = true;
7976
+ this.clientStore.registerCall(this);
8061
7977
  return response;
8062
7978
  });
8063
7979
  /**
@@ -8072,21 +7988,6 @@ class Call {
8072
7988
  }
8073
7989
  this.state.setMetadata(response.call);
8074
7990
  this.state.setMembers(response.members);
8075
- this.clientStore.registerCall(this);
8076
- return response;
8077
- });
8078
- /**
8079
- * Will start to watch for call related WebSocket events, but it won't join the call. If you watch a call you'll be notified about WebSocket events, but you won't be able to publish your audio and video, and you won't be able to see and hear others. You won't show up in the list of joined participants.
8080
- *
8081
- * @param data
8082
- */
8083
- this.watch = (data) => __awaiter(this, void 0, void 0, function* () {
8084
- const response = yield watch(this.streamClient, this.type, this.id, data);
8085
- this.state.setMetadata(response.call);
8086
- this.state.setMembers(response.members);
8087
- if ((data === null || data === void 0 ? void 0 : data.ring) && !this.ringing) {
8088
- this.ringingSubject.next(true);
8089
- }
8090
7991
  this.watching = true;
8091
7992
  this.clientStore.registerCall(this);
8092
7993
  return response;
@@ -8117,14 +8018,17 @@ class Call {
8117
8018
  this.clientStore.registerCall(this);
8118
8019
  // FIXME OL: remove once cascading is implemented
8119
8020
  let sfuUrl = call.sfuServer.url;
8021
+ let sfuWsUrl = call.sfuServer.ws_endpoint;
8120
8022
  if (typeof window !== 'undefined' &&
8121
8023
  window.location &&
8122
8024
  window.location.search) {
8123
8025
  const params = new URLSearchParams(window.location.search);
8124
8026
  const sfuUrlParam = params.get('sfuUrl');
8125
8027
  sfuUrl = sfuUrlParam || call.sfuServer.url;
8028
+ const sfuWsUrlParam = params.get('sfuWsUrl');
8029
+ sfuWsUrl = sfuWsUrlParam || call.sfuServer.ws_endpoint;
8126
8030
  }
8127
- const sfuClient = (this.sfuClient = new StreamSfuClient(this.dispatcher, sfuUrl, call.token));
8031
+ const sfuClient = (this.sfuClient = new StreamSfuClient(this.dispatcher, sfuUrl, sfuWsUrl, call.token));
8128
8032
  /**
8129
8033
  * A closure which hides away the re-connection logic.
8130
8034
  */
@@ -10567,7 +10471,7 @@ class StreamClient {
10567
10471
  }
10568
10472
  getUserAgent() {
10569
10473
  return (this.userAgent ||
10570
- `stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${"0.0.1-alpha.136"}`);
10474
+ `stream-video-javascript-client-${this.node ? 'node' : 'browser'}-${"0.0.1-alpha.138"}`);
10571
10475
  }
10572
10476
  setUserAgent(userAgent) {
10573
10477
  this.userAgent = userAgent;