From d50da0077832eb7a17c176b4ba65f8dd663d015e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Apr 2021 16:29:43 +0000 Subject: [PATCH 01/10] Bump elliptic from 6.5.3 to 6.5.4 Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.3 to 6.5.4. - [Release notes](https://github.com/indutny/elliptic/releases) - [Commits](https://github.com/indutny/elliptic/compare/v6.5.3...v6.5.4) Signed-off-by: dependabot[bot] --- package-lock.json | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/package-lock.json b/package-lock.json index 02103edc..df5b100c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1808,24 +1808,24 @@ "dev": true }, "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", "dev": true, "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", + "bn.js": "^4.11.9", + "brorand": "^1.1.0", "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" }, "dependencies": { "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==", "dev": true } } From 6f0fab8685183c4f5773ebf698291bdb78a880ef Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Apr 2021 09:10:03 +0000 Subject: [PATCH 02/10] Bump y18n from 4.0.0 to 4.0.3 Bumps [y18n](https://github.com/yargs/y18n) from 4.0.0 to 4.0.3. - [Release notes](https://github.com/yargs/y18n/releases) - [Changelog](https://github.com/yargs/y18n/blob/y18n-v4.0.3/CHANGELOG.md) - [Commits](https://github.com/yargs/y18n/compare/v4.0.0...y18n-v4.0.3) Signed-off-by: dependabot[bot] --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index df5b100c..9fac52f4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7005,9 +7005,9 @@ "dev": true }, "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", "dev": true }, "yargs": { From 0cad5931944e3341500e01e349d564043856b7a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Apr 2021 16:29:20 +0000 Subject: [PATCH 03/10] Bump ssri from 6.0.1 to 6.0.2 Bumps [ssri](https://github.com/npm/ssri) from 6.0.1 to 6.0.2. - [Release notes](https://github.com/npm/ssri/releases) - [Changelog](https://github.com/npm/ssri/blob/v6.0.2/CHANGELOG.md) - [Commits](https://github.com/npm/ssri/compare/v6.0.1...v6.0.2) Signed-off-by: dependabot[bot] --- package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 9fac52f4..6ab4fc08 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6828,9 +6828,9 @@ } }, "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", + "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", "dev": true, "requires": { "figgy-pudding": "^3.5.1" From e664471a43476f963d2f26368acd769b86b2ca14 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Apr 2021 16:29:26 +0000 Subject: [PATCH 04/10] Bump elliptic from 6.5.3 to 6.5.4 in /sample Bumps [elliptic](https://github.com/indutny/elliptic) from 6.5.3 to 6.5.4. - [Release notes](https://github.com/indutny/elliptic/releases) - [Commits](https://github.com/indutny/elliptic/compare/v6.5.3...v6.5.4) Signed-off-by: dependabot[bot] --- sample/package-lock.json | 61 +++++++++++----------------------------- 1 file changed, 16 insertions(+), 45 deletions(-) diff --git a/sample/package-lock.json b/sample/package-lock.json index 844f1a7d..4cce2f9a 100644 --- a/sample/package-lock.json +++ b/sample/package-lock.json @@ -472,8 +472,7 @@ "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" }, "browserify-aes": { "version": "1.2.0", @@ -998,25 +997,23 @@ } }, "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", - "dev": true, + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", + "bn.js": "^4.11.9", + "brorand": "^1.1.0", "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" }, "dependencies": { "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" } } }, @@ -1467,7 +1464,6 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, "requires": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" @@ -1482,7 +1478,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, "requires": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", @@ -1532,8 +1527,7 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "is-accessor-descriptor": { "version": "0.1.6", @@ -1837,14 +1831,12 @@ "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" }, "minimalistic-crypto-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" }, "minimatch": { "version": "3.0.4", @@ -3677,27 +3669,6 @@ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" }, - "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", - "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" - }, - "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" - } - } - }, "emoji-regex": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", From f6ec04fadc27091a95981ab36b56d46b205f612a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 29 Apr 2021 16:29:30 +0000 Subject: [PATCH 05/10] Bump ssri from 6.0.1 to 6.0.2 in /sample Bumps [ssri](https://github.com/npm/ssri) from 6.0.1 to 6.0.2. - [Release notes](https://github.com/npm/ssri/releases) - [Changelog](https://github.com/npm/ssri/blob/v6.0.2/CHANGELOG.md) - [Commits](https://github.com/npm/ssri/compare/v6.0.1...v6.0.2) Signed-off-by: dependabot[bot] --- sample/package-lock.json | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/sample/package-lock.json b/sample/package-lock.json index 4cce2f9a..fc800b1f 100644 --- a/sample/package-lock.json +++ b/sample/package-lock.json @@ -1249,8 +1249,7 @@ "figgy-pudding": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", - "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", - "dev": true + "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==" }, "file-uri-to-path": { "version": "1.0.0", @@ -3000,7 +2999,6 @@ "p-map": "^3.0.0", "promise-inflight": "^1.0.1", "rimraf": "^2.7.1", - "ssri": "^7.0.0", "unique-filename": "^1.1.1" }, "dependencies": { @@ -7017,15 +7015,6 @@ "tweetnacl": "~0.14.0" } }, - "ssri": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-7.1.0.tgz", - "integrity": "sha512-77/WrDZUWocK0mvA5NTRQyveUf+wsrIc6vyrxpS8tVvYBcX215QbafrJR3KtkpskIzoFLqqNuuYQvxaMjXJ/0g==", - "requires": { - "figgy-pudding": "^3.5.1", - "minipass": "^3.1.1" - } - }, "static-extend": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", @@ -8119,14 +8108,6 @@ "ajv-keywords": "^3.1.0" } }, - "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "requires": { - "figgy-pudding": "^3.5.1" - } - }, "terser-webpack-plugin": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz", @@ -8951,10 +8932,9 @@ } }, "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "dev": true, + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.2.tgz", + "integrity": "sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==", "requires": { "figgy-pudding": "^3.5.1" } From 0797d825f021ce49425943bb8a9d866560977224 Mon Sep 17 00:00:00 2001 From: C-A de Salaberry Date: Tue, 18 Aug 2020 11:40:30 +0200 Subject: [PATCH 06/10] feat(publisher) :sparkles: prevent publisher cleanup option --- README.md | 82 ++++++++++++----------- src/NetworkTest/index.ts | 1 + src/NetworkTest/testConnectivity/index.ts | 6 +- src/NetworkTest/testQuality/index.ts | 6 +- 4 files changed, 54 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index d44fcb4c..b947578f 100644 --- a/README.md +++ b/README.md @@ -234,21 +234,25 @@ The `OTNetworkTest()` constructor includes the following parameters: will be used in the real OpenTok session. This way, the test prompts the end user to grant permission to the correct device. Note that changing the video device may not influence the quality test score. - - * `initSessionOptions` (Object) -- An object that includes optional options - for initializing the session - ([Session Options](https://tokbox.com/developer/sdks/js/reference/OT.html#initSession)). + + * `initSessionOptions` (Object) -- An object that includes optional options + for initializing the session + ([Session Options](https://tokbox.com/developer/sdks/js/reference/OT.html#initSession)). This object includes the following properties: * `ipWhitelist ` (Boolean) -- This is available as an add-on feature - for **enterprise accounts**. Set this to true if IP white listing - is enabled for your project. The default value is false. + for **enterprise accounts**. Set this to true if IP white listing + is enabled for your project. The default value is false. * `iceConfig ` (Object) -- This feature is part of the configurable TURN add-on feature. - * `proxyServerUrl` (String) -- (Optional) Set this to the proxy server URL + * `proxyServerUrl` (String) -- (Optional) Set this to the proxy server URL you use in the OpenTok client SDKs (for example, when calling `OT.setProxyUrl()` - in OpenTok.js). For more information, please check the + in OpenTok.js). For more information, please check the [IP Proxy Documentation](https://tokbox.com/developer/guides/ip-proxy/). + * `skipPublisherCleaningOnSuccess` (Boolean) -- (Optional) On iOS if a publisher is displayed + while the tests are running, the publisher turns black once the tests are done. + this switch allows you to skip cleaning the publisher in the normal publisher flow. + The `options` parameter is optional. The constructor throws an Error object with a `message` property and a `name` property. The @@ -505,7 +509,7 @@ an error object (against the values defined in ErrorNames) to determine the type | ------------------------------------------------------------------ | ----------- | | `MISSING_OPENTOK_INSTANCE` | An instance of OT, the OpenTok.js client SDK, was not passed into the constructor. | | `INCOMPLETE_SESSON_CREDENTIALS` | The sessionInfo object passed into the constructor did not include an `apiKey`, `sessionId`, or `token` object. | -| `MISSING_SESSON_CREDENTIALS` | No sessionInfo object was passed into the constructor. | +| `MISSING_SESSON_CREDENTIALS` | No sessionInfo object was passed into the constructor. | #### testConnectivity() errors @@ -516,22 +520,22 @@ the following: | Error.name property set to this property
of ErrorNames ... | Description | | -------------------------------------------------------------- | ----------- | -| `API_CONNECTIVITY_ERROR` | The test failed to connect to OpenTOK API Server. | -| `CONNECT_TO_SESSION_ERROR` | The test failed to connect to the test OpenTok session due to a network error. | -| `CONNECT_TO_SESSION_TOKEN_ERROR` | The test failed to connect to the test OpenTok session due to an invalid token. | -| `CONNECT_TO_SESSION_ID_ERROR` | The test failed to connect to the test OpenTok session due to an invalid session ID. | -| `CONNECT_TO_SESSION_NETWORK_ERROR` | The test failed to connect to the test OpenTok session due to a network error. | -| `FAILED_TO_OBTAIN_MEDIA_DEVICES` | The test failed to obtain media devices (a camera or microphone). | -| `NO_AUDIO_CAPTURE_DEVICES` | The browser cannot access a microphone. | -| `NO_VIDEO_CAPTURE_DEVICES` | The browser cannot access a camera. | -| `PUBLISH_TO_SESSION_ERROR` | Encountered an unknown error while attempting to publish to a session. | -| `FAILED_MESSAGING_SERVER_TEST` | The test failed to connect to media server due to messaging server connection failure. | -| `FAILED_TO_CREATE_LOCAL_PUBLISHER` | The test failed to create a local publisher object. | -| `PUBLISH_TO_SESSION_NOT_CONNECTED` | The test failed to publish to the test session because the client was not connected to the session. | -| `PUBLISH_TO_SESSION_PERMISSION_OR_TIMEOUT_ERROR` | The test failed to publish to the test session due a permissions error or timeout. | -| `PUBLISH_TO_SESSION_NETWORK_ERROR` | The test failed to publish to the test session due a network error. | -| `SUBSCRIBE_TO_SESSION_ERROR` | The test encountered an unknown error while attempting to subscribe to a test stream. | -| `LOGGING_SERVER_CONNECTION_ERROR` | The test failed to connect to the OpenTok logging server. | +| `API_CONNECTIVITY_ERROR` | The test failed to connect to OpenTOK API Server. | +| `CONNECT_TO_SESSION_ERROR` | The test failed to connect to the test OpenTok session due to a network error. | +| `CONNECT_TO_SESSION_TOKEN_ERROR` | The test failed to connect to the test OpenTok session due to an invalid token. | +| `CONNECT_TO_SESSION_ID_ERROR` | The test failed to connect to the test OpenTok session due to an invalid session ID. | +| `CONNECT_TO_SESSION_NETWORK_ERROR` | The test failed to connect to the test OpenTok session due to a network error. | +| `FAILED_TO_OBTAIN_MEDIA_DEVICES` | The test failed to obtain media devices (a camera or microphone). | +| `NO_AUDIO_CAPTURE_DEVICES` | The browser cannot access a microphone. | +| `NO_VIDEO_CAPTURE_DEVICES` | The browser cannot access a camera. | +| `PUBLISH_TO_SESSION_ERROR` | Encountered an unknown error while attempting to publish to a session. | +| `FAILED_MESSAGING_SERVER_TEST` | The test failed to connect to media server due to messaging server connection failure. | +| `FAILED_TO_CREATE_LOCAL_PUBLISHER` | The test failed to create a local publisher object. | +| `PUBLISH_TO_SESSION_NOT_CONNECTED` | The test failed to publish to the test session because the client was not connected to the session. | +| `PUBLISH_TO_SESSION_PERMISSION_OR_TIMEOUT_ERROR` | The test failed to publish to the test session due a permissions error or timeout. | +| `PUBLISH_TO_SESSION_NETWORK_ERROR` | The test failed to publish to the test session due a network error. | +| `SUBSCRIBE_TO_SESSION_ERROR` | The test encountered an unknown error while attempting to subscribe to a test stream. | +| `LOGGING_SERVER_CONNECTION_ERROR` | The test failed to connect to the OpenTok logging server. | #### testQuality() errors @@ -541,20 +545,20 @@ method has a `name` property set to one of the following: | Error.name property set to this
property of ErrorNames ... | Description | | -------------------------------------------------------------- | ----------- | | `INVALID_ON_UPDATE_CALLBACK` | The `updateCallback` parameter is invalid. It must be a function that accepts a single parameter. | -| `UNSUPPORTED_BROWSER` | The test is running on an unsupported browser (see [Supported browsers](#supported-browsers)). | -| `CONNECT_TO_SESSION_ERROR` | The test failed to connect to the test OpenTok session due to a network error. | -| `CONNECT_TO_SESSION_TOKEN_ERROR` | The test failed to connect to the test OpenTok session due to an invalid token. | -| `CONNECT_TO_SESSION_ID_ERROR` | The test failed to connect to the test OpenTok session due to an invalid session ID. | -| `CONNECT_TO_SESSION_NETWORK_ERROR` | The test failed to connect to the test OpenTok session due to a network error. | -| `FAILED_TO_OBTAIN_MEDIA_DEVICES` | The test failed to obtain media devices (a camera or microphone). | -| `NO_AUDIO_CAPTURE_DEVICES` | The browser cannot access a microphone. | -| `NO_VIDEO_CAPTURE_DEVICES` | The browser cannot access a camera. | -| `PUBLISH_TO_SESSION_ERROR` | The test encountered an unknown error while attempting to publish to a session. | -| `INIT_PUBLISHER_ERROR` | The test failed to initialize a publisher. | -| `PUBLISH_TO_SESSION_NOT_CONNECTED` | The test failed to publish to the test session because the client was not connected to the session. | -| `PUBLISH_TO_SESSION_PERMISSION_OR_TIMEOUT_ERROR` | The test failed to publish to the test session due a permissions error or timeout. | -| `SUBSCRIBE_TO_SESSION_ERROR` | The test encountered an unknown error while attempting to subscribe to a test stream. | -| `SUBSCRIBER_GET_STATS_ERROR` | The test failed to get audio and video statistics for the test stream. | +| `UNSUPPORTED_BROWSER` | The test is running on an unsupported browser (see [Supported browsers](#supported-browsers)). | +| `CONNECT_TO_SESSION_ERROR` | The test failed to connect to the test OpenTok session due to a network error. | +| `CONNECT_TO_SESSION_TOKEN_ERROR` | The test failed to connect to the test OpenTok session due to an invalid token. | +| `CONNECT_TO_SESSION_ID_ERROR` | The test failed to connect to the test OpenTok session due to an invalid session ID. | +| `CONNECT_TO_SESSION_NETWORK_ERROR` | The test failed to connect to the test OpenTok session due to a network error. | +| `FAILED_TO_OBTAIN_MEDIA_DEVICES` | The test failed to obtain media devices (a camera or microphone). | +| `NO_AUDIO_CAPTURE_DEVICES` | The browser cannot access a microphone. | +| `NO_VIDEO_CAPTURE_DEVICES` | The browser cannot access a camera. | +| `PUBLISH_TO_SESSION_ERROR` | The test encountered an unknown error while attempting to publish to a session. | +| `INIT_PUBLISHER_ERROR` | The test failed to initialize a publisher. | +| `PUBLISH_TO_SESSION_NOT_CONNECTED` | The test failed to publish to the test session because the client was not connected to the session. | +| `PUBLISH_TO_SESSION_PERMISSION_OR_TIMEOUT_ERROR` | The test failed to publish to the test session due a permissions error or timeout. | +| `SUBSCRIBE_TO_SESSION_ERROR` | The test encountered an unknown error while attempting to subscribe to a test stream. | +| `SUBSCRIBER_GET_STATS_ERROR` | The test failed to get audio and video statistics for the test stream. | ## MOS estimates diff --git a/src/NetworkTest/index.ts b/src/NetworkTest/index.ts index acb7048a..0492fe2d 100644 --- a/src/NetworkTest/index.ts +++ b/src/NetworkTest/index.ts @@ -35,6 +35,7 @@ export interface NetworkTestOptions { videoSource?: string; initSessionOptions?: OT.InitSessionOptions; proxyServerUrl?: string; + skipPublisherCleaningOnSuccess?: boolean; } export default class NetworkTest { diff --git a/src/NetworkTest/testConnectivity/index.ts b/src/NetworkTest/testConnectivity/index.ts index a192d8dd..d9160f7a 100644 --- a/src/NetworkTest/testConnectivity/index.ts +++ b/src/NetworkTest/testConnectivity/index.ts @@ -295,7 +295,11 @@ export function testConnectivity( }; otLogging.logEvent({ action: 'testConnectivity', variation: 'Success' }); return cleanSubscriber(flowResults.session, flowResults.subscriber) - .then(() => cleanPublisher(flowResults.publisher)) + .then(() => { + if (options?.skipPublisherCleaningOnSuccess) return; + + return cleanPublisher(flowResults.publisher); + }) .then(() => disconnectFromSession(flowResults.session)) .then(() => resolve(results)); }; diff --git a/src/NetworkTest/testQuality/index.ts b/src/NetworkTest/testQuality/index.ts index b1f64650..6808bb7c 100644 --- a/src/NetworkTest/testQuality/index.ts +++ b/src/NetworkTest/testQuality/index.ts @@ -291,7 +291,11 @@ function checkSubscriberQuality( session.off(); }); cleanSubscriber(session, subscriber) - .then(() => cleanPublisher(publisher)) + .then(() => { + if (options?.skipPublisherCleaningOnSuccess) return; + + return cleanPublisher(publisher); + }) .then(() => session.disconnect()); } }; From 7b1556c356fe52e9f8dd95688e3e51dc05d36236 Mon Sep 17 00:00:00 2001 From: C-A de Salaberry Date: Tue, 18 Aug 2020 14:39:14 +0200 Subject: [PATCH 07/10] fix(ts) :bug: incompatible ts feature used --- src/NetworkTest/testConnectivity/index.ts | 2 +- src/NetworkTest/testQuality/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NetworkTest/testConnectivity/index.ts b/src/NetworkTest/testConnectivity/index.ts index d9160f7a..f855e775 100644 --- a/src/NetworkTest/testConnectivity/index.ts +++ b/src/NetworkTest/testConnectivity/index.ts @@ -296,7 +296,7 @@ export function testConnectivity( otLogging.logEvent({ action: 'testConnectivity', variation: 'Success' }); return cleanSubscriber(flowResults.session, flowResults.subscriber) .then(() => { - if (options?.skipPublisherCleaningOnSuccess) return; + if (options && options.skipPublisherCleaningOnSuccess) return; return cleanPublisher(flowResults.publisher); }) diff --git a/src/NetworkTest/testQuality/index.ts b/src/NetworkTest/testQuality/index.ts index 6808bb7c..a08bd7e9 100644 --- a/src/NetworkTest/testQuality/index.ts +++ b/src/NetworkTest/testQuality/index.ts @@ -292,7 +292,7 @@ function checkSubscriberQuality( }); cleanSubscriber(session, subscriber) .then(() => { - if (options?.skipPublisherCleaningOnSuccess) return; + if (options && options.skipPublisherCleaningOnSuccess) return; return cleanPublisher(publisher); }) From d6b5f1c4b488903cbaea92eb6742912e8b83b249 Mon Sep 17 00:00:00 2001 From: C-A de Salaberry Date: Tue, 18 Aug 2020 14:43:16 +0200 Subject: [PATCH 08/10] fix(build) webpack complained with empty return --- src/NetworkTest/testConnectivity/index.ts | 2 +- src/NetworkTest/testQuality/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NetworkTest/testConnectivity/index.ts b/src/NetworkTest/testConnectivity/index.ts index f855e775..ce3e6978 100644 --- a/src/NetworkTest/testConnectivity/index.ts +++ b/src/NetworkTest/testConnectivity/index.ts @@ -296,7 +296,7 @@ export function testConnectivity( otLogging.logEvent({ action: 'testConnectivity', variation: 'Success' }); return cleanSubscriber(flowResults.session, flowResults.subscriber) .then(() => { - if (options && options.skipPublisherCleaningOnSuccess) return; + if (options && options.skipPublisherCleaningOnSuccess) return Promise.resolve(); return cleanPublisher(flowResults.publisher); }) diff --git a/src/NetworkTest/testQuality/index.ts b/src/NetworkTest/testQuality/index.ts index a08bd7e9..a4d250c0 100644 --- a/src/NetworkTest/testQuality/index.ts +++ b/src/NetworkTest/testQuality/index.ts @@ -292,7 +292,7 @@ function checkSubscriberQuality( }); cleanSubscriber(session, subscriber) .then(() => { - if (options && options.skipPublisherCleaningOnSuccess) return; + if (options && options.skipPublisherCleaningOnSuccess) return Promise.resolve(); return cleanPublisher(publisher); }) From 3b66703c4fa1c39744d9406a6f7612abc2730bb0 Mon Sep 17 00:00:00 2001 From: C-A de Salaberry Date: Tue, 18 Aug 2020 14:44:26 +0200 Subject: [PATCH 09/10] fix(build) :package: temporarily add built assets --- .gitignore | 4 +- dist/NetworkTest/errors/index.d.ts | 22 +++++ dist/NetworkTest/errors/types.d.ts | 68 ++++++++++++++ dist/NetworkTest/index.d.ts | 58 ++++++++++++ dist/NetworkTest/index.js | 2 + dist/NetworkTest/index.js.map | 1 + .../testConnectivity/errors/index.d.ts | 82 ++++++++++++++++ .../testConnectivity/errors/mapping.d.ts | 20 ++++ dist/NetworkTest/testConnectivity/index.d.ts | 19 ++++ .../NetworkTest/testQuality/errors/index.d.ts | 76 +++++++++++++++ .../testQuality/helpers/MOSState.d.ts | 24 +++++ .../helpers/calculateQualityStats.d.ts | 3 + .../helpers/calculateThroughput.d.ts | 3 + .../testQuality/helpers/config.d.ts | 30 ++++++ .../helpers/getLatestSampleWindow.d.ts | 2 + .../helpers/getQualityEvaluation.d.ts | 8 ++ .../helpers/isBitrateSteadyState.d.ts | 2 + .../helpers/isSupportedBrowser.d.ts | 5 + .../testQuality/helpers/subscriberMOS.d.ts | 4 + dist/NetworkTest/testQuality/index.d.ts | 22 +++++ dist/NetworkTest/testQuality/types/stats.d.ts | 31 +++++++ dist/NetworkTest/types/callbacks.d.ts | 5 + .../NetworkTest/types/opentok/connection.d.ts | 5 + dist/NetworkTest/types/opentok/error.d.ts | 4 + dist/NetworkTest/types/opentok/events.d.ts | 17 ++++ dist/NetworkTest/types/opentok/index.d.ts | 77 +++++++++++++++ dist/NetworkTest/types/opentok/publisher.d.ts | 92 ++++++++++++++++++ dist/NetworkTest/types/opentok/session.d.ts | 93 +++++++++++++++++++ dist/NetworkTest/types/opentok/stream.d.ts | 15 +++ .../NetworkTest/types/opentok/subscriber.d.ts | 64 +++++++++++++ dist/NetworkTest/types/opentok/widget.d.ts | 18 ++++ dist/NetworkTest/util/index.d.ts | 48 ++++++++++ 32 files changed, 922 insertions(+), 2 deletions(-) create mode 100644 dist/NetworkTest/errors/index.d.ts create mode 100644 dist/NetworkTest/errors/types.d.ts create mode 100644 dist/NetworkTest/index.d.ts create mode 100644 dist/NetworkTest/index.js create mode 100644 dist/NetworkTest/index.js.map create mode 100644 dist/NetworkTest/testConnectivity/errors/index.d.ts create mode 100644 dist/NetworkTest/testConnectivity/errors/mapping.d.ts create mode 100644 dist/NetworkTest/testConnectivity/index.d.ts create mode 100644 dist/NetworkTest/testQuality/errors/index.d.ts create mode 100644 dist/NetworkTest/testQuality/helpers/MOSState.d.ts create mode 100644 dist/NetworkTest/testQuality/helpers/calculateQualityStats.d.ts create mode 100644 dist/NetworkTest/testQuality/helpers/calculateThroughput.d.ts create mode 100644 dist/NetworkTest/testQuality/helpers/config.d.ts create mode 100644 dist/NetworkTest/testQuality/helpers/getLatestSampleWindow.d.ts create mode 100644 dist/NetworkTest/testQuality/helpers/getQualityEvaluation.d.ts create mode 100644 dist/NetworkTest/testQuality/helpers/isBitrateSteadyState.d.ts create mode 100644 dist/NetworkTest/testQuality/helpers/isSupportedBrowser.d.ts create mode 100644 dist/NetworkTest/testQuality/helpers/subscriberMOS.d.ts create mode 100644 dist/NetworkTest/testQuality/index.d.ts create mode 100644 dist/NetworkTest/testQuality/types/stats.d.ts create mode 100644 dist/NetworkTest/types/callbacks.d.ts create mode 100644 dist/NetworkTest/types/opentok/connection.d.ts create mode 100644 dist/NetworkTest/types/opentok/error.d.ts create mode 100644 dist/NetworkTest/types/opentok/events.d.ts create mode 100644 dist/NetworkTest/types/opentok/index.d.ts create mode 100644 dist/NetworkTest/types/opentok/publisher.d.ts create mode 100644 dist/NetworkTest/types/opentok/session.d.ts create mode 100644 dist/NetworkTest/types/opentok/stream.d.ts create mode 100644 dist/NetworkTest/types/opentok/subscriber.d.ts create mode 100644 dist/NetworkTest/types/opentok/widget.d.ts create mode 100644 dist/NetworkTest/util/index.d.ts diff --git a/.gitignore b/.gitignore index eb0bd8ab..7a1f5ab9 100644 --- a/.gitignore +++ b/.gitignore @@ -58,7 +58,7 @@ typings/ .env # build files -dist/ +# dist/ # env .env @@ -72,4 +72,4 @@ sample/node_modules /test/credentials.json # We're using npm (at least for now) -yarn.lock \ No newline at end of file +yarn.lock diff --git a/dist/NetworkTest/errors/index.d.ts b/dist/NetworkTest/errors/index.d.ts new file mode 100644 index 00000000..4ef2b5d7 --- /dev/null +++ b/dist/NetworkTest/errors/index.d.ts @@ -0,0 +1,22 @@ +/** + * @module Errors + */ +/** + * Base class for errors used throughout Network Connectivity tests. + */ +export declare class NetworkTestError extends Error { + name: string; + constructor(message: string, name?: string); +} +export declare class MissingOpenTokInstanceError extends NetworkTestError { + constructor(); +} +export declare class IncompleteSessionCredentialsError extends NetworkTestError { + constructor(); +} +export declare class MissingSessionCredentialsError extends NetworkTestError { + constructor(); +} +export declare class InvalidOnUpdateCallback extends NetworkTestError { + constructor(); +} diff --git a/dist/NetworkTest/errors/types.d.ts b/dist/NetworkTest/errors/types.d.ts new file mode 100644 index 00000000..1b74f124 --- /dev/null +++ b/dist/NetworkTest/errors/types.d.ts @@ -0,0 +1,68 @@ +/** + * @module Errors/Connectivity/OpenTok + */ +import { OTError } from '../types/opentok/error'; +export declare enum ErrorNames { + NETWORK_TEST_ERROR = "NetworkTestError", + MISSING_OPENTOK_INSTANCE = "MissingOpenTokInstanceError", + INCOMPLETE_SESSON_CREDENTIALS = "IncompleteSessionCredentialsError", + MISSING_SESSON_CREDENTIALS = "MissingSessionCredentialsError", + INVALID_ON_UPDATE_CALLBACK = "InvalidOnUpdateCallback", + CONNECTIVITY_ERROR = "ConnectivityError", + API_CONNECTIVITY_ERROR = "APIConnectivityError", + CONNECT_TO_SESSION_ERROR = "ConnectToSessionError", + CONNECT_TO_SESSION_TOKEN_ERROR = "ConnectToSessionTokenError", + CONNECT_TO_SESSION_ID_ERROR = "ConnectToSessionSessionIdError", + CONNECT_TO_SESSION_NETWORK_ERROR = "ConnectToSessionNetworkError", + MEDIA_DEVICE_ERROR = "MediaDeviceError", + FAILED_TO_OBTAIN_MEDIA_DEVICES = "FailedToObtainMediaDevices", + NO_VIDEO_CAPTURE_DEVICES = "NoVideoCaptureDevicesError", + NO_AUDIO_CAPTURE_DEVICES = "NoAudioCaptureDevicesError", + PUBLISH_TO_SESSION_ERROR = "PublishToSessionError", + INIT_PUBLISHER_ERROR = "InitPublisherError", + FAILED_MESSAGING_SERVER_TEST = "FailedMessagingServerTestError", + FAILED_TO_CREATE_LOCAL_PUBLISHER = "FailedToCreateLocalPublisher", + PUBLISH_TO_SESSION_NOT_CONNECTED = "PublishToSessionNotConnectedError", + PUBLISH_TO_SESSION_PERMISSION_OR_TIMEOUT_ERROR = "PublishToSessionPermissionOrTimeoutError", + PUBLISH_TO_SESSION_NETWORK_ERROR = "PublishToSessionNetworkError", + SUBSCRIBE_TO_SESSION_ERROR = "SubscribeToSessionError", + LOGGING_SERVER_CONNECTION_ERROR = "LoggingServerConnectionError", + QUALITY_TEST_ERROR = "QualityTestError", + UNSUPPORTED_BROWSER = "UnsupportedBrowser", + SUBSCRIBER_GET_STATS_ERROR = "SubscriberGetStatsError", + MISSING_SUBSCRIBER_ERROR = "MissingSubscriberError" +} +export declare enum OTErrorType { + JS_EXCEPTION = "JS_EXCEPTION", + OT_AUTHENTICATION_ERROR = "OT_AUTHENTICATION_ERROR", + OT_INVALID_HTTP_STATUS = "OT_INVALID_HTTP_STATUS", + OT_CONNECT_FAILED = "OT_CONNECT_FAILED", + OT_INVALID_SESSION_ID = "OT_INVALID_SESSION_ID", + CONNECT_FAILED = "CONNECT_FAILED", + CONNECT_REJECTED = "CONNECT_REJECTED", + CONNECTION_TIMEOUT = "CONNECTION_TIMEOUT", + NOT_CONNECTED = "NOT_CONNECTED", + INVALID_PARAMETER = "INVALID_PARAMETER", + P2P_CONNECTION_FAILED = "P2P_CONNECTION_FAILED", + API_RESPONSE_FAILURE = "API_RESPONSE_FAILURE", + TERMS_OF_SERVICE_FAILURE = "TERMS_OF_SERVICE_FAILURE", + CONNECTION_LIMIT_EXCEEDED = "CONNECTION_LIMIT_EXCEEDED", + UNABLE_TO_PUBLISH = "UNABLE_TO_PUBLISH", + UNABLE_TO_SUBSCRIBE = "UNABLE_TO_SUBSCRIBE", + UNSUPPORTED_VIDEO_CODEC = "UNSUPPORTED_VIDEO_CODEC", + UNABLE_TO_FORCE_DISCONNECT = "UNABLE_TO_FORCE_DISCONNECT", + UNABLE_TO_FORCE_UNPUBLISH = "UNABLE_TO_FORCE_UNPUBLISH", + PUBLISHER_ICE_WORKFLOW_FAILED = "PUBLISHER_ICE_WORKFLOW_FAILED", + SUBSCRIBER_ICE_WORKFLOW_FAILED = "SUBSCRIBER_ICE_WORKFLOW_FAILED", + STREAM_LIMIT_EXCEEDED = "STREAM_LIMIT_EXCEEDED", + UNEXPECTED_SERVER_RESPONSE = "UNEXPECTED_SERVER_RESPONSE", + REPORT_ISSUE_ERROR = "REPORT_ISSUE_ERROR", + ANVIL_BADLY_FORMED_RESPONSE = "ANVIL_BADLY_FORMED_RESPONSE", + ANVIL_INVALID_HTTP_STATUS = "ANVIL_INVALID_HTTP_STATUS", + ANVIL_XDOMAIN_OR_PARSING_ERROR = "ANVIL_XDOMAIN_OR_PARSING_ERROR", + ANVIL_UNKNOWN_HTTP_ERROR = "ANVIL_UNKNOWN_HTTP_ERROR", + ANVIL_UNEXPECTED_ERROR_CODE = "ANVIL_UNEXPECTED_ERROR_CODE", + ANVIL_EMPTY_RESPONSE_BODY = "ANVIL_EMPTY_RESPONSE_BODY", + ANVIL_CONNECT_FAILED = "ANVIL_CONNECT_FAILED" +} +export declare const errorHasName: (error: OTError | null | undefined, name: OTErrorType) => boolean; diff --git a/dist/NetworkTest/index.d.ts b/dist/NetworkTest/index.d.ts new file mode 100644 index 00000000..4a725b02 --- /dev/null +++ b/dist/NetworkTest/index.d.ts @@ -0,0 +1,58 @@ +/** + * @module NetworkTest + */ +import { OT } from './types/opentok'; +import { UpdateCallback, UpdateCallbackStats } from './types/callbacks'; +import { ConnectivityTestResults } from './testConnectivity'; +import { QualityTestResults } from './testQuality'; +import OTKAnalytics = require('opentok-solutions-logging'); +export interface NetworkTestOptions { + audioOnly?: boolean; + timeout?: number; + audioSource?: string; + videoSource?: string; + initSessionOptions?: OT.InitSessionOptions; + proxyServerUrl?: string; + skipPublisherCleaningOnSuccess?: boolean; +} +export default class NetworkTest { + credentials: OT.SessionCredentials; + OT: OT.Client; + otLogging: OTKAnalytics; + options?: NetworkTestOptions; + /** + * Returns an instance of NetworkConnectivity. See the "API reference" section of the + * README.md file in the root of the opentok-network-test-js project for details. + */ + constructor(OT: OT.Client, credentials: OT.SessionCredentials, options?: NetworkTestOptions); + private validateOT; + private validateCredentials; + private validateProxyUrl; + private setProxyUrl; + private startLoggingEngine; + /** + * This method checks to see if the client can connect to TokBox servers required for + * using OpenTok. + * + * See the "API reference" section of the README.md file in the root of the + * opentok-network-test-js project for details. + */ + testConnectivity(): Promise; + /** + * This function runs a test publisher and based on the measured video bitrate, + * audio bitrate, and the audio packet loss for the published stream, it returns + * results indicating the recommended supported publisher settings. + * + * See the "API reference" section of the README.md file in the root of the + * opentok-network-test-js project for details. + */ + testQuality(updateCallback?: UpdateCallback): Promise; + /** + * Stops the currently running test. + * + * See the "API reference" section of the README.md file in the root of the + * opentok-network-test-js project for details. + */ + stop(): void; +} +export { ErrorNames } from './errors/types'; diff --git a/dist/NetworkTest/index.js b/dist/NetworkTest/index.js new file mode 100644 index 00000000..05776135 --- /dev/null +++ b/dist/NetworkTest/index.js @@ -0,0 +1,2 @@ +!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define("OpenTokNetworkConnectivity",[],t):"object"==typeof exports?exports.OpenTokNetworkConnectivity=t():e.OpenTokNetworkConnectivity=t()}(window,(function(){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=20)}([function(e,t,r){"use strict";var n=r(7),o=Object.prototype.toString;function i(e){return"[object Array]"===o.call(e)}function s(e){return void 0===e}function a(e){return null!==e&&"object"==typeof e}function u(e){return"[object Function]"===o.call(e)}function c(e,t){if(null!=e)if("object"!=typeof e&&(e=[e]),i(e))for(var r=0,n=e.length;r2&&void 0!==arguments[2]&&arguments[2],n=function(e,n){return void 0!==t[n]||r?Object.assign({},e,o({},n,t[n])):e};return e.reduce(n,{})},t.pickAll=function(e,r){return t.pick(e,r,!0)},t.last=function(e){return e[e.length-1]},t.nth=function(e,t){return e<0?t[t.length+e]:t[e]},t.head=function(e){return t.nth(0,e)}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=r(1);!function(e){e.NETWORK_TEST_ERROR="NetworkTestError",e.MISSING_OPENTOK_INSTANCE="MissingOpenTokInstanceError",e.INCOMPLETE_SESSON_CREDENTIALS="IncompleteSessionCredentialsError",e.MISSING_SESSON_CREDENTIALS="MissingSessionCredentialsError",e.INVALID_ON_UPDATE_CALLBACK="InvalidOnUpdateCallback",e.CONNECTIVITY_ERROR="ConnectivityError",e.API_CONNECTIVITY_ERROR="APIConnectivityError",e.CONNECT_TO_SESSION_ERROR="ConnectToSessionError",e.CONNECT_TO_SESSION_TOKEN_ERROR="ConnectToSessionTokenError",e.CONNECT_TO_SESSION_ID_ERROR="ConnectToSessionSessionIdError",e.CONNECT_TO_SESSION_NETWORK_ERROR="ConnectToSessionNetworkError",e.MEDIA_DEVICE_ERROR="MediaDeviceError",e.FAILED_TO_OBTAIN_MEDIA_DEVICES="FailedToObtainMediaDevices",e.NO_VIDEO_CAPTURE_DEVICES="NoVideoCaptureDevicesError",e.NO_AUDIO_CAPTURE_DEVICES="NoAudioCaptureDevicesError",e.PUBLISH_TO_SESSION_ERROR="PublishToSessionError",e.INIT_PUBLISHER_ERROR="InitPublisherError",e.FAILED_MESSAGING_SERVER_TEST="FailedMessagingServerTestError",e.FAILED_TO_CREATE_LOCAL_PUBLISHER="FailedToCreateLocalPublisher",e.PUBLISH_TO_SESSION_NOT_CONNECTED="PublishToSessionNotConnectedError",e.PUBLISH_TO_SESSION_PERMISSION_OR_TIMEOUT_ERROR="PublishToSessionPermissionOrTimeoutError",e.PUBLISH_TO_SESSION_NETWORK_ERROR="PublishToSessionNetworkError",e.SUBSCRIBE_TO_SESSION_ERROR="SubscribeToSessionError",e.LOGGING_SERVER_CONNECTION_ERROR="LoggingServerConnectionError",e.QUALITY_TEST_ERROR="QualityTestError",e.UNSUPPORTED_BROWSER="UnsupportedBrowser",e.SUBSCRIBER_GET_STATS_ERROR="SubscriberGetStatsError",e.MISSING_SUBSCRIBER_ERROR="MissingSubscriberError"}(t.ErrorNames||(t.ErrorNames={})),function(e){e.JS_EXCEPTION="JS_EXCEPTION",e.OT_AUTHENTICATION_ERROR="OT_AUTHENTICATION_ERROR",e.OT_INVALID_HTTP_STATUS="OT_INVALID_HTTP_STATUS",e.OT_CONNECT_FAILED="OT_CONNECT_FAILED",e.OT_INVALID_SESSION_ID="OT_INVALID_SESSION_ID",e.CONNECT_FAILED="CONNECT_FAILED",e.CONNECT_REJECTED="CONNECT_REJECTED",e.CONNECTION_TIMEOUT="CONNECTION_TIMEOUT",e.NOT_CONNECTED="NOT_CONNECTED",e.INVALID_PARAMETER="INVALID_PARAMETER",e.P2P_CONNECTION_FAILED="P2P_CONNECTION_FAILED",e.API_RESPONSE_FAILURE="API_RESPONSE_FAILURE",e.TERMS_OF_SERVICE_FAILURE="TERMS_OF_SERVICE_FAILURE",e.CONNECTION_LIMIT_EXCEEDED="CONNECTION_LIMIT_EXCEEDED",e.UNABLE_TO_PUBLISH="UNABLE_TO_PUBLISH",e.UNABLE_TO_SUBSCRIBE="UNABLE_TO_SUBSCRIBE",e.UNSUPPORTED_VIDEO_CODEC="UNSUPPORTED_VIDEO_CODEC",e.UNABLE_TO_FORCE_DISCONNECT="UNABLE_TO_FORCE_DISCONNECT",e.UNABLE_TO_FORCE_UNPUBLISH="UNABLE_TO_FORCE_UNPUBLISH",e.PUBLISHER_ICE_WORKFLOW_FAILED="PUBLISHER_ICE_WORKFLOW_FAILED",e.SUBSCRIBER_ICE_WORKFLOW_FAILED="SUBSCRIBER_ICE_WORKFLOW_FAILED",e.STREAM_LIMIT_EXCEEDED="STREAM_LIMIT_EXCEEDED",e.UNEXPECTED_SERVER_RESPONSE="UNEXPECTED_SERVER_RESPONSE",e.REPORT_ISSUE_ERROR="REPORT_ISSUE_ERROR",e.ANVIL_BADLY_FORMED_RESPONSE="ANVIL_BADLY_FORMED_RESPONSE",e.ANVIL_INVALID_HTTP_STATUS="ANVIL_INVALID_HTTP_STATUS",e.ANVIL_XDOMAIN_OR_PARSING_ERROR="ANVIL_XDOMAIN_OR_PARSING_ERROR",e.ANVIL_UNKNOWN_HTTP_ERROR="ANVIL_UNKNOWN_HTTP_ERROR",e.ANVIL_UNEXPECTED_ERROR_CODE="ANVIL_UNEXPECTED_ERROR_CODE",e.ANVIL_EMPTY_RESPONSE_BODY="ANVIL_EMPTY_RESPONSE_BODY",e.ANVIL_CONNECT_FAILED="ANVIL_CONNECT_FAILED"}(t.OTErrorType||(t.OTErrorType={})),t.errorHasName=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:null,t=arguments[1];return n.get("name",e)===t}},function(e,t,r){"use strict";var n=r(16);function o(){}var i=null,s={};function a(e){if("object"!=typeof this)throw new TypeError("Promises must be constructed via new");if("function"!=typeof e)throw new TypeError("Promise constructor's argument is not a function");this._U=0,this._V=0,this._W=null,this._X=null,e!==o&&p(e,this)}function u(e,t){for(;3===e._V;)e=e._W;if(a._Y&&a._Y(e),0===e._V)return 0===e._U?(e._U=1,void(e._X=t)):1===e._U?(e._U=2,void(e._X=[e._X,t])):void e._X.push(t);!function(e,t){n((function(){var r=1===e._V?t.onFulfilled:t.onRejected;if(null!==r){var n=function(e,t){try{return e(t)}catch(e){return i=e,s}}(r,e._W);n===s?f(t.promise,i):c(t.promise,n)}else 1===e._V?c(t.promise,e._W):f(t.promise,e._W)}))}(e,t)}function c(e,t){if(t===e)return f(e,new TypeError("A promise cannot be resolved with itself."));if(t&&("object"==typeof t||"function"==typeof t)){var r=function(e){try{return e.then}catch(e){return i=e,s}}(t);if(r===s)return f(e,i);if(r===e.then&&t instanceof a)return e._V=3,e._W=t,void l(e);if("function"==typeof r)return void p(r.bind(t),e)}e._V=1,e._W=t,l(e)}function f(e,t){e._V=2,e._W=t,a._Z&&a._Z(e,t),l(e)}function l(e){if(1===e._U&&(u(e,e._X),e._X=null),2===e._U){for(var t=0;t=200&&e<300}};u.headers={common:{Accept:"application/json, text/plain, */*"}},n.forEach(["delete","get","head"],(function(e){u.headers[e]={}})),n.forEach(["post","put","patch"],(function(e){u.headers[e]=n.merge(i)})),e.exports=u}).call(this,r(28))},function(e,t,r){"use strict";var n=r(0),o=r(30),i=r(8),s=r(32),a=r(35),u=r(36),c=r(12);e.exports=function(e){return new Promise((function(t,f){var l=e.data,d=e.headers;n.isFormData(l)&&delete d["Content-Type"];var p=new XMLHttpRequest;if(e.auth){var h=e.auth.username||"",E=e.auth.password||"";d.Authorization="Basic "+btoa(h+":"+E)}var _=s(e.baseURL,e.url);if(p.open(e.method.toUpperCase(),i(_,e.params,e.paramsSerializer),!0),p.timeout=e.timeout,p.onreadystatechange=function(){if(p&&4===p.readyState&&(0!==p.status||p.responseURL&&0===p.responseURL.indexOf("file:"))){var r="getAllResponseHeaders"in p?a(p.getAllResponseHeaders()):null,n={data:e.responseType&&"text"!==e.responseType?p.response:p.responseText,status:p.status,statusText:p.statusText,headers:r,config:e,request:p};o(t,f,n),p=null}},p.onabort=function(){p&&(f(c("Request aborted",e,"ECONNABORTED",p)),p=null)},p.onerror=function(){f(c("Network Error",e,null,p)),p=null},p.ontimeout=function(){var t="timeout of "+e.timeout+"ms exceeded";e.timeoutErrorMessage&&(t=e.timeoutErrorMessage),f(c(t,e,"ECONNABORTED",p)),p=null},n.isStandardBrowserEnv()){var O=r(37),v=(e.withCredentials||u(_))&&e.xsrfCookieName?O.read(e.xsrfCookieName):void 0;v&&(d[e.xsrfHeaderName]=v)}if("setRequestHeader"in p&&n.forEach(d,(function(e,t){void 0===l&&"content-type"===t.toLowerCase()?delete d[t]:p.setRequestHeader(t,e)})),n.isUndefined(e.withCredentials)||(p.withCredentials=!!e.withCredentials),e.responseType)try{p.responseType=e.responseType}catch(t){if("json"!==e.responseType)throw t}"function"==typeof e.onDownloadProgress&&p.addEventListener("progress",e.onDownloadProgress),"function"==typeof e.onUploadProgress&&p.upload&&p.upload.addEventListener("progress",e.onUploadProgress),e.cancelToken&&e.cancelToken.promise.then((function(e){p&&(p.abort(),f(e),p=null)})),void 0===l&&(l=null),p.send(l)}))}},function(e,t,r){"use strict";var n=r(31);e.exports=function(e,t,r,o,i){var s=new Error(e);return n(s,t,r,o,i)}},function(e,t,r){"use strict";var n=r(0);e.exports=function(e,t){t=t||{};var r={},o=["url","method","params","data"],i=["headers","auth","proxy"],s=["baseURL","url","transformRequest","transformResponse","paramsSerializer","timeout","withCredentials","adapter","responseType","xsrfCookieName","xsrfHeaderName","onUploadProgress","onDownloadProgress","maxContentLength","validateStatus","maxRedirects","httpAgent","httpsAgent","cancelToken","socketPath"];n.forEach(o,(function(e){void 0!==t[e]&&(r[e]=t[e])})),n.forEach(i,(function(o){n.isObject(t[o])?r[o]=n.deepMerge(e[o],t[o]):void 0!==t[o]?r[o]=t[o]:n.isObject(e[o])?r[o]=n.deepMerge(e[o]):void 0!==e[o]&&(r[o]=e[o])})),n.forEach(s,(function(n){void 0!==t[n]?r[n]=t[n]:void 0!==e[n]&&(r[n]=e[n])}));var a=o.concat(i).concat(s),u=Object.keys(t).filter((function(e){return-1===a.indexOf(e)}));return n.forEach(u,(function(n){void 0!==t[n]?r[n]=t[n]:void 0!==e[n]&&(r[n]=e[n])})),r}},function(e,t,r){"use strict";function n(e){this.message=e}n.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},n.prototype.__CANCEL__=!0,e.exports=n},function(e,t,r){"use strict";e.exports=r(40)},function(e,t,r){"use strict";(function(t){function r(e){o.length||(n(),!0),o[o.length]=e}e.exports=r;var n,o=[],i=0;function s(){for(;i1024){for(var t=0,r=o.length-i;t=t}))}},function(e,t,r){"use strict";function n(e,t){for(var r=[],n=1;ne.maxLogLength;)t.shift();this.audioScoresLog=t}},{key:"pruneVideoScores",value:function(){for(var t=this.videoScoresLog;t.length>e.maxLogLength;)t.shift();this.videoScoresLog=t}},{key:"pruneScores",value:function(){this.pruneAudioScores(),this.pruneVideoScores()}},{key:"audioQualityScore",value:function(){return this.hasAudioTrack()?this.audioScore():1}},{key:"videoQualityScore",value:function(){return this.hasVideoTrack()?this.videoScore():1}}]),e}();o.maxLogLength=1e3,o.scoreInterval=1e3,t.default=o},function(e,t,r){"use strict";var n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},o=function(){function e(e,t){for(var r=0;r1&&void 0!==arguments[1]?arguments[1]:[];if(e)r(new a.FailedToObtainMediaDevices);else{var i=n.reduce((function(e,t){var r="audioInput"===t.kind?"audio":"video";return Object.assign({},e,o({},r,Object.assign({},e[r],o({},t.deviceId,t))))}),{audio:{},video:{}});Object.keys(i.audio).length||Object.keys(i.video).length?t(i):r(new a.FailedToObtainMediaDevices)}}))}))})(e).then((function(o){var i=document.createElement("div");i.style.position="fixed",i.style.bottom="-1px",i.style.width="1px",i.style.height="1px",i.style.opacity="0.01",document.body.appendChild(i);var s={width:"100%",height:"100%",insertMode:"append",showControls:!1};t&&t.audioSource&&(s.audioSource=t.audioSource),t&&t.videoSource&&(s.videoSource=t.videoSource),t&&t.audioOnly&&(s.videoSource=null),Object.keys(o.audio).length||(s.audioSource=null),Object.keys(o.video).length||(s.videoSource=null);var u=e.initPublisher(i,s,(function(e){e?n(new a.FailedToCreateLocalPublisher):r({publisher:u})}));u.on("streamCreated",(function(){i.style.visibility="hidden"}))})).catch(n)}))}function h(e){var t=e.session,r=e.publisher;return new s((function(e,n){var o=function(e){d(r).then((function(){return l(t)})).then((function(){n(e)}))};if(r.stream)var i=document.createElement("div"),s=t.subscribe(r.stream,i,{testNetwork:!0,audioVolume:0},(function(n){n?o(new a.SubscribeToSessionError):e(Object.assign({session:t},{publisher:r},{subscriber:s}))}));else o(new a.SubscribeToSessionError)}))}function E(e,t,r){return new s((function(n,o){var s=f.getOr("","properties.loggingURL",e)+"/logging/ClientEvent",u=t&&t.proxyServerUrl&&t.proxyServerUrl+"/"+s.replace("https://","")||s,c=function(){return o(new a.LoggingServerConnectionError)};i.default.post(u).then((function(e){return 200===e.status?n(r):c()})).catch(c)}))}t.testConnectivity=function(e,t,r,o){return new s((function(i,f){(function(e,t,r){var n=t.apiKey,o=t.sessionId,i=t.token;return new s((function(t,s){var c={};r&&r.initSessionOptions&&(c=r.initSessionOptions),r&&r.proxyServerUrl&&(e.hasOwnProperty("setProxyUrl")||(c.proxyUrl=r.proxyServerUrl));var f=e.initSession(n,o,c);f.connect(i,(function(e){u.errorHasName(e,u.OTErrorType.OT_AUTHENTICATION_ERROR)?s(new a.ConnectToSessionTokenError):u.errorHasName(e,u.OTErrorType.OT_INVALID_SESSION_ID)?s(new a.ConnectToSessionSessionIdError):u.errorHasName(e,u.OTErrorType.OT_CONNECT_FAILED)?s(new a.ConnectToSessionNetworkError):u.errorHasName(e,u.OTErrorType.OT_INVALID_HTTP_STATUS)?s(new a.APIConnectivityError):e?s(new a.ConnectToSessionError):t(f)}))}))})(e,t,o).then((function(t){return function(e,t,r){return new s((function(n,o){var i=function(e){l(t).then((function(){o(e)}))};p(e,r).then((function(e){var r=e.publisher;t.publish(r,(function(e){e?u.errorHasName(e,u.OTErrorType.NOT_CONNECTED)?i(new a.PublishToSessionNotConnectedError):u.errorHasName(e,u.OTErrorType.UNABLE_TO_PUBLISH)?i(new a.PublishToSessionPermissionOrTimeoutError):e&&i(new a.PublishToSessionError):n(Object.assign({session:t},{publisher:r}))}))})).catch((function(e){i(e)}))}))}(e,t,o)})).then(h).then((function(t){return E(e,o,t)})).then((function(e){var t,n,a={success:!0,failedTests:[]};return r.logEvent({action:"testConnectivity",variation:"Success"}),(t=e.session,n=e.subscriber,new s((function(e,r){n.on("destroyed",(function(){e()})),n||e(),t.unsubscribe(n)}))).then((function(){return o&&o.skipPublisherCleaningOnSuccess?s.resolve():d(e.publisher)})).then((function(){return l(e.session)})).then((function(){return i(a)}))})).catch((function(t){var s=function(){var e=c.mapErrors.apply(c,arguments),t=e.find((function(e){return"messaging"===e.type})),o=[].concat(n(e),n(t?c.mapErrors(new a.FailedMessagingServerTestError):[])),s={failedTests:o,success:!1};r.logEvent({action:"testConnectivity",variation:"Success"}),i(s)};"LoggingServerConnectionError"===t.name?s(t):E(e,o).then((function(){return s(t)})).catch((function(e){return s(t,e)}))}))}))}},function(e,t,r){"use strict";var n=r(0),o=r(7),i=r(24),s=r(13);function a(e){var t=new i(e),r=o(i.prototype.request,t);return n.extend(r,i.prototype,t),n.extend(r,t),r}var u=a(r(10));u.Axios=i,u.create=function(e){return a(s(u.defaults,e))},u.Cancel=r(14),u.CancelToken=r(38),u.isCancel=r(9),u.all=function(e){return Promise.all(e)},u.spread=r(39),e.exports=u,e.exports.default=u},function(e,t,r){"use strict";var n=r(0),o=r(8),i=r(25),s=r(26),a=r(13);function u(e){this.defaults=e,this.interceptors={request:new i,response:new i}}u.prototype.request=function(e){"string"==typeof e?(e=arguments[1]||{}).url=arguments[0]:e=e||{},(e=a(this.defaults,e)).method?e.method=e.method.toLowerCase():this.defaults.method?e.method=this.defaults.method.toLowerCase():e.method="get";var t=[s,void 0],r=Promise.resolve(e);for(this.interceptors.request.forEach((function(e){t.unshift(e.fulfilled,e.rejected)})),this.interceptors.response.forEach((function(e){t.push(e.fulfilled,e.rejected)}));t.length;)r=r.then(t.shift(),t.shift());return r},u.prototype.getUri=function(e){return e=a(this.defaults,e),o(e.url,e.params,e.paramsSerializer).replace(/^\?/,"")},n.forEach(["delete","get","head","options"],(function(e){u.prototype[e]=function(t,r){return this.request(n.merge(r||{},{method:e,url:t}))}})),n.forEach(["post","put","patch"],(function(e){u.prototype[e]=function(t,r,o){return this.request(n.merge(o||{},{method:e,url:t,data:r}))}})),e.exports=u},function(e,t,r){"use strict";var n=r(0);function o(){this.handlers=[]}o.prototype.use=function(e,t){return this.handlers.push({fulfilled:e,rejected:t}),this.handlers.length-1},o.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},o.prototype.forEach=function(e){n.forEach(this.handlers,(function(t){null!==t&&e(t)}))},e.exports=o},function(e,t,r){"use strict";var n=r(0),o=r(27),i=r(9),s=r(10);function a(e){e.cancelToken&&e.cancelToken.throwIfRequested()}e.exports=function(e){return a(e),e.headers=e.headers||{},e.data=o(e.data,e.headers,e.transformRequest),e.headers=n.merge(e.headers.common||{},e.headers[e.method]||{},e.headers),n.forEach(["delete","get","head","post","put","patch","common"],(function(t){delete e.headers[t]})),(e.adapter||s.adapter)(e).then((function(t){return a(e),t.data=o(t.data,t.headers,e.transformResponse),t}),(function(t){return i(t)||(a(e),t&&t.response&&(t.response.data=o(t.response.data,t.response.headers,e.transformResponse))),Promise.reject(t)}))}},function(e,t,r){"use strict";var n=r(0);e.exports=function(e,t,r){return n.forEach(r,(function(r){e=r(e,t)})),e}},function(e,t){var r,n,o=e.exports={};function i(){throw new Error("setTimeout has not been defined")}function s(){throw new Error("clearTimeout has not been defined")}function a(e){if(r===setTimeout)return setTimeout(e,0);if((r===i||!r)&&setTimeout)return r=setTimeout,setTimeout(e,0);try{return r(e,0)}catch(t){try{return r.call(null,e,0)}catch(t){return r.call(this,e,0)}}}!function(){try{r="function"==typeof setTimeout?setTimeout:i}catch(e){r=i}try{n="function"==typeof clearTimeout?clearTimeout:s}catch(e){n=s}}();var u,c=[],f=!1,l=-1;function d(){f&&u&&(f=!1,u.length?c=u.concat(c):l=-1,c.length&&p())}function p(){if(!f){var e=a(d);f=!0;for(var t=c.length;t;){for(u=c,c=[];++l1)for(var r=1;r=0)return;s[t]="set-cookie"===t?(s[t]?s[t]:[]).concat([r]):s[t]?s[t]+", "+r:r}})),s):s}},function(e,t,r){"use strict";var n=r(0);e.exports=n.isStandardBrowserEnv()?function(){var e,t=/(msie|trident)/i.test(navigator.userAgent),r=document.createElement("a");function o(e){var n=e;return t&&(r.setAttribute("href",n),n=r.href),r.setAttribute("href",n),{href:r.href,protocol:r.protocol?r.protocol.replace(/:$/,""):"",host:r.host,search:r.search?r.search.replace(/^\?/,""):"",hash:r.hash?r.hash.replace(/^#/,""):"",hostname:r.hostname,port:r.port,pathname:"/"===r.pathname.charAt(0)?r.pathname:"/"+r.pathname}}return e=o(window.location.href),function(t){var r=n.isString(t)?o(t):t;return r.protocol===e.protocol&&r.host===e.host}}():function(){return!0}},function(e,t,r){"use strict";var n=r(0);e.exports=n.isStandardBrowserEnv()?{write:function(e,t,r,o,i,s){var a=[];a.push(e+"="+encodeURIComponent(t)),n.isNumber(r)&&a.push("expires="+new Date(r).toGMTString()),n.isString(o)&&a.push("path="+o),n.isString(i)&&a.push("domain="+i),!0===s&&a.push("secure"),document.cookie=a.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}:{write:function(){},read:function(){return null},remove:function(){}}},function(e,t,r){"use strict";var n=r(14);function o(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise((function(e){t=e}));var r=this;e((function(e){r.reason||(r.reason=new n(e),t(r.reason))}))}o.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},o.source=function(){var e;return{token:new o((function(t){e=t})),cancel:e}},e.exports=o},function(e,t,r){"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}},function(e,t,r){"use strict";e.exports=r(3),r(42),r(43),r(44),r(45),r(47)},function(e,t){var r;r=function(){return this}();try{r=r||new Function("return this")()}catch(e){"object"==typeof window&&(r=window)}e.exports=r},function(e,t,r){"use strict";var n=r(3);e.exports=n,n.prototype.done=function(e,t){var r=arguments.length?this.then.apply(this,arguments):this;r.then(null,(function(e){setTimeout((function(){throw e}),0)}))}},function(e,t,r){"use strict";var n=r(3);e.exports=n,n.prototype.finally=function(e){return this.then((function(t){return n.resolve(e()).then((function(){return t}))}),(function(t){return n.resolve(e()).then((function(){throw t}))}))}},function(e,t,r){"use strict";var n=r(3);e.exports=n;var o=f(!0),i=f(!1),s=f(null),a=f(void 0),u=f(0),c=f("");function f(e){var t=new n(n._0);return t._V=1,t._W=e,t}n.resolve=function(e){if(e instanceof n)return e;if(null===e)return s;if(void 0===e)return a;if(!0===e)return o;if(!1===e)return i;if(0===e)return u;if(""===e)return c;if("object"==typeof e||"function"==typeof e)try{var t=e.then;if("function"==typeof t)return new n(t.bind(e))}catch(e){return new n((function(t,r){r(e)}))}return f(e)};var l=function(e){return"function"==typeof Array.from?(l=Array.from,Array.from(e)):(l=function(e){return Array.prototype.slice.call(e)},Array.prototype.slice.call(e))};n.all=function(e){var t=l(e);return new n((function(e,r){if(0===t.length)return e([]);var o=t.length;function i(s,a){if(a&&("object"==typeof a||"function"==typeof a)){if(a instanceof n&&a.then===n.prototype.then){for(;3===a._V;)a=a._W;return 1===a._V?i(s,a._W):(2===a._V&&r(a._W),void a.then((function(e){i(s,e)}),r))}var u=a.then;if("function"==typeof u)return void new n(u.bind(a)).then((function(e){i(s,e)}),r)}t[s]=a,0==--o&&e(t)}for(var s=0;s "+t+") {","args = new Array(arguments.length + 1);","for (var i = 0; i < arguments.length; i++) {","args[i] = arguments[i];","}","}","return new Promise(function (rs, rj) {","var cb = "+i+";","var res;","switch (argLength) {",r.concat(["extra"]).map((function(e,t){return"case "+t+":res = fn.call("+["self"].concat(r.slice(0,t)).concat("cb").join(",")+");break;"})).join(""),"default:","args[argLength] = cb;","res = fn.apply(self, args);","}","if (res &&",'(typeof res === "object" || typeof res === "function") &&','typeof res.then === "function"',") {rs(res);}","});","};"].join("");return Function(["Promise","fn"],s)(n,e)}(e)};var i="function (err, res) {if (err) { rj(err); } else { rs(res); }}";n.nodeify=function(e){return function(){var t=Array.prototype.slice.call(arguments),r="function"==typeof t[t.length-1]?t.pop():null,i=this;try{return e.apply(this,arguments).nodeify(r,i)}catch(e){if(null==r)return new n((function(t,r){r(e)}));o((function(){r.call(i,e)}))}}},n.prototype.nodeify=function(e,t){if("function"!=typeof e)return this;this.then((function(r){o((function(){e.call(t,null,r)}))}),(function(r){o((function(){e.call(t,r)}))}))}},function(e,t,r){"use strict";var n=r(16),o=[],i=[],s=n.makeRequestCallFromTimer((function(){if(i.length)throw i.shift()}));function a(e){var t;(t=o.length?o.pop():new u).task=e,n(t)}function u(){this.task=null}e.exports=a,u.prototype.call=function(){try{this.task.call()}catch(e){a.onerror?a.onerror(e):(i.push(e),s())}finally{this.task=null,o[o.length]=this}}},function(e,t,r){"use strict";var n=r(3);e.exports=n,n.enableSynchronous=function(){n.prototype.isPending=function(){return 0==this.getState()},n.prototype.isFulfilled=function(){return 1==this.getState()},n.prototype.isRejected=function(){return 2==this.getState()},n.prototype.getValue=function(){if(3===this._V)return this._W.getValue();if(!this.isFulfilled())throw new Error("Cannot get a value of an unfulfilled promise.");return this._W},n.prototype.getReason=function(){if(3===this._V)return this._W.getReason();if(!this.isRejected())throw new Error("Cannot get a rejection reason of a non-rejected promise.");return this._W},n.prototype.getState=function(){return 3===this._V?this._W.getState():-1===this._V||-2===this._V?0:this._V}},n.disableSynchronous=function(){n.prototype.isPending=void 0,n.prototype.isFulfilled=void 0,n.prototype.isRejected=void 0,n.prototype.getValue=void 0,n.prototype.getReason=void 0,n.prototype.getState=void 0}},function(e,t,r){"use strict";function n(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function o(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function i(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0});var s=r(5),a=r(2),u=function(e){function t(e,r){return n(this,t),o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,r||a.ErrorNames.CONNECTIVITY_ERROR))}return i(t,e),t}(s.NetworkTestError);t.ConnectivityError=u;var c=function(e){function t(){n(this,t);return o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,"Failed to connect to OpenTOK API Server",a.ErrorNames.API_CONNECTIVITY_ERROR))}return i(t,e),t}(u);t.APIConnectivityError=c;var f=function(e){function t(e,r){n(this,t);return o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e||"Failed to connect to the session due to a network error.",r||a.ErrorNames.CONNECT_TO_SESSION_ERROR))}return i(t,e),t}(u);t.ConnectToSessionError=f;var l=function(e){function t(){return n(this,t),o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,"Failed to connect to the session due to an invalid token.",a.ErrorNames.CONNECT_TO_SESSION_TOKEN_ERROR))}return i(t,e),t}(f);t.ConnectToSessionTokenError=l;var d=function(e){function t(){return n(this,t),o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,"Failed to connect to the session due to an invalid session ID.",a.ErrorNames.CONNECT_TO_SESSION_ID_ERROR))}return i(t,e),t}(f);t.ConnectToSessionSessionIdError=d;var p=function(e){function t(){return n(this,t),o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,"Failed to connect to the session due to a network error.",a.ErrorNames.CONNECT_TO_SESSION_NETWORK_ERROR))}return i(t,e),t}(f);t.ConnectToSessionNetworkError=p;var h=function(e){function t(e,r){n(this,t);return o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e||"OpenTok failed to find media devices for this browser.",r||a.ErrorNames.MEDIA_DEVICE_ERROR))}return i(t,e),t}(u);t.MediaDeviceError=h;var E=function(e){function t(){return n(this,t),o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,"Failed to obtain media devices.",a.ErrorNames.FAILED_TO_OBTAIN_MEDIA_DEVICES))}return i(t,e),t}(h);t.FailedToObtainMediaDevices=E;var _=function(e){function t(){return n(this,t),o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,"This browser has no video capture devices",a.ErrorNames.NO_VIDEO_CAPTURE_DEVICES))}return i(t,e),t}(h);t.NoVideoCaptureDevicesError=_;var O=function(e){function t(){return n(this,t),o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,"This browser has no audio capture devices.",a.ErrorNames.NO_AUDIO_CAPTURE_DEVICES))}return i(t,e),t}(h);t.NoAudioCaptureDevicesError=O;var v=function(e){function t(e,r){n(this,t);return o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e||"Encountered an unknown error while attempting to publish to a session.",r||a.ErrorNames.PUBLISH_TO_SESSION_ERROR))}return i(t,e),t}(u);t.PublishToSessionError=v;var S=function(e){function t(){n(this,t);return o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,"Failed to connect to media server due to messaging server connection failure",a.ErrorNames.FAILED_MESSAGING_SERVER_TEST))}return i(t,e),t}(v);t.FailedMessagingServerTestError=S;var m=function(e){function t(){return n(this,t),o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,"Failed to create a local publisher object.",a.ErrorNames.FAILED_TO_CREATE_LOCAL_PUBLISHER))}return i(t,e),t}(v);t.FailedToCreateLocalPublisher=m;var y=function(e){function t(){return n(this,t),o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,"Precall failed to publish to the session because it was not connected.",a.ErrorNames.PUBLISH_TO_SESSION_NOT_CONNECTED))}return i(t,e),t}(v);t.PublishToSessionNotConnectedError=y;var T=function(e){function t(){return n(this,t),o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,"Precall failed to publish to the session due a permissions error or timeout.",a.ErrorNames.PUBLISH_TO_SESSION_PERMISSION_OR_TIMEOUT_ERROR))}return i(t,e),t}(v);t.PublishToSessionPermissionOrTimeoutError=T;var g=function(e){function t(){return n(this,t),o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,"Precall failed to publish to the session due a network error.",a.ErrorNames.PUBLISH_TO_SESSION_NETWORK_ERROR))}return i(t,e),t}(v);t.PublishToSessionNetworkError=g;var b=function(e){function t(e){n(this,t);return o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e||"Encountered an unknown error while attempting to subscribe to a session.",a.ErrorNames.SUBSCRIBE_TO_SESSION_ERROR))}return i(t,e),t}(u);t.SubscribeToSessionError=b;var N=function(e){function t(){return n(this,t),o(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,"Failed to connect to the OpenTok logging server.",a.ErrorNames.LOGGING_SERVER_CONNECTION_ERROR))}return i(t,e),t}(u);t.LoggingServerConnectionError=N},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n,o=r(2);!function(e){e.Api="api",e.Messaging="messaging",e.OpentokJs="OpenTok.js",e.Media="media",e.Logging="logging",e.ConnectivityError="OpenTok.js"}(n=t.FailureType||(t.FailureType={}));var i=function(e){return{error:e,type:function(){switch(e.name){case o.ErrorNames.API_CONNECTIVITY_ERROR:case o.ErrorNames.CONNECT_TO_SESSION_NETWORK_ERROR:return n.Api;case o.ErrorNames.CONNECT_TO_SESSION_ERROR:case o.ErrorNames.CONNECT_TO_SESSION_TOKEN_ERROR:case o.ErrorNames.CONNECT_TO_SESSION_ID_ERROR:return n.Messaging;case o.ErrorNames.MEDIA_DEVICE_ERROR:case o.ErrorNames.FAILED_TO_OBTAIN_MEDIA_DEVICES:case o.ErrorNames.NO_VIDEO_CAPTURE_DEVICES:case o.ErrorNames.NO_AUDIO_CAPTURE_DEVICES:case o.ErrorNames.FAILED_TO_CREATE_LOCAL_PUBLISHER:case o.ErrorNames.PUBLISH_TO_SESSION_NOT_CONNECTED:case o.ErrorNames.PUBLISH_TO_SESSION_PERMISSION_OR_TIMEOUT_ERROR:case o.ErrorNames.PUBLISH_TO_SESSION_NETWORK_ERROR:return n.OpentokJs;case o.ErrorNames.PUBLISH_TO_SESSION_ERROR:case o.ErrorNames.SUBSCRIBE_TO_SESSION_ERROR:case o.ErrorNames.FAILED_MESSAGING_SERVER_TEST:return n.Media;case o.ErrorNames.LOGGING_SERVER_CONNECTION_ERROR:return n.Logging;default:return n.OpentokJs}}()}};t.mapErrors=function(){for(var e=arguments.length,t=Array(e),r=0;r1&&void 0!==arguments[1]?arguments[1]:[];if(e)r(new s.FailedToObtainMediaDevices);else{var i=o.reduce((function(e,t){var r="audioInput"===t.kind?"audio":"video";return Object.assign({},e,n({},r,Object.assign({},e[r],n({},t.deviceId,t))))}),{audio:{},video:{}});Object.keys(i.audio).length?t(i):r(new s.NoAudioCaptureDevicesError)}}))}))}(e).then((function(n){Object.keys(n.video).length||(d=!0);var o={resolution:"1280x720",width:"100%",height:"100%",insertMode:"append",showControls:!1};t&&t.audioSource&&(o.audioSource=t.audioSource),t&&t.videoSource&&(o.videoSource=t.videoSource),d&&(o.videoSource=null);var f=e.initPublisher(c,o,(function(e){e?u(new s.InitPublisherError(e.message)):r.publish(f,(function(e){if(e)return a.errorHasName(e,a.OTErrorType.NOT_CONNECTED)?u(new s.PublishToSessionNotConnectedError):a.errorHasName(e,a.OTErrorType.UNABLE_TO_PUBLISH)?u(new s.PublishToSessionPermissionOrTimeoutError):u(new s.PublishToSessionError)}))}));f.on("streamCreated",(function(e){var t=r.subscribe(e.stream,c,{testNetwork:!0,insertMode:"append"},(function(e){return e?u(new s.SubscribeToSessionError(e.message)):i({publisher:f,subscriber:t})}))}))})).catch(u)}))}}function S(e,t,r,n){return new o((function(i,u){(function(e,t){return new o((function(r,n){e.connection?r(e):e.connect(t,(function(t){t&&(a.errorHasName(t,a.OTErrorType.OT_AUTHENTICATION_ERROR)?n(new s.ConnectToSessionTokenError):a.errorHasName(t,a.OTErrorType.OT_INVALID_SESSION_ID)?n(new s.ConnectToSessionSessionIdError):a.errorHasName(t,a.OTErrorType.OT_CONNECT_FAILED)?n(new s.ConnectToSessionNetworkError):n(new s.ConnectToSessionError)),r(e)}))}))})(t,r.token).then(v(e,n)).then(i).catch(u)}))}function m(e,t,r,n,a,l){var v=void 0;return new o((function(y,T){S(e,t,r,n).then((function(S){var g=S.publisher,b=S.subscriber;if(b)try{var N=Object.assign({state:new c.default(l)},{subscriber:b},{credentials:r}),R=function(){var s,u=function(e){var t=["bitrate","packetLossRatio","supported","reason","mos"];return e.state.stats.audio.mos=e.state.audioQualityScore(),e.state.stats.video.mos=e.state.videoQualityScore(),{audio:i.pick(t,e.state.stats.audio),video:i.pick(t.concat(["frameRate","recommendedResolution","recommendedFrameRate"]),e.state.stats.video)}}(N);d||(s=u).audio.bitrate&&s.audio.bitrate>f.default.qualityThresholds.audio[0].bps&&(s.audio.packetLossRatio&&s.audio.packetLossRatio100?4.5:1+.035*r+71e-7*r*(r-60)*(100-r)}(0,(a(r.audio)-a(n.audio))/o)}t.default=function(e,t,r,a){return e.intervalId=window.setInterval((function(){t.getStats((function(i,u){if(!u)return null;if(u.audio.bytesReceived<0||s.getOr(1,"video.bytesReceived",u)<0)return e.clearInterval(),a(e);if(u&&e.statsLog.push(u),r&&"function"==typeof r&&r(i,u),e.statsLog.length<2)return null;e.stats=o.default(e);var c=f(t,e.statsLog);e.videoScoresLog.push(c);var d=l(t,e.statsLog);return e.audioScoresLog.push(d),e.pruneScores(),n.default(e.statsLog)?(e.clearInterval(),a(e)):null}))}),i.default.scoreInterval),t.on("destroyed",e.clearInterval.bind(e)),e}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=r(17),o=r(18),i=r(4);t.default=function(e){var t=n.default(e),r=i.default.steadyStateAllowedDelta,s=!0;if(t.lengtho*r&&(s=!1)}})),s}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=r(17),o=r(18),i=r(55),s=r(4),a=r(1);function u(e,t){var r=0,n=0,o=0;t.forEach((function(t){r+=t.averageBitrate,n+=t.packetLossRatio,"video"===e&&(o+=Number(a.getOr(0,"frameRate",t)))}));var s={bitrate:r/t.length,packetLossRatio:n/t.length},u=i.default(s,e),c=u.supported,f=u.reason,l=u.recommendedResolution,d=u.recommendedFrameRate,p="video"===e?{recommendedResolution:l,recommendedFrameRate:d,frameRate:o/t.length}:{};return Object.assign({},s,{supported:c,reason:f},p)}t.default=function(e){var t=n.default(e.statsLog),r=o.default(t);return{audio:e.hasAudioTrack()?u("audio",r.audio):{supported:!1,reason:s.default.strings.noMic},video:e.audioOnlyFallback?{supported:!1,reason:s.default.strings.bandwidthLow}:e.hasVideoTrack()?u("video",r.video):{supported:!1,reason:s.default.strings.noCam}}}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=r(4),o=r(1);t.default=function(e,t){for(var r=n.default.qualityThresholds,i=e.bitrate,s=e.packetLossRatio,a=r[t],u=!1,c=30,f="",l=void 0,d=0;d=p.bps&&s<=p.plr){u=!0,"video"===t&&(l=o.get("recommendedSetting",p),c=Number(l.substring(l.indexOf("@")+1).replace("FPS","")),f=l.substring(0,l.indexOf("@")-1));break}}var h={supported:u,recommendedFrameRate:c,recommendedResolution:f};return u?u&&"video"===t&&(h.recommendedFrameRate=c,h.recommendedResolution=f):h.reason=n.default.strings.bandwidthLow,h}},function(e,t,r){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=r(1);t.default=function(){var e=function(){var e=window&&window.navigator;return"undefined"!=typeof window&&window.navigator?n.get("mozGetUserMedia",e)?"Firefox":n.get("webkitGetUserMedia",e)?window.hasOwnProperty("webkitRTCPeerConnection")?e.userAgent.match(/Edg/)?"Edge":e.userAgent.match(/Opera|OPR\//)?"Opera":"Chrome":e.userAgent.match(/Version\/(\d+).(\d+)/)?"Safari":"WebKit browser without WebRTC support":e.mediaDevices&&e.userAgent.match(/edge\/(\d+).(\d+)$/)?"non-Chromium Edge":e.userAgent.indexOf("MSIE ")>0||e.userAgent.match(/Trident.*rv\:11\./)?"Internet Explorer":e.mediaDevices&&e.userAgent.match(/AppleWebKit\/(\d+)\./)?"Safari":"unsupported browser":"not a browser"}();return{browser:e,supported:["Chrome","Firefox","Internet Explorer","Safari","Edge"].indexOf(e)>-1}}},function(e,t,r){var n,o,i;var s=function(){function e(e,t){for(var r=0;r undefined\n * typeof document -> undefined\n *\n * react-native:\n * navigator.product -> 'ReactNative'\n * nativescript\n * navigator.product -> 'NativeScript' or 'NS'\n */\nfunction isStandardBrowserEnv() {\n if (typeof navigator !== 'undefined' && (navigator.product === 'ReactNative' ||\n navigator.product === 'NativeScript' ||\n navigator.product === 'NS')) {\n return false;\n }\n return (\n typeof window !== 'undefined' &&\n typeof document !== 'undefined'\n );\n}\n\n/**\n * Iterate over an Array or an Object invoking a function for each item.\n *\n * If `obj` is an Array callback will be called passing\n * the value, index, and complete array for each item.\n *\n * If 'obj' is an Object callback will be called passing\n * the value, key, and complete object for each property.\n *\n * @param {Object|Array} obj The object to iterate\n * @param {Function} fn The callback to invoke for each item\n */\nfunction forEach(obj, fn) {\n // Don't bother if no value provided\n if (obj === null || typeof obj === 'undefined') {\n return;\n }\n\n // Force an array if not already something iterable\n if (typeof obj !== 'object') {\n /*eslint no-param-reassign:0*/\n obj = [obj];\n }\n\n if (isArray(obj)) {\n // Iterate over array values\n for (var i = 0, l = obj.length; i < l; i++) {\n fn.call(null, obj[i], i, obj);\n }\n } else {\n // Iterate over object keys\n for (var key in obj) {\n if (Object.prototype.hasOwnProperty.call(obj, key)) {\n fn.call(null, obj[key], key, obj);\n }\n }\n }\n}\n\n/**\n * Accepts varargs expecting each argument to be an object, then\n * immutably merges the properties of each object and returns result.\n *\n * When multiple objects contain the same key the later object in\n * the arguments list will take precedence.\n *\n * Example:\n *\n * ```js\n * var result = merge({foo: 123}, {foo: 456});\n * console.log(result.foo); // outputs 456\n * ```\n *\n * @param {Object} obj1 Object to merge\n * @returns {Object} Result of all merge properties\n */\nfunction merge(/* obj1, obj2, obj3, ... */) {\n var result = {};\n function assignValue(val, key) {\n if (typeof result[key] === 'object' && typeof val === 'object') {\n result[key] = merge(result[key], val);\n } else {\n result[key] = val;\n }\n }\n\n for (var i = 0, l = arguments.length; i < l; i++) {\n forEach(arguments[i], assignValue);\n }\n return result;\n}\n\n/**\n * Function equal to merge with the difference being that no reference\n * to original objects is kept.\n *\n * @see merge\n * @param {Object} obj1 Object to merge\n * @returns {Object} Result of all merge properties\n */\nfunction deepMerge(/* obj1, obj2, obj3, ... */) {\n var result = {};\n function assignValue(val, key) {\n if (typeof result[key] === 'object' && typeof val === 'object') {\n result[key] = deepMerge(result[key], val);\n } else if (typeof val === 'object') {\n result[key] = deepMerge({}, val);\n } else {\n result[key] = val;\n }\n }\n\n for (var i = 0, l = arguments.length; i < l; i++) {\n forEach(arguments[i], assignValue);\n }\n return result;\n}\n\n/**\n * Extends object a by mutably adding to it the properties of object b.\n *\n * @param {Object} a The object to be extended\n * @param {Object} b The object to copy properties from\n * @param {Object} thisArg The object to bind function to\n * @return {Object} The resulting value of object a\n */\nfunction extend(a, b, thisArg) {\n forEach(b, function assignValue(val, key) {\n if (thisArg && typeof val === 'function') {\n a[key] = bind(val, thisArg);\n } else {\n a[key] = val;\n }\n });\n return a;\n}\n\nmodule.exports = {\n isArray: isArray,\n isArrayBuffer: isArrayBuffer,\n isBuffer: isBuffer,\n isFormData: isFormData,\n isArrayBufferView: isArrayBufferView,\n isString: isString,\n isNumber: isNumber,\n isObject: isObject,\n isUndefined: isUndefined,\n isDate: isDate,\n isFile: isFile,\n isBlob: isBlob,\n isFunction: isFunction,\n isStream: isStream,\n isURLSearchParams: isURLSearchParams,\n isStandardBrowserEnv: isStandardBrowserEnv,\n forEach: forEach,\n merge: merge,\n deepMerge: deepMerge,\n extend: extend,\n trim: trim\n};\n","/**\n * @module Util\n */\n\n/**\n * Returns a copy of an object, setting or overriding the property with the provided value\n */\nexport const assoc = (key: string, value: any, obj: Object): Object => ({ ...obj, [key]: value });\n\n/**\n * Returns a copy of an object, setting or overriding the property at the specified path\n * with the provided value. The path should be provided as a period-delimited string.\n */\nexport const assocPath = (path: string, value: any, obj: Object): Object => {\n const keys: string[] = path.split('.');\n const key = keys[0];\n if (!keys.length) {\n return obj;\n }\n if (keys.length === 1) {\n return assoc(key, value, obj);\n }\n\n const valForKey = get(key, obj);\n const base: Object = (!!valForKey && typeof valForKey === 'object') ? valForKey : { ...obj, [key]: {} };\n const update = assoc(key, assocPath(keys.slice(1).join('.'), value, get(key, base)), obj);\n return { ...obj, ...update };\n\n};\n\n/**\n * Returns a (nested) property from the provided object or undefined\n */\nexport const get = (props: string, obj: any): T => {\n if (!obj) {\n return obj;\n }\n const [current, ...rest] = props.split('.');\n const result = obj[current];\n if (result === undefined || result === null) {\n return result;\n }\n if (rest.length) {\n return get(rest.join('.'), result);\n }\n return result;\n};\n\n/**\n * Returns a (nested) property from the provided object or the default\n * value if undefined\n */\nexport const getOr = (defaultValue: any, props: string, obj: any): T => get(props, obj) || defaultValue;\n\n/**\n * Returns a subset of the provided object with the specified properties. Keys whose corresponding\n * values are undefined are not included.\n */\nexport const pick =\n (\n props: K[],\n obj: T,\n all: boolean = false): Partial => {\n const update = (acc: object, prop: K): Partial =>\n obj[prop] !== undefined || all ? { ...acc, [prop]: obj[prop] } : acc;\n return props.reduce(update, {});\n };\n\n/**\n * Returns a subset of the provided object with the specified properties. Keys whose corresponding\n * values are undefined are included.\n */\nexport const pickAll = (props: K[], obj: T): Partial =>\n pick(props, obj, true);\n\n/**\n * Returns the last element from an array\n */\nexport const last = (list: T[]): (T | undefined) => list[list.length - 1];\n\n/**\n * Returns the nth element of an array. If a negative value is passed, the nth element from the end\n * of the array will be returned.\n */\nexport const nth = (n: number, list: T[]): (T | undefined) => {\n return n < 0 ? list[list.length + n] : list[n];\n};\n\n/**\n * Returns the first element from a list, or undefined if it doesn't exist\n */\nexport const head = (list: T[]): (T | undefined) => nth(0, list);\n","/**\n * @module Errors/Connectivity/OpenTok\n */\n\n/**\n * Define errors returned by OpenTok.js\n */\n\nimport { get } from '../util';\nimport { OTError } from '../types/opentok/error';\n\nexport enum ErrorNames {\n NETWORK_TEST_ERROR = 'NetworkTestError',\n MISSING_OPENTOK_INSTANCE = 'MissingOpenTokInstanceError',\n INCOMPLETE_SESSON_CREDENTIALS = 'IncompleteSessionCredentialsError',\n MISSING_SESSON_CREDENTIALS = 'MissingSessionCredentialsError',\n INVALID_ON_UPDATE_CALLBACK = 'InvalidOnUpdateCallback',\n CONNECTIVITY_ERROR = 'ConnectivityError',\n API_CONNECTIVITY_ERROR = 'APIConnectivityError',\n CONNECT_TO_SESSION_ERROR = 'ConnectToSessionError',\n CONNECT_TO_SESSION_TOKEN_ERROR = 'ConnectToSessionTokenError',\n CONNECT_TO_SESSION_ID_ERROR = 'ConnectToSessionSessionIdError',\n CONNECT_TO_SESSION_NETWORK_ERROR = 'ConnectToSessionNetworkError',\n MEDIA_DEVICE_ERROR = 'MediaDeviceError',\n FAILED_TO_OBTAIN_MEDIA_DEVICES = 'FailedToObtainMediaDevices',\n NO_VIDEO_CAPTURE_DEVICES = 'NoVideoCaptureDevicesError',\n NO_AUDIO_CAPTURE_DEVICES = 'NoAudioCaptureDevicesError',\n PUBLISH_TO_SESSION_ERROR = 'PublishToSessionError',\n INIT_PUBLISHER_ERROR = 'InitPublisherError',\n FAILED_MESSAGING_SERVER_TEST = 'FailedMessagingServerTestError',\n FAILED_TO_CREATE_LOCAL_PUBLISHER = 'FailedToCreateLocalPublisher',\n PUBLISH_TO_SESSION_NOT_CONNECTED = 'PublishToSessionNotConnectedError',\n PUBLISH_TO_SESSION_PERMISSION_OR_TIMEOUT_ERROR = 'PublishToSessionPermissionOrTimeoutError',\n PUBLISH_TO_SESSION_NETWORK_ERROR = 'PublishToSessionNetworkError',\n SUBSCRIBE_TO_SESSION_ERROR = 'SubscribeToSessionError',\n LOGGING_SERVER_CONNECTION_ERROR = 'LoggingServerConnectionError',\n QUALITY_TEST_ERROR = 'QualityTestError',\n UNSUPPORTED_BROWSER = 'UnsupportedBrowser',\n SUBSCRIBER_GET_STATS_ERROR = 'SubscriberGetStatsError',\n MISSING_SUBSCRIBER_ERROR = 'MissingSubscriberError',\n}\n\n\nexport enum OTErrorType {\n JS_EXCEPTION = 'JS_EXCEPTION',\n OT_AUTHENTICATION_ERROR = 'OT_AUTHENTICATION_ERROR',\n OT_INVALID_HTTP_STATUS = 'OT_INVALID_HTTP_STATUS',\n OT_CONNECT_FAILED = 'OT_CONNECT_FAILED',\n OT_INVALID_SESSION_ID = 'OT_INVALID_SESSION_ID',\n CONNECT_FAILED = 'CONNECT_FAILED',\n CONNECT_REJECTED = 'CONNECT_REJECTED',\n CONNECTION_TIMEOUT = 'CONNECTION_TIMEOUT',\n NOT_CONNECTED = 'NOT_CONNECTED',\n INVALID_PARAMETER = 'INVALID_PARAMETER',\n P2P_CONNECTION_FAILED = 'P2P_CONNECTION_FAILED',\n API_RESPONSE_FAILURE = 'API_RESPONSE_FAILURE',\n TERMS_OF_SERVICE_FAILURE = 'TERMS_OF_SERVICE_FAILURE',\n CONNECTION_LIMIT_EXCEEDED = 'CONNECTION_LIMIT_EXCEEDED',\n UNABLE_TO_PUBLISH = 'UNABLE_TO_PUBLISH',\n UNABLE_TO_SUBSCRIBE = 'UNABLE_TO_SUBSCRIBE',\n UNSUPPORTED_VIDEO_CODEC = 'UNSUPPORTED_VIDEO_CODEC',\n UNABLE_TO_FORCE_DISCONNECT = 'UNABLE_TO_FORCE_DISCONNECT',\n UNABLE_TO_FORCE_UNPUBLISH = 'UNABLE_TO_FORCE_UNPUBLISH',\n PUBLISHER_ICE_WORKFLOW_FAILED = 'PUBLISHER_ICE_WORKFLOW_FAILED',\n SUBSCRIBER_ICE_WORKFLOW_FAILED = 'SUBSCRIBER_ICE_WORKFLOW_FAILED',\n STREAM_LIMIT_EXCEEDED = 'STREAM_LIMIT_EXCEEDED',\n UNEXPECTED_SERVER_RESPONSE = 'UNEXPECTED_SERVER_RESPONSE',\n REPORT_ISSUE_ERROR = 'REPORT_ISSUE_ERROR',\n ANVIL_BADLY_FORMED_RESPONSE = 'ANVIL_BADLY_FORMED_RESPONSE',\n ANVIL_INVALID_HTTP_STATUS = 'ANVIL_INVALID_HTTP_STATUS',\n ANVIL_XDOMAIN_OR_PARSING_ERROR = 'ANVIL_XDOMAIN_OR_PARSING_ERROR',\n ANVIL_UNKNOWN_HTTP_ERROR = 'ANVIL_UNKNOWN_HTTP_ERROR',\n ANVIL_UNEXPECTED_ERROR_CODE = 'ANVIL_UNEXPECTED_ERROR_CODE',\n ANVIL_EMPTY_RESPONSE_BODY = 'ANVIL_EMPTY_RESPONSE_BODY',\n ANVIL_CONNECT_FAILED = 'ANVIL_CONNECT_FAILED',\n}\n\nexport const errorHasName =\n (error: OTError | null = null, name: OTErrorType): boolean => get('name', error) === name;\n","'use strict';\n\nvar asap = require('asap/raw');\n\nfunction noop() {}\n\n// States:\n//\n// 0 - pending\n// 1 - fulfilled with _value\n// 2 - rejected with _value\n// 3 - adopted the state of another promise, _value\n//\n// once the state is no longer pending (0) it is immutable\n\n// All `_` prefixed properties will be reduced to `_{random number}`\n// at build time to obfuscate them and discourage their use.\n// We don't use symbols or Object.defineProperty to fully hide them\n// because the performance isn't good enough.\n\n\n// to avoid using try/catch inside critical functions, we\n// extract them to here.\nvar LAST_ERROR = null;\nvar IS_ERROR = {};\nfunction getThen(obj) {\n try {\n return obj.then;\n } catch (ex) {\n LAST_ERROR = ex;\n return IS_ERROR;\n }\n}\n\nfunction tryCallOne(fn, a) {\n try {\n return fn(a);\n } catch (ex) {\n LAST_ERROR = ex;\n return IS_ERROR;\n }\n}\nfunction tryCallTwo(fn, a, b) {\n try {\n fn(a, b);\n } catch (ex) {\n LAST_ERROR = ex;\n return IS_ERROR;\n }\n}\n\nmodule.exports = Promise;\n\nfunction Promise(fn) {\n if (typeof this !== 'object') {\n throw new TypeError('Promises must be constructed via new');\n }\n if (typeof fn !== 'function') {\n throw new TypeError('Promise constructor\\'s argument is not a function');\n }\n this._U = 0;\n this._V = 0;\n this._W = null;\n this._X = null;\n if (fn === noop) return;\n doResolve(fn, this);\n}\nPromise._Y = null;\nPromise._Z = null;\nPromise._0 = noop;\n\nPromise.prototype.then = function(onFulfilled, onRejected) {\n if (this.constructor !== Promise) {\n return safeThen(this, onFulfilled, onRejected);\n }\n var res = new Promise(noop);\n handle(this, new Handler(onFulfilled, onRejected, res));\n return res;\n};\n\nfunction safeThen(self, onFulfilled, onRejected) {\n return new self.constructor(function (resolve, reject) {\n var res = new Promise(noop);\n res.then(resolve, reject);\n handle(self, new Handler(onFulfilled, onRejected, res));\n });\n}\nfunction handle(self, deferred) {\n while (self._V === 3) {\n self = self._W;\n }\n if (Promise._Y) {\n Promise._Y(self);\n }\n if (self._V === 0) {\n if (self._U === 0) {\n self._U = 1;\n self._X = deferred;\n return;\n }\n if (self._U === 1) {\n self._U = 2;\n self._X = [self._X, deferred];\n return;\n }\n self._X.push(deferred);\n return;\n }\n handleResolved(self, deferred);\n}\n\nfunction handleResolved(self, deferred) {\n asap(function() {\n var cb = self._V === 1 ? deferred.onFulfilled : deferred.onRejected;\n if (cb === null) {\n if (self._V === 1) {\n resolve(deferred.promise, self._W);\n } else {\n reject(deferred.promise, self._W);\n }\n return;\n }\n var ret = tryCallOne(cb, self._W);\n if (ret === IS_ERROR) {\n reject(deferred.promise, LAST_ERROR);\n } else {\n resolve(deferred.promise, ret);\n }\n });\n}\nfunction resolve(self, newValue) {\n // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure\n if (newValue === self) {\n return reject(\n self,\n new TypeError('A promise cannot be resolved with itself.')\n );\n }\n if (\n newValue &&\n (typeof newValue === 'object' || typeof newValue === 'function')\n ) {\n var then = getThen(newValue);\n if (then === IS_ERROR) {\n return reject(self, LAST_ERROR);\n }\n if (\n then === self.then &&\n newValue instanceof Promise\n ) {\n self._V = 3;\n self._W = newValue;\n finale(self);\n return;\n } else if (typeof then === 'function') {\n doResolve(then.bind(newValue), self);\n return;\n }\n }\n self._V = 1;\n self._W = newValue;\n finale(self);\n}\n\nfunction reject(self, newValue) {\n self._V = 2;\n self._W = newValue;\n if (Promise._Z) {\n Promise._Z(self, newValue);\n }\n finale(self);\n}\nfunction finale(self) {\n if (self._U === 1) {\n handle(self, self._X);\n self._X = null;\n }\n if (self._U === 2) {\n for (var i = 0; i < self._X.length; i++) {\n handle(self, self._X[i]);\n }\n self._X = null;\n }\n}\n\nfunction Handler(onFulfilled, onRejected, promise){\n this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;\n this.onRejected = typeof onRejected === 'function' ? onRejected : null;\n this.promise = promise;\n}\n\n/**\n * Take a potentially misbehaving resolver function and make sure\n * onFulfilled and onRejected are only called once.\n *\n * Makes no guarantees about asynchrony.\n */\nfunction doResolve(fn, promise) {\n var done = false;\n var res = tryCallTwo(fn, function (value) {\n if (done) return;\n done = true;\n resolve(promise, value);\n }, function (reason) {\n if (done) return;\n done = true;\n reject(promise, reason);\n });\n if (!done && res === IS_ERROR) {\n done = true;\n reject(promise, LAST_ERROR);\n }\n}\n","\nexport interface AudioThreshold { bps: number; plr: number; }\nexport interface VideoThreshold extends AudioThreshold { recommendedSetting: string; }\n\nexport type QualityTestConfig = {\n getStatsInterval: number,\n getStatsVideoAndAudioTestDuration: number,\n getStatsAudioOnlyDuration: number,\n subscribeOptions: {\n testNetwork: boolean,\n audioVolume: number,\n },\n minimumVideoAndAudioTestSampleSize: number,\n steadyStateSampleWindow: number, // this is also used to calculate bandwidth\n steadyStateAllowedDelta: number,\n qualityThresholds: {\n audio: AudioThreshold[],\n video: VideoThreshold[],\n },\n strings: {\n bandwidthLow: string,\n noCam: string,\n noMic: string,\n },\n};\n\nconst config: QualityTestConfig = {\n getStatsInterval: 1000,\n getStatsVideoAndAudioTestDuration: 30000,\n getStatsAudioOnlyDuration: 10000,\n subscribeOptions: {\n testNetwork: true,\n audioVolume: 0,\n },\n minimumVideoAndAudioTestSampleSize: 5,\n steadyStateSampleWindow: 5000, // this is also used to calculate bandwidth\n steadyStateAllowedDelta: 0.05, // 1 = 100%, from point to point\n qualityThresholds: {\n video: [\n {\n bps: 1000000,\n plr: 0.005,\n recommendedSetting: '1280x720 @ 30FPS',\n },\n {\n bps: 600000,\n plr: 0.005,\n recommendedSetting: '640x480 @ 30FPS',\n },\n {\n bps: 300000,\n plr: 0.005,\n recommendedSetting: '320x240 @ 30FPS',\n },\n {\n bps: 350000,\n plr: 0.03,\n recommendedSetting: '1280x720 @ 30FPS',\n },\n {\n bps: 250000,\n plr: 0.03,\n recommendedSetting: '640x480 @ 30FPS',\n },\n {\n bps: 150000,\n plr: 0.03,\n recommendedSetting: '320x240 @ 30FPS',\n },\n ],\n audio: [\n {\n bps: 25000,\n plr: 0.05,\n },\n ],\n },\n strings: {\n bandwidthLow: 'Bandwidth too low.',\n noCam: 'No camera was found.',\n noMic: 'No microphone was found.',\n },\n};\n\nexport default config;\n","/**\n * @module Errors\n */\n\nimport { ErrorNames } from './types';\n\n/**\n * Base class for errors used throughout Network Connectivity tests.\n */\nexport class NetworkTestError extends Error {\n name: string;\n constructor(message: string, name?: string) {\n super(message);\n this.name = name || ErrorNames.NETWORK_TEST_ERROR;\n this.stack = (new Error(message)).stack;\n }\n}\n\nexport class MissingOpenTokInstanceError extends NetworkTestError {\n constructor() {\n super('An instance of OT, the OpenTok.js client SDK, is required.');\n this.name = ErrorNames.MISSING_OPENTOK_INSTANCE;\n }\n}\n\nexport class IncompleteSessionCredentialsError extends NetworkTestError {\n constructor() {\n super('NetworkConnectivity requires an apiKey, sessionId, and token.',\n ErrorNames.INCOMPLETE_SESSON_CREDENTIALS);\n }\n}\n\nexport class MissingSessionCredentialsError extends NetworkTestError {\n constructor() {\n super('NetworkConnectivity requires OpenTok session credentials.',\n ErrorNames.MISSING_SESSON_CREDENTIALS);\n }\n}\n\nexport class InvalidOnUpdateCallback extends NetworkTestError {\n constructor() {\n super('The onUpdate callback must be a function that accepts a single parameter.',\n ErrorNames.INVALID_ON_UPDATE_CALLBACK);\n }\n}\n","module.exports = require('./lib/axios');","'use strict';\n\nmodule.exports = function bind(fn, thisArg) {\n return function wrap() {\n var args = new Array(arguments.length);\n for (var i = 0; i < args.length; i++) {\n args[i] = arguments[i];\n }\n return fn.apply(thisArg, args);\n };\n};\n","'use strict';\n\nvar utils = require('./../utils');\n\nfunction encode(val) {\n return encodeURIComponent(val).\n replace(/%40/gi, '@').\n replace(/%3A/gi, ':').\n replace(/%24/g, '$').\n replace(/%2C/gi, ',').\n replace(/%20/g, '+').\n replace(/%5B/gi, '[').\n replace(/%5D/gi, ']');\n}\n\n/**\n * Build a URL by appending params to the end\n *\n * @param {string} url The base of the url (e.g., http://www.google.com)\n * @param {object} [params] The params to be appended\n * @returns {string} The formatted url\n */\nmodule.exports = function buildURL(url, params, paramsSerializer) {\n /*eslint no-param-reassign:0*/\n if (!params) {\n return url;\n }\n\n var serializedParams;\n if (paramsSerializer) {\n serializedParams = paramsSerializer(params);\n } else if (utils.isURLSearchParams(params)) {\n serializedParams = params.toString();\n } else {\n var parts = [];\n\n utils.forEach(params, function serialize(val, key) {\n if (val === null || typeof val === 'undefined') {\n return;\n }\n\n if (utils.isArray(val)) {\n key = key + '[]';\n } else {\n val = [val];\n }\n\n utils.forEach(val, function parseValue(v) {\n if (utils.isDate(v)) {\n v = v.toISOString();\n } else if (utils.isObject(v)) {\n v = JSON.stringify(v);\n }\n parts.push(encode(key) + '=' + encode(v));\n });\n });\n\n serializedParams = parts.join('&');\n }\n\n if (serializedParams) {\n var hashmarkIndex = url.indexOf('#');\n if (hashmarkIndex !== -1) {\n url = url.slice(0, hashmarkIndex);\n }\n\n url += (url.indexOf('?') === -1 ? '?' : '&') + serializedParams;\n }\n\n return url;\n};\n","'use strict';\n\nmodule.exports = function isCancel(value) {\n return !!(value && value.__CANCEL__);\n};\n","'use strict';\n\nvar utils = require('./utils');\nvar normalizeHeaderName = require('./helpers/normalizeHeaderName');\n\nvar DEFAULT_CONTENT_TYPE = {\n 'Content-Type': 'application/x-www-form-urlencoded'\n};\n\nfunction setContentTypeIfUnset(headers, value) {\n if (!utils.isUndefined(headers) && utils.isUndefined(headers['Content-Type'])) {\n headers['Content-Type'] = value;\n }\n}\n\nfunction getDefaultAdapter() {\n var adapter;\n if (typeof XMLHttpRequest !== 'undefined') {\n // For browsers use XHR adapter\n adapter = require('./adapters/xhr');\n } else if (typeof process !== 'undefined' && Object.prototype.toString.call(process) === '[object process]') {\n // For node use HTTP adapter\n adapter = require('./adapters/http');\n }\n return adapter;\n}\n\nvar defaults = {\n adapter: getDefaultAdapter(),\n\n transformRequest: [function transformRequest(data, headers) {\n normalizeHeaderName(headers, 'Accept');\n normalizeHeaderName(headers, 'Content-Type');\n if (utils.isFormData(data) ||\n utils.isArrayBuffer(data) ||\n utils.isBuffer(data) ||\n utils.isStream(data) ||\n utils.isFile(data) ||\n utils.isBlob(data)\n ) {\n return data;\n }\n if (utils.isArrayBufferView(data)) {\n return data.buffer;\n }\n if (utils.isURLSearchParams(data)) {\n setContentTypeIfUnset(headers, 'application/x-www-form-urlencoded;charset=utf-8');\n return data.toString();\n }\n if (utils.isObject(data)) {\n setContentTypeIfUnset(headers, 'application/json;charset=utf-8');\n return JSON.stringify(data);\n }\n return data;\n }],\n\n transformResponse: [function transformResponse(data) {\n /*eslint no-param-reassign:0*/\n if (typeof data === 'string') {\n try {\n data = JSON.parse(data);\n } catch (e) { /* Ignore */ }\n }\n return data;\n }],\n\n /**\n * A timeout in milliseconds to abort a request. If set to 0 (default) a\n * timeout is not created.\n */\n timeout: 0,\n\n xsrfCookieName: 'XSRF-TOKEN',\n xsrfHeaderName: 'X-XSRF-TOKEN',\n\n maxContentLength: -1,\n\n validateStatus: function validateStatus(status) {\n return status >= 200 && status < 300;\n }\n};\n\ndefaults.headers = {\n common: {\n 'Accept': 'application/json, text/plain, */*'\n }\n};\n\nutils.forEach(['delete', 'get', 'head'], function forEachMethodNoData(method) {\n defaults.headers[method] = {};\n});\n\nutils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {\n defaults.headers[method] = utils.merge(DEFAULT_CONTENT_TYPE);\n});\n\nmodule.exports = defaults;\n","'use strict';\n\nvar utils = require('./../utils');\nvar settle = require('./../core/settle');\nvar buildURL = require('./../helpers/buildURL');\nvar buildFullPath = require('../core/buildFullPath');\nvar parseHeaders = require('./../helpers/parseHeaders');\nvar isURLSameOrigin = require('./../helpers/isURLSameOrigin');\nvar createError = require('../core/createError');\n\nmodule.exports = function xhrAdapter(config) {\n return new Promise(function dispatchXhrRequest(resolve, reject) {\n var requestData = config.data;\n var requestHeaders = config.headers;\n\n if (utils.isFormData(requestData)) {\n delete requestHeaders['Content-Type']; // Let the browser set it\n }\n\n var request = new XMLHttpRequest();\n\n // HTTP basic authentication\n if (config.auth) {\n var username = config.auth.username || '';\n var password = config.auth.password || '';\n requestHeaders.Authorization = 'Basic ' + btoa(username + ':' + password);\n }\n\n var fullPath = buildFullPath(config.baseURL, config.url);\n request.open(config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true);\n\n // Set the request timeout in MS\n request.timeout = config.timeout;\n\n // Listen for ready state\n request.onreadystatechange = function handleLoad() {\n if (!request || request.readyState !== 4) {\n return;\n }\n\n // The request errored out and we didn't get a response, this will be\n // handled by onerror instead\n // With one exception: request that using file: protocol, most browsers\n // will return status as 0 even though it's a successful request\n if (request.status === 0 && !(request.responseURL && request.responseURL.indexOf('file:') === 0)) {\n return;\n }\n\n // Prepare the response\n var responseHeaders = 'getAllResponseHeaders' in request ? parseHeaders(request.getAllResponseHeaders()) : null;\n var responseData = !config.responseType || config.responseType === 'text' ? request.responseText : request.response;\n var response = {\n data: responseData,\n status: request.status,\n statusText: request.statusText,\n headers: responseHeaders,\n config: config,\n request: request\n };\n\n settle(resolve, reject, response);\n\n // Clean up request\n request = null;\n };\n\n // Handle browser request cancellation (as opposed to a manual cancellation)\n request.onabort = function handleAbort() {\n if (!request) {\n return;\n }\n\n reject(createError('Request aborted', config, 'ECONNABORTED', request));\n\n // Clean up request\n request = null;\n };\n\n // Handle low level network errors\n request.onerror = function handleError() {\n // Real errors are hidden from us by the browser\n // onerror should only fire if it's a network error\n reject(createError('Network Error', config, null, request));\n\n // Clean up request\n request = null;\n };\n\n // Handle timeout\n request.ontimeout = function handleTimeout() {\n var timeoutErrorMessage = 'timeout of ' + config.timeout + 'ms exceeded';\n if (config.timeoutErrorMessage) {\n timeoutErrorMessage = config.timeoutErrorMessage;\n }\n reject(createError(timeoutErrorMessage, config, 'ECONNABORTED',\n request));\n\n // Clean up request\n request = null;\n };\n\n // Add xsrf header\n // This is only done if running in a standard browser environment.\n // Specifically not if we're in a web worker, or react-native.\n if (utils.isStandardBrowserEnv()) {\n var cookies = require('./../helpers/cookies');\n\n // Add xsrf header\n var xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ?\n cookies.read(config.xsrfCookieName) :\n undefined;\n\n if (xsrfValue) {\n requestHeaders[config.xsrfHeaderName] = xsrfValue;\n }\n }\n\n // Add headers to the request\n if ('setRequestHeader' in request) {\n utils.forEach(requestHeaders, function setRequestHeader(val, key) {\n if (typeof requestData === 'undefined' && key.toLowerCase() === 'content-type') {\n // Remove Content-Type if data is undefined\n delete requestHeaders[key];\n } else {\n // Otherwise add header to the request\n request.setRequestHeader(key, val);\n }\n });\n }\n\n // Add withCredentials to request if needed\n if (!utils.isUndefined(config.withCredentials)) {\n request.withCredentials = !!config.withCredentials;\n }\n\n // Add responseType to request if needed\n if (config.responseType) {\n try {\n request.responseType = config.responseType;\n } catch (e) {\n // Expected DOMException thrown by browsers not compatible XMLHttpRequest Level 2.\n // But, this can be suppressed for 'json' type as it can be parsed by default 'transformResponse' function.\n if (config.responseType !== 'json') {\n throw e;\n }\n }\n }\n\n // Handle progress if needed\n if (typeof config.onDownloadProgress === 'function') {\n request.addEventListener('progress', config.onDownloadProgress);\n }\n\n // Not all browsers support upload events\n if (typeof config.onUploadProgress === 'function' && request.upload) {\n request.upload.addEventListener('progress', config.onUploadProgress);\n }\n\n if (config.cancelToken) {\n // Handle cancellation\n config.cancelToken.promise.then(function onCanceled(cancel) {\n if (!request) {\n return;\n }\n\n request.abort();\n reject(cancel);\n // Clean up request\n request = null;\n });\n }\n\n if (requestData === undefined) {\n requestData = null;\n }\n\n // Send the request\n request.send(requestData);\n });\n};\n","'use strict';\n\nvar enhanceError = require('./enhanceError');\n\n/**\n * Create an Error with the specified message, config, error code, request and response.\n *\n * @param {string} message The error message.\n * @param {Object} config The config.\n * @param {string} [code] The error code (for example, 'ECONNABORTED').\n * @param {Object} [request] The request.\n * @param {Object} [response] The response.\n * @returns {Error} The created error.\n */\nmodule.exports = function createError(message, config, code, request, response) {\n var error = new Error(message);\n return enhanceError(error, config, code, request, response);\n};\n","'use strict';\n\nvar utils = require('../utils');\n\n/**\n * Config-specific merge-function which creates a new config-object\n * by merging two configuration objects together.\n *\n * @param {Object} config1\n * @param {Object} config2\n * @returns {Object} New object resulting from merging config2 to config1\n */\nmodule.exports = function mergeConfig(config1, config2) {\n // eslint-disable-next-line no-param-reassign\n config2 = config2 || {};\n var config = {};\n\n var valueFromConfig2Keys = ['url', 'method', 'params', 'data'];\n var mergeDeepPropertiesKeys = ['headers', 'auth', 'proxy'];\n var defaultToConfig2Keys = [\n 'baseURL', 'url', 'transformRequest', 'transformResponse', 'paramsSerializer',\n 'timeout', 'withCredentials', 'adapter', 'responseType', 'xsrfCookieName',\n 'xsrfHeaderName', 'onUploadProgress', 'onDownloadProgress',\n 'maxContentLength', 'validateStatus', 'maxRedirects', 'httpAgent',\n 'httpsAgent', 'cancelToken', 'socketPath'\n ];\n\n utils.forEach(valueFromConfig2Keys, function valueFromConfig2(prop) {\n if (typeof config2[prop] !== 'undefined') {\n config[prop] = config2[prop];\n }\n });\n\n utils.forEach(mergeDeepPropertiesKeys, function mergeDeepProperties(prop) {\n if (utils.isObject(config2[prop])) {\n config[prop] = utils.deepMerge(config1[prop], config2[prop]);\n } else if (typeof config2[prop] !== 'undefined') {\n config[prop] = config2[prop];\n } else if (utils.isObject(config1[prop])) {\n config[prop] = utils.deepMerge(config1[prop]);\n } else if (typeof config1[prop] !== 'undefined') {\n config[prop] = config1[prop];\n }\n });\n\n utils.forEach(defaultToConfig2Keys, function defaultToConfig2(prop) {\n if (typeof config2[prop] !== 'undefined') {\n config[prop] = config2[prop];\n } else if (typeof config1[prop] !== 'undefined') {\n config[prop] = config1[prop];\n }\n });\n\n var axiosKeys = valueFromConfig2Keys\n .concat(mergeDeepPropertiesKeys)\n .concat(defaultToConfig2Keys);\n\n var otherKeys = Object\n .keys(config2)\n .filter(function filterAxiosKeys(key) {\n return axiosKeys.indexOf(key) === -1;\n });\n\n utils.forEach(otherKeys, function otherKeysDefaultToConfig2(prop) {\n if (typeof config2[prop] !== 'undefined') {\n config[prop] = config2[prop];\n } else if (typeof config1[prop] !== 'undefined') {\n config[prop] = config1[prop];\n }\n });\n\n return config;\n};\n","'use strict';\n\n/**\n * A `Cancel` is an object that is thrown when an operation is canceled.\n *\n * @class\n * @param {string=} message The message.\n */\nfunction Cancel(message) {\n this.message = message;\n}\n\nCancel.prototype.toString = function toString() {\n return 'Cancel' + (this.message ? ': ' + this.message : '');\n};\n\nCancel.prototype.__CANCEL__ = true;\n\nmodule.exports = Cancel;\n","'use strict';\n\nmodule.exports = require('./lib')\n","\"use strict\";\n\n// Use the fastest means possible to execute a task in its own turn, with\n// priority over other events including IO, animation, reflow, and redraw\n// events in browsers.\n//\n// An exception thrown by a task will permanently interrupt the processing of\n// subsequent tasks. The higher level `asap` function ensures that if an\n// exception is thrown by a task, that the task queue will continue flushing as\n// soon as possible, but if you use `rawAsap` directly, you are responsible to\n// either ensure that no exceptions are thrown from your task, or to manually\n// call `rawAsap.requestFlush` if an exception is thrown.\nmodule.exports = rawAsap;\nfunction rawAsap(task) {\n if (!queue.length) {\n requestFlush();\n flushing = true;\n }\n // Equivalent to push, but avoids a function call.\n queue[queue.length] = task;\n}\n\nvar queue = [];\n// Once a flush has been requested, no further calls to `requestFlush` are\n// necessary until the next `flush` completes.\nvar flushing = false;\n// `requestFlush` is an implementation-specific method that attempts to kick\n// off a `flush` event as quickly as possible. `flush` will attempt to exhaust\n// the event queue before yielding to the browser's own event loop.\nvar requestFlush;\n// The position of the next task to execute in the task queue. This is\n// preserved between calls to `flush` so that it can be resumed if\n// a task throws an exception.\nvar index = 0;\n// If a task schedules additional tasks recursively, the task queue can grow\n// unbounded. To prevent memory exhaustion, the task queue will periodically\n// truncate already-completed tasks.\nvar capacity = 1024;\n\n// The flush function processes all tasks that have been scheduled with\n// `rawAsap` unless and until one of those tasks throws an exception.\n// If a task throws an exception, `flush` ensures that its state will remain\n// consistent and will resume where it left off when called again.\n// However, `flush` does not make any arrangements to be called again if an\n// exception is thrown.\nfunction flush() {\n while (index < queue.length) {\n var currentIndex = index;\n // Advance the index before calling the task. This ensures that we will\n // begin flushing on the next task the task throws an error.\n index = index + 1;\n queue[currentIndex].call();\n // Prevent leaking memory for long chains of recursive calls to `asap`.\n // If we call `asap` within tasks scheduled by `asap`, the queue will\n // grow, but to avoid an O(n) walk for every task we execute, we don't\n // shift tasks off the queue after they have been executed.\n // Instead, we periodically shift 1024 tasks off the queue.\n if (index > capacity) {\n // Manually shift all values starting at the index back to the\n // beginning of the queue.\n for (var scan = 0, newLength = queue.length - index; scan < newLength; scan++) {\n queue[scan] = queue[scan + index];\n }\n queue.length -= index;\n index = 0;\n }\n }\n queue.length = 0;\n index = 0;\n flushing = false;\n}\n\n// `requestFlush` is implemented using a strategy based on data collected from\n// every available SauceLabs Selenium web driver worker at time of writing.\n// https://docs.google.com/spreadsheets/d/1mG-5UYGup5qxGdEMWkhP6BWCz053NUb2E1QoUTU16uA/edit#gid=783724593\n\n// Safari 6 and 6.1 for desktop, iPad, and iPhone are the only browsers that\n// have WebKitMutationObserver but not un-prefixed MutationObserver.\n// Must use `global` or `self` instead of `window` to work in both frames and web\n// workers. `global` is a provision of Browserify, Mr, Mrs, or Mop.\n\n/* globals self */\nvar scope = typeof global !== \"undefined\" ? global : self;\nvar BrowserMutationObserver = scope.MutationObserver || scope.WebKitMutationObserver;\n\n// MutationObservers are desirable because they have high priority and work\n// reliably everywhere they are implemented.\n// They are implemented in all modern browsers.\n//\n// - Android 4-4.3\n// - Chrome 26-34\n// - Firefox 14-29\n// - Internet Explorer 11\n// - iPad Safari 6-7.1\n// - iPhone Safari 7-7.1\n// - Safari 6-7\nif (typeof BrowserMutationObserver === \"function\") {\n requestFlush = makeRequestCallFromMutationObserver(flush);\n\n// MessageChannels are desirable because they give direct access to the HTML\n// task queue, are implemented in Internet Explorer 10, Safari 5.0-1, and Opera\n// 11-12, and in web workers in many engines.\n// Although message channels yield to any queued rendering and IO tasks, they\n// would be better than imposing the 4ms delay of timers.\n// However, they do not work reliably in Internet Explorer or Safari.\n\n// Internet Explorer 10 is the only browser that has setImmediate but does\n// not have MutationObservers.\n// Although setImmediate yields to the browser's renderer, it would be\n// preferrable to falling back to setTimeout since it does not have\n// the minimum 4ms penalty.\n// Unfortunately there appears to be a bug in Internet Explorer 10 Mobile (and\n// Desktop to a lesser extent) that renders both setImmediate and\n// MessageChannel useless for the purposes of ASAP.\n// https://github.com/kriskowal/q/issues/396\n\n// Timers are implemented universally.\n// We fall back to timers in workers in most engines, and in foreground\n// contexts in the following browsers.\n// However, note that even this simple case requires nuances to operate in a\n// broad spectrum of browsers.\n//\n// - Firefox 3-13\n// - Internet Explorer 6-9\n// - iPad Safari 4.3\n// - Lynx 2.8.7\n} else {\n requestFlush = makeRequestCallFromTimer(flush);\n}\n\n// `requestFlush` requests that the high priority event queue be flushed as\n// soon as possible.\n// This is useful to prevent an error thrown in a task from stalling the event\n// queue if the exception handled by Node.js’s\n// `process.on(\"uncaughtException\")` or by a domain.\nrawAsap.requestFlush = requestFlush;\n\n// To request a high priority event, we induce a mutation observer by toggling\n// the text of a text node between \"1\" and \"-1\".\nfunction makeRequestCallFromMutationObserver(callback) {\n var toggle = 1;\n var observer = new BrowserMutationObserver(callback);\n var node = document.createTextNode(\"\");\n observer.observe(node, {characterData: true});\n return function requestCall() {\n toggle = -toggle;\n node.data = toggle;\n };\n}\n\n// The message channel technique was discovered by Malte Ubl and was the\n// original foundation for this library.\n// http://www.nonblocking.io/2011/06/windownexttick.html\n\n// Safari 6.0.5 (at least) intermittently fails to create message ports on a\n// page's first load. Thankfully, this version of Safari supports\n// MutationObservers, so we don't need to fall back in that case.\n\n// function makeRequestCallFromMessageChannel(callback) {\n// var channel = new MessageChannel();\n// channel.port1.onmessage = callback;\n// return function requestCall() {\n// channel.port2.postMessage(0);\n// };\n// }\n\n// For reasons explained above, we are also unable to use `setImmediate`\n// under any circumstances.\n// Even if we were, there is another bug in Internet Explorer 10.\n// It is not sufficient to assign `setImmediate` to `requestFlush` because\n// `setImmediate` must be called *by name* and therefore must be wrapped in a\n// closure.\n// Never forget.\n\n// function makeRequestCallFromSetImmediate(callback) {\n// return function requestCall() {\n// setImmediate(callback);\n// };\n// }\n\n// Safari 6.0 has a problem where timers will get lost while the user is\n// scrolling. This problem does not impact ASAP because Safari 6.0 supports\n// mutation observers, so that implementation is used instead.\n// However, if we ever elect to use timers in Safari, the prevalent work-around\n// is to add a scroll event listener that calls for a flush.\n\n// `setTimeout` does not call the passed callback if the delay is less than\n// approximately 7 in web workers in Firefox 8 through 18, and sometimes not\n// even then.\n\nfunction makeRequestCallFromTimer(callback) {\n return function requestCall() {\n // We dispatch a timeout with a specified delay of 0 for engines that\n // can reliably accommodate that request. This will usually be snapped\n // to a 4 milisecond delay, but once we're flushing, there's no delay\n // between events.\n var timeoutHandle = setTimeout(handleTimer, 0);\n // However, since this timer gets frequently dropped in Firefox\n // workers, we enlist an interval handle that will try to fire\n // an event 20 times per second until it succeeds.\n var intervalHandle = setInterval(handleTimer, 50);\n\n function handleTimer() {\n // Whichever timer succeeds will cancel both timers and\n // execute the callback.\n clearTimeout(timeoutHandle);\n clearInterval(intervalHandle);\n callback();\n }\n };\n}\n\n// This is for `asap.js` only.\n// Its name will be periodically randomized to break any code that depends on\n// its existence.\nrawAsap.makeRequestCallFromTimer = makeRequestCallFromTimer;\n\n// ASAP was originally a nextTick shim included in Q. This was factored out\n// into this ASAP package. It was later adapted to RSVP which made further\n// amendments. These decisions, particularly to marginalize MessageChannel and\n// to capture the MutationObserver implementation in a closure, were integrated\n// back into ASAP proper.\n// https://github.com/tildeio/rsvp.js/blob/cddf7232546a9cf858524b75cde6f9edf72620a7/lib/rsvp/asap.js\n","import config from './config';\nimport { SubscriberStats } from '../../types/opentok/subscriber';\nimport { getOr, last } from '../../util';\n\nexport default function getLatestSampleWindow(stats: SubscriberStats[]): SubscriberStats[] {\n const mostRecentTimestamp: number = getOr(0, 'timestamp', last(stats));\n const oldestAllowedTime: number = mostRecentTimestamp - config.steadyStateSampleWindow;\n return stats.filter((stat: SubscriberStats) => stat.timestamp >= oldestAllowedTime);\n}\n","import { SubscriberStats } from '../../types/opentok/subscriber';\nimport { AV, HasAudioVideo, QualityStats } from '../types/stats';\n\nfunction calculateStats(type: AV, samples: SubscriberStats[]): QualityStats[] {\n\n const qualityStats: QualityStats[] = [];\n\n for (let i = 1; i < samples.length; i += 1) {\n\n const currStat = samples[i];\n const prevStat = samples[i - 1];\n\n if (currStat[type] && prevStat[type]) {\n const bytesIncreased = currStat[type].bytesReceived ?\n currStat[type].bytesReceived - prevStat[type].bytesReceived : 0;\n const bitsIncreased = bytesIncreased * 8;\n const msIncreased = currStat.timestamp - prevStat.timestamp;\n const secondsElapsed = msIncreased / 1000;\n const averageBitrate = bitsIncreased / secondsElapsed;\n\n const packetsReceived = currStat[type].packetsReceived;\n const packetsLost = currStat[type].packetsLost;\n const packetLossRatio = packetsLost / packetsReceived;\n\n const frameRate = type === 'video' ? { frameRate: currStat[type].frameRate } : {};\n\n qualityStats.push({ averageBitrate, packetLossRatio, ...frameRate });\n }\n }\n\n return qualityStats;\n}\n\nexport default function calculateQualityStats(latestSamples: SubscriberStats[]): HasAudioVideo {\n if (latestSamples.length < 2) {\n throw new Error('Cannot calculate bitrate with less than two data points.');\n }\n\n return {\n audio: calculateStats('audio', latestSamples),\n video: calculateStats('video', latestSamples),\n };\n}\n","import { OT } from '../../types/opentok';\n\nimport { AverageStats, Bandwidth, HasAudioVideo } from '../types/stats';\n\nexport default class MOSState {\n statsLog: OT.SubscriberStats[];\n audioScoresLog: number[];\n videoScoresLog: number[];\n stats: HasAudioVideo = { audio: {}, video: {} };\n bandwidth: Bandwidth = { audio: 0, video: 0 };\n intervalId?: number;\n audioOnlyFallback: boolean;\n\n constructor(audioOnly?: boolean) {\n this.statsLog = [];\n this.audioScoresLog = [];\n this.videoScoresLog = [];\n this.audioOnlyFallback = !!audioOnly;\n }\n\n static readonly maxLogLength: number = 1000;\n static readonly scoreInterval: number = 1000;\n\n readonly hasAudioTrack = (): boolean => this.statsLog[0] && !!this.statsLog[0].audio;\n readonly hasVideoTrack = (): boolean => this.statsLog[0] && !!this.statsLog[0].video;\n\n private audioScore(): number {\n return this.audioScoresLog.reduce((acc, score) => acc + score, 0) / this.audioScoresLog.length;\n }\n\n private videoScore(): number {\n return this.videoScoresLog.reduce((acc, score) => acc + score, 0) / this.videoScoresLog.length;\n }\n\n clearInterval() {\n if (this.intervalId) {\n window.clearInterval(this.intervalId);\n }\n this.intervalId = undefined;\n }\n\n private pruneAudioScores() {\n const audioScoresLog = this.audioScoresLog;\n while (audioScoresLog.length > MOSState.maxLogLength) {\n audioScoresLog.shift();\n }\n this.audioScoresLog = audioScoresLog;\n }\n\n private pruneVideoScores() {\n const videoScoresLog = this.videoScoresLog;\n while (videoScoresLog.length > MOSState.maxLogLength) {\n videoScoresLog.shift();\n }\n this.videoScoresLog = videoScoresLog;\n }\n\n pruneScores() {\n this.pruneAudioScores();\n this.pruneVideoScores();\n }\n\n audioQualityScore(): number {\n return this.hasAudioTrack() ? this.audioScore() : 1;\n }\n\n videoQualityScore(): number {\n return this.hasVideoTrack() ? this.videoScore() : 1;\n }\n}\n","/**\n * @module NetworkTest\n */\n\n/**\n* Define Network Connectivy class\n*/\n\nconst version = require('../../package.json').version;\nimport { OT } from './types/opentok';\nimport { UpdateCallback, UpdateCallbackStats } from './types/callbacks';\nimport {\n testConnectivity,\n ConnectivityTestResults,\n} from './testConnectivity';\nimport {\n testQuality,\n stopQualityTest,\n QualityTestResults,\n} from './testQuality';\nimport {\n IncompleteSessionCredentialsError,\n InvalidOnUpdateCallback,\n MissingOpenTokInstanceError,\n MissingSessionCredentialsError,\n} from './errors';\n/* tslint:disable */\nimport OTKAnalytics = require('opentok-solutions-logging');\n/* tslint:enable */\n\nexport interface NetworkTestOptions {\n audioOnly?: boolean;\n timeout?: number;\n audioSource?: string;\n videoSource?: string;\n initSessionOptions?: OT.InitSessionOptions;\n proxyServerUrl?: string;\n skipPublisherCleaningOnSuccess?: boolean;\n}\n\nexport default class NetworkTest {\n credentials: OT.SessionCredentials;\n OT: OT.Client;\n otLogging: OTKAnalytics;\n options?: NetworkTestOptions;\n\n /**\n * Returns an instance of NetworkConnectivity. See the \"API reference\" section of the\n * README.md file in the root of the opentok-network-test-js project for details.\n */\n constructor(OT: OT.Client, credentials: OT.SessionCredentials, options?: NetworkTestOptions) {\n this.validateOT(OT);\n this.validateCredentials(credentials);\n const proxyServerUrl = this.validateProxyUrl(options);\n this.otLogging = this.startLoggingEngine(credentials.apiKey, credentials.sessionId, proxyServerUrl);\n this.OT = OT;\n this.credentials = credentials;\n this.options = options;\n this.setProxyUrl(proxyServerUrl);\n }\n\n private validateOT(OT: OT.Client) {\n if (!OT || typeof OT !== 'object' || !OT.initSession) {\n throw new MissingOpenTokInstanceError();\n }\n }\n\n private validateCredentials(credentials: OT.SessionCredentials) {\n if (!credentials) {\n throw new MissingSessionCredentialsError();\n }\n if (!credentials.apiKey || !credentials.sessionId || !credentials.token) {\n throw new IncompleteSessionCredentialsError();\n }\n }\n\n private validateProxyUrl(options?: NetworkTestOptions): string {\n if (!options || !options.proxyServerUrl) {\n return '';\n }\n return options.proxyServerUrl;\n }\n\n private setProxyUrl(proxyServerUrl: string) {\n if (this.OT.setProxyUrl && typeof this.OT.setProxyUrl === 'function' && proxyServerUrl) {\n this.OT.setProxyUrl(proxyServerUrl);\n }\n }\n\n private startLoggingEngine(apiKey: string, sessionId: string, proxyUrl: string): OTKAnalytics {\n return new OTKAnalytics({\n sessionId,\n partnerId: apiKey,\n source: window.location.href,\n clientVersion: 'js-network-test-' + version,\n name: 'opentok-network-test',\n componentId: 'opentok-network-test',\n }, {\n proxyUrl,\n });\n }\n\n /**\n * This method checks to see if the client can connect to TokBox servers required for\n * using OpenTok.\n *\n * See the \"API reference\" section of the README.md file in the root of the\n * opentok-network-test-js project for details.\n */\n testConnectivity(): Promise {\n this.otLogging.logEvent({ action: 'testConnectivity', variation: 'Attempt' });\n return testConnectivity(this.OT, this.credentials, this.otLogging, this.options);\n }\n\n /**\n * This function runs a test publisher and based on the measured video bitrate,\n * audio bitrate, and the audio packet loss for the published stream, it returns\n * results indicating the recommended supported publisher settings.\n *\n * See the \"API reference\" section of the README.md file in the root of the\n * opentok-network-test-js project for details.\n */\n testQuality(updateCallback?: UpdateCallback): Promise {\n this.otLogging.logEvent({ action: 'testQuality', variation: 'Attempt' });\n if (updateCallback) {\n if (typeof updateCallback !== 'function' || updateCallback.length !== 1) {\n this.otLogging.logEvent({ action: 'testQuality', variation: 'Failure' });\n throw new InvalidOnUpdateCallback();\n }\n }\n return testQuality(\n this.OT, this.credentials, this.otLogging, this.options, updateCallback);\n }\n\n /**\n * Stops the currently running test.\n *\n * See the \"API reference\" section of the README.md file in the root of the\n * opentok-network-test-js project for details.\n */\n stop() {\n stopQualityTest();\n }\n}\n\nexport { ErrorNames } from './errors/types';\n","/**\n * @module Test/Connectivity\n * @preferred\n *\n * Defines the methods required for the Connectivity Test Flow\n */\n\n/**\n * Connectivity Test Flow\n */\nimport axios from 'axios';\nimport * as Promise from 'promise';\n/* tslint:disable */\nimport OTKAnalytics = require('opentok-solutions-logging');\n/* tslint:enable */\nimport {\n NetworkTestOptions,\n} from '../index';\nimport { OT } from '../types/opentok';\nimport * as e from './errors';\nimport { OTErrorType, errorHasName } from '../errors/types';\nimport { mapErrors, FailureCase } from './errors/mapping';\nimport { getOr } from '../util';\n\ntype AV = 'audio' | 'video';\ntype CreateLocalPublisherResults = { publisher: OT.Publisher };\ntype PublishToSessionResults = { session: OT.Session } & CreateLocalPublisherResults;\ntype SubscribeToSessionResults = { subscriber: OT.Subscriber } & PublishToSessionResults;\ntype DeviceMap = { [deviceId: string]: OT.Device };\ntype AvailableDevices = { audio: DeviceMap, video: DeviceMap };\n\nexport type ConnectivityTestResults = {\n success: boolean,\n failedTests: FailureCase[],\n};\n\n/**\n * Disconnect from a session. Once disconnected, remove all session\n * event listeners and invoke the provided callback function.\n */\nfunction disconnectFromSession(session: OT.Session) {\n return new Promise((resolve, reject) => {\n session.on('sessionDisconnected', () => {\n session.off();\n resolve();\n });\n session.disconnect();\n });\n}\n\n/**\n * Clean subscriber objects before disconnecting from the session\n * @param session\n * @param subscriber\n */\nfunction cleanSubscriber(session: OT.Session, subscriber: OT.Subscriber) {\n return new Promise((resolve, reject) => {\n subscriber.on('destroyed', () => {\n resolve();\n });\n if (!subscriber) {\n resolve();\n }\n session.unsubscribe(subscriber);\n });\n}\n\nfunction cleanPublisher(publisher: OT.Publisher) {\n return new Promise((resolve, reject) => {\n publisher.on('destroyed', () => {\n resolve();\n });\n if (!publisher) {\n resolve();\n }\n publisher.destroy();\n });\n}\n\n/**\n * Attempt to connect to the OpenTok sessionope\n */\nfunction connectToSession(\n OT: OT.Client,\n { apiKey, sessionId, token }: OT.SessionCredentials,\n options?: NetworkTestOptions,\n): Promise {\n return new Promise((resolve, reject) => {\n let sessionOptions: OT.InitSessionOptions = {};\n if (options && options.initSessionOptions) {\n sessionOptions = options.initSessionOptions;\n }\n if (options && options.proxyServerUrl) {\n if (!OT.hasOwnProperty('setProxyUrl')) { // Fallback for OT.version < 2.17.4\n sessionOptions.proxyUrl = options.proxyServerUrl;\n }\n }\n const session = OT.initSession(apiKey, sessionId, sessionOptions);\n session.connect(token, (error?: OT.OTError) => {\n if (errorHasName(error, OTErrorType.OT_AUTHENTICATION_ERROR)) {\n reject(new e.ConnectToSessionTokenError());\n } else if (errorHasName(error, OTErrorType.OT_INVALID_SESSION_ID)) {\n reject(new e.ConnectToSessionSessionIdError());\n } else if (errorHasName(error, OTErrorType.OT_CONNECT_FAILED)) {\n reject(new e.ConnectToSessionNetworkError());\n } else if (errorHasName(error, OTErrorType.OT_INVALID_HTTP_STATUS)) {\n reject(new e.APIConnectivityError());\n } else if (error) {\n reject(new e.ConnectToSessionError());\n } else {\n resolve(session);\n }\n });\n });\n}\n\n/**\n * Ensure that audio and video devices are available\n */\nfunction validateDevices(OT: OT.Client): Promise {\n return new Promise((resolve, reject) => {\n OT.getDevices((error?: OT.OTError, devices: OT.Device[] = []) => {\n\n if (error) {\n reject(new e.FailedToObtainMediaDevices());\n } else {\n\n const availableDevices: AvailableDevices = devices.reduce(\n (acc: AvailableDevices, device: OT.Device) => {\n const type: AV = device.kind === 'audioInput' ? 'audio' : 'video';\n return { ...acc, [type]: { ...acc[type], [device.deviceId]: device } };\n },\n { audio: {}, video: {} },\n );\n\n if (!Object.keys(availableDevices.audio).length && !Object.keys(availableDevices.video).length) {\n reject(new e.FailedToObtainMediaDevices());\n } else {\n resolve(availableDevices);\n }\n }\n });\n });\n}\n\n/**\n * Create a local publisher object using any specified device options\n */\nfunction checkCreateLocalPublisher(\n OT: OT.Client,\n options?: NetworkTestOptions,\n): Promise {\n return new Promise((resolve, reject) => {\n validateDevices(OT)\n .then((availableDevices: AvailableDevices) => {\n const publisherDiv = document.createElement('div');\n publisherDiv.style.position = 'fixed';\n publisherDiv.style.bottom = '-1px';\n publisherDiv.style.width = '1px';\n publisherDiv.style.height = '1px';\n publisherDiv.style.opacity = '0.01';\n document.body.appendChild(publisherDiv);\n const publisherOptions: OT.PublisherProperties = {\n width: '100%',\n height: '100%',\n insertMode: 'append',\n showControls: false,\n };\n if (options && options.audioSource) {\n publisherOptions.audioSource = options.audioSource;\n }\n if (options && options.videoSource) {\n publisherOptions.videoSource = options.videoSource;\n }\n if (options && options.audioOnly) {\n publisherOptions.videoSource = null;\n }\n if (!Object.keys(availableDevices.audio).length) {\n publisherOptions.audioSource = null;\n }\n if (!Object.keys(availableDevices.video).length) {\n publisherOptions.videoSource = null;\n }\n const publisher = OT.initPublisher(publisherDiv, publisherOptions, (error?: OT.OTError) => {\n if (!error) {\n resolve({ publisher });\n } else {\n reject(new e.FailedToCreateLocalPublisher());\n }\n });\n publisher.on('streamCreated', () => {\n publisherDiv.style.visibility = 'hidden';\n });\n })\n .catch(reject);\n });\n}\n\n/**\n * Attempt to publish to the session\n */\nfunction checkPublishToSession(\n OT: OT.Client, session: OT.Session,\n options?: NetworkTestOptions,\n): Promise {\n return new Promise((resolve, reject) => {\n const disconnectAndReject = (rejectError: Error) => {\n disconnectFromSession(session).then(() => {\n reject(rejectError);\n });\n };\n checkCreateLocalPublisher(OT, options)\n .then(({ publisher }: CreateLocalPublisherResults) => {\n session.publish(publisher, (error?: OT.OTError) => {\n if (error) {\n if (errorHasName(error, OTErrorType.NOT_CONNECTED)) {\n disconnectAndReject(new e.PublishToSessionNotConnectedError());\n } else if (errorHasName(error, OTErrorType.UNABLE_TO_PUBLISH)) {\n disconnectAndReject(\n new e.PublishToSessionPermissionOrTimeoutError());\n } else if (error) {\n disconnectAndReject(new e.PublishToSessionError());\n }\n } else {\n resolve({ ...{ session }, ...{ publisher } });\n }\n });\n }).catch((error: e.ConnectivityError) => {\n disconnectAndReject(error);\n });\n });\n}\n\n/**\n * Attempt to subscribe to our publisher\n */\nfunction checkSubscribeToSession({ session, publisher }: PublishToSessionResults): Promise {\n return new Promise((resolve, reject) => {\n const config = { testNetwork: true, audioVolume: 0 };\n const disconnectAndReject = (rejectError: Error) => {\n cleanPublisher(publisher)\n .then(() => disconnectFromSession(session))\n .then(() => {\n reject(rejectError);\n });\n };\n if (!publisher.stream) {\n disconnectAndReject(new e.SubscribeToSessionError());\n } else {\n const subscriberDiv = document.createElement('div');\n const subscriber = session.subscribe(publisher.stream, subscriberDiv, config, (error?: OT.OTError) => {\n if (error) {\n disconnectAndReject(new e.SubscribeToSessionError());\n } else {\n resolve({ ...{ session }, ...{ publisher }, ...{ subscriber } });\n }\n });\n }\n });\n}\n\n/**\n * Attempt to connect to the tokbox client logging server\n */\nfunction checkLoggingServer(OT: OT.Client, options?: NetworkTestOptions, input?: SubscribeToSessionResults): Promise {\n return new Promise((resolve, reject) => {\n const loggingUrl = `${getOr('', 'properties.loggingURL', OT)}/logging/ClientEvent`; // https://hlg.tokbox.com/prod\n const url = options && options.proxyServerUrl && `${options.proxyServerUrl}/${loggingUrl.replace('https://', '')}` || loggingUrl;\n const handleError = () => reject(new e.LoggingServerConnectionError());\n\n axios.post(url)\n .then(response => response.status === 200 ? resolve(input) : handleError())\n .catch(handleError);\n\n });\n}\n\n/**\n * This method checks to see if the client can connect to TokBox servers required for using OpenTok\n */\nexport function testConnectivity(\n OT: OT.Client,\n credentials: OT.SessionCredentials,\n otLogging: OTKAnalytics,\n options?: NetworkTestOptions,\n): Promise {\n return new Promise((resolve, reject) => {\n\n const onSuccess = (flowResults: SubscribeToSessionResults) => {\n const results: ConnectivityTestResults = {\n success: true,\n failedTests: [],\n };\n otLogging.logEvent({ action: 'testConnectivity', variation: 'Success' });\n return cleanSubscriber(flowResults.session, flowResults.subscriber)\n .then(() => {\n if (options && options.skipPublisherCleaningOnSuccess) return Promise.resolve();\n\n return cleanPublisher(flowResults.publisher);\n })\n .then(() => disconnectFromSession(flowResults.session))\n .then(() => resolve(results));\n };\n\n const onFailure = (error: Error) => {\n\n const handleResults = (...errors: e.ConnectivityError[]) => {\n /**\n * If we have a messaging server failure, we will also fail the media\n * server test by default.\n */\n const baseFailures: FailureCase[] = mapErrors(...errors);\n const messagingFailure = baseFailures.find(c => c.type === 'messaging');\n const failedTests = [\n ...baseFailures,\n ...messagingFailure ? mapErrors(new e.FailedMessagingServerTestError()) : [],\n ];\n\n const results = {\n failedTests,\n success: false,\n };\n otLogging.logEvent({ action: 'testConnectivity', variation: 'Success' });\n resolve(results);\n };\n\n /**\n * If we encounter an error before testing the connection to the logging server, let's perform\n * that test as well before returning results.\n */\n if (error.name === 'LoggingServerConnectionError') {\n handleResults(error);\n } else {\n checkLoggingServer(OT, options)\n .then(() => handleResults(error))\n .catch((loggingError: e.LoggingServerConnectionError) => handleResults(error, loggingError));\n }\n };\n\n connectToSession(OT, credentials, options)\n .then((session: OT.Session) => checkPublishToSession(OT, session, options))\n .then(checkSubscribeToSession)\n .then((results: SubscribeToSessionResults) => checkLoggingServer(OT, options, results))\n .then(onSuccess)\n .catch(onFailure);\n });\n}\n","'use strict';\n\nvar utils = require('./utils');\nvar bind = require('./helpers/bind');\nvar Axios = require('./core/Axios');\nvar mergeConfig = require('./core/mergeConfig');\nvar defaults = require('./defaults');\n\n/**\n * Create an instance of Axios\n *\n * @param {Object} defaultConfig The default config for the instance\n * @return {Axios} A new instance of Axios\n */\nfunction createInstance(defaultConfig) {\n var context = new Axios(defaultConfig);\n var instance = bind(Axios.prototype.request, context);\n\n // Copy axios.prototype to instance\n utils.extend(instance, Axios.prototype, context);\n\n // Copy context to instance\n utils.extend(instance, context);\n\n return instance;\n}\n\n// Create the default instance to be exported\nvar axios = createInstance(defaults);\n\n// Expose Axios class to allow class inheritance\naxios.Axios = Axios;\n\n// Factory for creating new instances\naxios.create = function create(instanceConfig) {\n return createInstance(mergeConfig(axios.defaults, instanceConfig));\n};\n\n// Expose Cancel & CancelToken\naxios.Cancel = require('./cancel/Cancel');\naxios.CancelToken = require('./cancel/CancelToken');\naxios.isCancel = require('./cancel/isCancel');\n\n// Expose all/spread\naxios.all = function all(promises) {\n return Promise.all(promises);\n};\naxios.spread = require('./helpers/spread');\n\nmodule.exports = axios;\n\n// Allow use of default import syntax in TypeScript\nmodule.exports.default = axios;\n","'use strict';\n\nvar utils = require('./../utils');\nvar buildURL = require('../helpers/buildURL');\nvar InterceptorManager = require('./InterceptorManager');\nvar dispatchRequest = require('./dispatchRequest');\nvar mergeConfig = require('./mergeConfig');\n\n/**\n * Create a new instance of Axios\n *\n * @param {Object} instanceConfig The default config for the instance\n */\nfunction Axios(instanceConfig) {\n this.defaults = instanceConfig;\n this.interceptors = {\n request: new InterceptorManager(),\n response: new InterceptorManager()\n };\n}\n\n/**\n * Dispatch a request\n *\n * @param {Object} config The config specific for this request (merged with this.defaults)\n */\nAxios.prototype.request = function request(config) {\n /*eslint no-param-reassign:0*/\n // Allow for axios('example/url'[, config]) a la fetch API\n if (typeof config === 'string') {\n config = arguments[1] || {};\n config.url = arguments[0];\n } else {\n config = config || {};\n }\n\n config = mergeConfig(this.defaults, config);\n\n // Set config.method\n if (config.method) {\n config.method = config.method.toLowerCase();\n } else if (this.defaults.method) {\n config.method = this.defaults.method.toLowerCase();\n } else {\n config.method = 'get';\n }\n\n // Hook up interceptors middleware\n var chain = [dispatchRequest, undefined];\n var promise = Promise.resolve(config);\n\n this.interceptors.request.forEach(function unshiftRequestInterceptors(interceptor) {\n chain.unshift(interceptor.fulfilled, interceptor.rejected);\n });\n\n this.interceptors.response.forEach(function pushResponseInterceptors(interceptor) {\n chain.push(interceptor.fulfilled, interceptor.rejected);\n });\n\n while (chain.length) {\n promise = promise.then(chain.shift(), chain.shift());\n }\n\n return promise;\n};\n\nAxios.prototype.getUri = function getUri(config) {\n config = mergeConfig(this.defaults, config);\n return buildURL(config.url, config.params, config.paramsSerializer).replace(/^\\?/, '');\n};\n\n// Provide aliases for supported request methods\nutils.forEach(['delete', 'get', 'head', 'options'], function forEachMethodNoData(method) {\n /*eslint func-names:0*/\n Axios.prototype[method] = function(url, config) {\n return this.request(utils.merge(config || {}, {\n method: method,\n url: url\n }));\n };\n});\n\nutils.forEach(['post', 'put', 'patch'], function forEachMethodWithData(method) {\n /*eslint func-names:0*/\n Axios.prototype[method] = function(url, data, config) {\n return this.request(utils.merge(config || {}, {\n method: method,\n url: url,\n data: data\n }));\n };\n});\n\nmodule.exports = Axios;\n","'use strict';\n\nvar utils = require('./../utils');\n\nfunction InterceptorManager() {\n this.handlers = [];\n}\n\n/**\n * Add a new interceptor to the stack\n *\n * @param {Function} fulfilled The function to handle `then` for a `Promise`\n * @param {Function} rejected The function to handle `reject` for a `Promise`\n *\n * @return {Number} An ID used to remove interceptor later\n */\nInterceptorManager.prototype.use = function use(fulfilled, rejected) {\n this.handlers.push({\n fulfilled: fulfilled,\n rejected: rejected\n });\n return this.handlers.length - 1;\n};\n\n/**\n * Remove an interceptor from the stack\n *\n * @param {Number} id The ID that was returned by `use`\n */\nInterceptorManager.prototype.eject = function eject(id) {\n if (this.handlers[id]) {\n this.handlers[id] = null;\n }\n};\n\n/**\n * Iterate over all the registered interceptors\n *\n * This method is particularly useful for skipping over any\n * interceptors that may have become `null` calling `eject`.\n *\n * @param {Function} fn The function to call for each interceptor\n */\nInterceptorManager.prototype.forEach = function forEach(fn) {\n utils.forEach(this.handlers, function forEachHandler(h) {\n if (h !== null) {\n fn(h);\n }\n });\n};\n\nmodule.exports = InterceptorManager;\n","'use strict';\n\nvar utils = require('./../utils');\nvar transformData = require('./transformData');\nvar isCancel = require('../cancel/isCancel');\nvar defaults = require('../defaults');\n\n/**\n * Throws a `Cancel` if cancellation has been requested.\n */\nfunction throwIfCancellationRequested(config) {\n if (config.cancelToken) {\n config.cancelToken.throwIfRequested();\n }\n}\n\n/**\n * Dispatch a request to the server using the configured adapter.\n *\n * @param {object} config The config that is to be used for the request\n * @returns {Promise} The Promise to be fulfilled\n */\nmodule.exports = function dispatchRequest(config) {\n throwIfCancellationRequested(config);\n\n // Ensure headers exist\n config.headers = config.headers || {};\n\n // Transform request data\n config.data = transformData(\n config.data,\n config.headers,\n config.transformRequest\n );\n\n // Flatten headers\n config.headers = utils.merge(\n config.headers.common || {},\n config.headers[config.method] || {},\n config.headers\n );\n\n utils.forEach(\n ['delete', 'get', 'head', 'post', 'put', 'patch', 'common'],\n function cleanHeaderConfig(method) {\n delete config.headers[method];\n }\n );\n\n var adapter = config.adapter || defaults.adapter;\n\n return adapter(config).then(function onAdapterResolution(response) {\n throwIfCancellationRequested(config);\n\n // Transform response data\n response.data = transformData(\n response.data,\n response.headers,\n config.transformResponse\n );\n\n return response;\n }, function onAdapterRejection(reason) {\n if (!isCancel(reason)) {\n throwIfCancellationRequested(config);\n\n // Transform response data\n if (reason && reason.response) {\n reason.response.data = transformData(\n reason.response.data,\n reason.response.headers,\n config.transformResponse\n );\n }\n }\n\n return Promise.reject(reason);\n });\n};\n","'use strict';\n\nvar utils = require('./../utils');\n\n/**\n * Transform the data for a request or a response\n *\n * @param {Object|String} data The data to be transformed\n * @param {Array} headers The headers for the request or response\n * @param {Array|Function} fns A single function or Array of functions\n * @returns {*} The resulting transformed data\n */\nmodule.exports = function transformData(data, headers, fns) {\n /*eslint no-param-reassign:0*/\n utils.forEach(fns, function transform(fn) {\n data = fn(data, headers);\n });\n\n return data;\n};\n","// shim for using process in browser\nvar process = module.exports = {};\n\n// cached from whatever global is present so that test runners that stub it\n// don't break things. But we need to wrap it in a try catch in case it is\n// wrapped in strict mode code which doesn't define any globals. It's inside a\n// function because try/catches deoptimize in certain engines.\n\nvar cachedSetTimeout;\nvar cachedClearTimeout;\n\nfunction defaultSetTimout() {\n throw new Error('setTimeout has not been defined');\n}\nfunction defaultClearTimeout () {\n throw new Error('clearTimeout has not been defined');\n}\n(function () {\n try {\n if (typeof setTimeout === 'function') {\n cachedSetTimeout = setTimeout;\n } else {\n cachedSetTimeout = defaultSetTimout;\n }\n } catch (e) {\n cachedSetTimeout = defaultSetTimout;\n }\n try {\n if (typeof clearTimeout === 'function') {\n cachedClearTimeout = clearTimeout;\n } else {\n cachedClearTimeout = defaultClearTimeout;\n }\n } catch (e) {\n cachedClearTimeout = defaultClearTimeout;\n }\n} ())\nfunction runTimeout(fun) {\n if (cachedSetTimeout === setTimeout) {\n //normal enviroments in sane situations\n return setTimeout(fun, 0);\n }\n // if setTimeout wasn't available but was latter defined\n if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {\n cachedSetTimeout = setTimeout;\n return setTimeout(fun, 0);\n }\n try {\n // when when somebody has screwed with setTimeout but no I.E. maddness\n return cachedSetTimeout(fun, 0);\n } catch(e){\n try {\n // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally\n return cachedSetTimeout.call(null, fun, 0);\n } catch(e){\n // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error\n return cachedSetTimeout.call(this, fun, 0);\n }\n }\n\n\n}\nfunction runClearTimeout(marker) {\n if (cachedClearTimeout === clearTimeout) {\n //normal enviroments in sane situations\n return clearTimeout(marker);\n }\n // if clearTimeout wasn't available but was latter defined\n if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {\n cachedClearTimeout = clearTimeout;\n return clearTimeout(marker);\n }\n try {\n // when when somebody has screwed with setTimeout but no I.E. maddness\n return cachedClearTimeout(marker);\n } catch (e){\n try {\n // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally\n return cachedClearTimeout.call(null, marker);\n } catch (e){\n // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error.\n // Some versions of I.E. have different rules for clearTimeout vs setTimeout\n return cachedClearTimeout.call(this, marker);\n }\n }\n\n\n\n}\nvar queue = [];\nvar draining = false;\nvar currentQueue;\nvar queueIndex = -1;\n\nfunction cleanUpNextTick() {\n if (!draining || !currentQueue) {\n return;\n }\n draining = false;\n if (currentQueue.length) {\n queue = currentQueue.concat(queue);\n } else {\n queueIndex = -1;\n }\n if (queue.length) {\n drainQueue();\n }\n}\n\nfunction drainQueue() {\n if (draining) {\n return;\n }\n var timeout = runTimeout(cleanUpNextTick);\n draining = true;\n\n var len = queue.length;\n while(len) {\n currentQueue = queue;\n queue = [];\n while (++queueIndex < len) {\n if (currentQueue) {\n currentQueue[queueIndex].run();\n }\n }\n queueIndex = -1;\n len = queue.length;\n }\n currentQueue = null;\n draining = false;\n runClearTimeout(timeout);\n}\n\nprocess.nextTick = function (fun) {\n var args = new Array(arguments.length - 1);\n if (arguments.length > 1) {\n for (var i = 1; i < arguments.length; i++) {\n args[i - 1] = arguments[i];\n }\n }\n queue.push(new Item(fun, args));\n if (queue.length === 1 && !draining) {\n runTimeout(drainQueue);\n }\n};\n\n// v8 likes predictible objects\nfunction Item(fun, array) {\n this.fun = fun;\n this.array = array;\n}\nItem.prototype.run = function () {\n this.fun.apply(null, this.array);\n};\nprocess.title = 'browser';\nprocess.browser = true;\nprocess.env = {};\nprocess.argv = [];\nprocess.version = ''; // empty string to avoid regexp issues\nprocess.versions = {};\n\nfunction noop() {}\n\nprocess.on = noop;\nprocess.addListener = noop;\nprocess.once = noop;\nprocess.off = noop;\nprocess.removeListener = noop;\nprocess.removeAllListeners = noop;\nprocess.emit = noop;\nprocess.prependListener = noop;\nprocess.prependOnceListener = noop;\n\nprocess.listeners = function (name) { return [] }\n\nprocess.binding = function (name) {\n throw new Error('process.binding is not supported');\n};\n\nprocess.cwd = function () { return '/' };\nprocess.chdir = function (dir) {\n throw new Error('process.chdir is not supported');\n};\nprocess.umask = function() { return 0; };\n","'use strict';\n\nvar utils = require('../utils');\n\nmodule.exports = function normalizeHeaderName(headers, normalizedName) {\n utils.forEach(headers, function processHeader(value, name) {\n if (name !== normalizedName && name.toUpperCase() === normalizedName.toUpperCase()) {\n headers[normalizedName] = value;\n delete headers[name];\n }\n });\n};\n","'use strict';\n\nvar createError = require('./createError');\n\n/**\n * Resolve or reject a Promise based on response status.\n *\n * @param {Function} resolve A function that resolves the promise.\n * @param {Function} reject A function that rejects the promise.\n * @param {object} response The response.\n */\nmodule.exports = function settle(resolve, reject, response) {\n var validateStatus = response.config.validateStatus;\n if (!validateStatus || validateStatus(response.status)) {\n resolve(response);\n } else {\n reject(createError(\n 'Request failed with status code ' + response.status,\n response.config,\n null,\n response.request,\n response\n ));\n }\n};\n","'use strict';\n\n/**\n * Update an Error with the specified config, error code, and response.\n *\n * @param {Error} error The error to update.\n * @param {Object} config The config.\n * @param {string} [code] The error code (for example, 'ECONNABORTED').\n * @param {Object} [request] The request.\n * @param {Object} [response] The response.\n * @returns {Error} The error.\n */\nmodule.exports = function enhanceError(error, config, code, request, response) {\n error.config = config;\n if (code) {\n error.code = code;\n }\n\n error.request = request;\n error.response = response;\n error.isAxiosError = true;\n\n error.toJSON = function() {\n return {\n // Standard\n message: this.message,\n name: this.name,\n // Microsoft\n description: this.description,\n number: this.number,\n // Mozilla\n fileName: this.fileName,\n lineNumber: this.lineNumber,\n columnNumber: this.columnNumber,\n stack: this.stack,\n // Axios\n config: this.config,\n code: this.code\n };\n };\n return error;\n};\n","'use strict';\n\nvar isAbsoluteURL = require('../helpers/isAbsoluteURL');\nvar combineURLs = require('../helpers/combineURLs');\n\n/**\n * Creates a new URL by combining the baseURL with the requestedURL,\n * only when the requestedURL is not already an absolute URL.\n * If the requestURL is absolute, this function returns the requestedURL untouched.\n *\n * @param {string} baseURL The base URL\n * @param {string} requestedURL Absolute or relative URL to combine\n * @returns {string} The combined full path\n */\nmodule.exports = function buildFullPath(baseURL, requestedURL) {\n if (baseURL && !isAbsoluteURL(requestedURL)) {\n return combineURLs(baseURL, requestedURL);\n }\n return requestedURL;\n};\n","'use strict';\n\n/**\n * Determines whether the specified URL is absolute\n *\n * @param {string} url The URL to test\n * @returns {boolean} True if the specified URL is absolute, otherwise false\n */\nmodule.exports = function isAbsoluteURL(url) {\n // A URL is considered absolute if it begins with \"://\" or \"//\" (protocol-relative URL).\n // RFC 3986 defines scheme name as a sequence of characters beginning with a letter and followed\n // by any combination of letters, digits, plus, period, or hyphen.\n return /^([a-z][a-z\\d\\+\\-\\.]*:)?\\/\\//i.test(url);\n};\n","'use strict';\n\n/**\n * Creates a new URL by combining the specified URLs\n *\n * @param {string} baseURL The base URL\n * @param {string} relativeURL The relative URL\n * @returns {string} The combined URL\n */\nmodule.exports = function combineURLs(baseURL, relativeURL) {\n return relativeURL\n ? baseURL.replace(/\\/+$/, '') + '/' + relativeURL.replace(/^\\/+/, '')\n : baseURL;\n};\n","'use strict';\n\nvar utils = require('./../utils');\n\n// Headers whose duplicates are ignored by node\n// c.f. https://nodejs.org/api/http.html#http_message_headers\nvar ignoreDuplicateOf = [\n 'age', 'authorization', 'content-length', 'content-type', 'etag',\n 'expires', 'from', 'host', 'if-modified-since', 'if-unmodified-since',\n 'last-modified', 'location', 'max-forwards', 'proxy-authorization',\n 'referer', 'retry-after', 'user-agent'\n];\n\n/**\n * Parse headers into an object\n *\n * ```\n * Date: Wed, 27 Aug 2014 08:58:49 GMT\n * Content-Type: application/json\n * Connection: keep-alive\n * Transfer-Encoding: chunked\n * ```\n *\n * @param {String} headers Headers needing to be parsed\n * @returns {Object} Headers parsed into an object\n */\nmodule.exports = function parseHeaders(headers) {\n var parsed = {};\n var key;\n var val;\n var i;\n\n if (!headers) { return parsed; }\n\n utils.forEach(headers.split('\\n'), function parser(line) {\n i = line.indexOf(':');\n key = utils.trim(line.substr(0, i)).toLowerCase();\n val = utils.trim(line.substr(i + 1));\n\n if (key) {\n if (parsed[key] && ignoreDuplicateOf.indexOf(key) >= 0) {\n return;\n }\n if (key === 'set-cookie') {\n parsed[key] = (parsed[key] ? parsed[key] : []).concat([val]);\n } else {\n parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;\n }\n }\n });\n\n return parsed;\n};\n","'use strict';\n\nvar utils = require('./../utils');\n\nmodule.exports = (\n utils.isStandardBrowserEnv() ?\n\n // Standard browser envs have full support of the APIs needed to test\n // whether the request URL is of the same origin as current location.\n (function standardBrowserEnv() {\n var msie = /(msie|trident)/i.test(navigator.userAgent);\n var urlParsingNode = document.createElement('a');\n var originURL;\n\n /**\n * Parse a URL to discover it's components\n *\n * @param {String} url The URL to be parsed\n * @returns {Object}\n */\n function resolveURL(url) {\n var href = url;\n\n if (msie) {\n // IE needs attribute set twice to normalize properties\n urlParsingNode.setAttribute('href', href);\n href = urlParsingNode.href;\n }\n\n urlParsingNode.setAttribute('href', href);\n\n // urlParsingNode provides the UrlUtils interface - http://url.spec.whatwg.org/#urlutils\n return {\n href: urlParsingNode.href,\n protocol: urlParsingNode.protocol ? urlParsingNode.protocol.replace(/:$/, '') : '',\n host: urlParsingNode.host,\n search: urlParsingNode.search ? urlParsingNode.search.replace(/^\\?/, '') : '',\n hash: urlParsingNode.hash ? urlParsingNode.hash.replace(/^#/, '') : '',\n hostname: urlParsingNode.hostname,\n port: urlParsingNode.port,\n pathname: (urlParsingNode.pathname.charAt(0) === '/') ?\n urlParsingNode.pathname :\n '/' + urlParsingNode.pathname\n };\n }\n\n originURL = resolveURL(window.location.href);\n\n /**\n * Determine if a URL shares the same origin as the current location\n *\n * @param {String} requestURL The URL to test\n * @returns {boolean} True if URL shares the same origin, otherwise false\n */\n return function isURLSameOrigin(requestURL) {\n var parsed = (utils.isString(requestURL)) ? resolveURL(requestURL) : requestURL;\n return (parsed.protocol === originURL.protocol &&\n parsed.host === originURL.host);\n };\n })() :\n\n // Non standard browser envs (web workers, react-native) lack needed support.\n (function nonStandardBrowserEnv() {\n return function isURLSameOrigin() {\n return true;\n };\n })()\n);\n","'use strict';\n\nvar utils = require('./../utils');\n\nmodule.exports = (\n utils.isStandardBrowserEnv() ?\n\n // Standard browser envs support document.cookie\n (function standardBrowserEnv() {\n return {\n write: function write(name, value, expires, path, domain, secure) {\n var cookie = [];\n cookie.push(name + '=' + encodeURIComponent(value));\n\n if (utils.isNumber(expires)) {\n cookie.push('expires=' + new Date(expires).toGMTString());\n }\n\n if (utils.isString(path)) {\n cookie.push('path=' + path);\n }\n\n if (utils.isString(domain)) {\n cookie.push('domain=' + domain);\n }\n\n if (secure === true) {\n cookie.push('secure');\n }\n\n document.cookie = cookie.join('; ');\n },\n\n read: function read(name) {\n var match = document.cookie.match(new RegExp('(^|;\\\\s*)(' + name + ')=([^;]*)'));\n return (match ? decodeURIComponent(match[3]) : null);\n },\n\n remove: function remove(name) {\n this.write(name, '', Date.now() - 86400000);\n }\n };\n })() :\n\n // Non standard browser env (web workers, react-native) lack needed support.\n (function nonStandardBrowserEnv() {\n return {\n write: function write() {},\n read: function read() { return null; },\n remove: function remove() {}\n };\n })()\n);\n","'use strict';\n\nvar Cancel = require('./Cancel');\n\n/**\n * A `CancelToken` is an object that can be used to request cancellation of an operation.\n *\n * @class\n * @param {Function} executor The executor function.\n */\nfunction CancelToken(executor) {\n if (typeof executor !== 'function') {\n throw new TypeError('executor must be a function.');\n }\n\n var resolvePromise;\n this.promise = new Promise(function promiseExecutor(resolve) {\n resolvePromise = resolve;\n });\n\n var token = this;\n executor(function cancel(message) {\n if (token.reason) {\n // Cancellation has already been requested\n return;\n }\n\n token.reason = new Cancel(message);\n resolvePromise(token.reason);\n });\n}\n\n/**\n * Throws a `Cancel` if cancellation has been requested.\n */\nCancelToken.prototype.throwIfRequested = function throwIfRequested() {\n if (this.reason) {\n throw this.reason;\n }\n};\n\n/**\n * Returns an object that contains a new `CancelToken` and a function that, when called,\n * cancels the `CancelToken`.\n */\nCancelToken.source = function source() {\n var cancel;\n var token = new CancelToken(function executor(c) {\n cancel = c;\n });\n return {\n token: token,\n cancel: cancel\n };\n};\n\nmodule.exports = CancelToken;\n","'use strict';\n\n/**\n * Syntactic sugar for invoking a function and expanding an array for arguments.\n *\n * Common use case would be to use `Function.prototype.apply`.\n *\n * ```js\n * function f(x, y, z) {}\n * var args = [1, 2, 3];\n * f.apply(null, args);\n * ```\n *\n * With `spread` this example can be re-written.\n *\n * ```js\n * spread(function(x, y, z) {})([1, 2, 3]);\n * ```\n *\n * @param {Function} callback\n * @returns {Function}\n */\nmodule.exports = function spread(callback) {\n return function wrap(arr) {\n return callback.apply(null, arr);\n };\n};\n","'use strict';\n\nmodule.exports = require('./core.js');\nrequire('./done.js');\nrequire('./finally.js');\nrequire('./es6-extensions.js');\nrequire('./node-extensions.js');\nrequire('./synchronous.js');\n","var g;\n\n// This works in non-strict mode\ng = (function() {\n\treturn this;\n})();\n\ntry {\n\t// This works if eval is allowed (see CSP)\n\tg = g || new Function(\"return this\")();\n} catch (e) {\n\t// This works if the window reference is available\n\tif (typeof window === \"object\") g = window;\n}\n\n// g can still be undefined, but nothing to do about it...\n// We return undefined, instead of nothing here, so it's\n// easier to handle this case. if(!global) { ...}\n\nmodule.exports = g;\n","'use strict';\n\nvar Promise = require('./core.js');\n\nmodule.exports = Promise;\nPromise.prototype.done = function (onFulfilled, onRejected) {\n var self = arguments.length ? this.then.apply(this, arguments) : this;\n self.then(null, function (err) {\n setTimeout(function () {\n throw err;\n }, 0);\n });\n};\n","'use strict';\n\nvar Promise = require('./core.js');\n\nmodule.exports = Promise;\nPromise.prototype.finally = function (f) {\n return this.then(function (value) {\n return Promise.resolve(f()).then(function () {\n return value;\n });\n }, function (err) {\n return Promise.resolve(f()).then(function () {\n throw err;\n });\n });\n};\n","'use strict';\n\n//This file contains the ES6 extensions to the core Promises/A+ API\n\nvar Promise = require('./core.js');\n\nmodule.exports = Promise;\n\n/* Static Functions */\n\nvar TRUE = valuePromise(true);\nvar FALSE = valuePromise(false);\nvar NULL = valuePromise(null);\nvar UNDEFINED = valuePromise(undefined);\nvar ZERO = valuePromise(0);\nvar EMPTYSTRING = valuePromise('');\n\nfunction valuePromise(value) {\n var p = new Promise(Promise._0);\n p._V = 1;\n p._W = value;\n return p;\n}\nPromise.resolve = function (value) {\n if (value instanceof Promise) return value;\n\n if (value === null) return NULL;\n if (value === undefined) return UNDEFINED;\n if (value === true) return TRUE;\n if (value === false) return FALSE;\n if (value === 0) return ZERO;\n if (value === '') return EMPTYSTRING;\n\n if (typeof value === 'object' || typeof value === 'function') {\n try {\n var then = value.then;\n if (typeof then === 'function') {\n return new Promise(then.bind(value));\n }\n } catch (ex) {\n return new Promise(function (resolve, reject) {\n reject(ex);\n });\n }\n }\n return valuePromise(value);\n};\n\nvar iterableToArray = function (iterable) {\n if (typeof Array.from === 'function') {\n // ES2015+, iterables exist\n iterableToArray = Array.from;\n return Array.from(iterable);\n }\n\n // ES5, only arrays and array-likes exist\n iterableToArray = function (x) { return Array.prototype.slice.call(x); };\n return Array.prototype.slice.call(iterable);\n}\n\nPromise.all = function (arr) {\n var args = iterableToArray(arr);\n\n return new Promise(function (resolve, reject) {\n if (args.length === 0) return resolve([]);\n var remaining = args.length;\n function res(i, val) {\n if (val && (typeof val === 'object' || typeof val === 'function')) {\n if (val instanceof Promise && val.then === Promise.prototype.then) {\n while (val._V === 3) {\n val = val._W;\n }\n if (val._V === 1) return res(i, val._W);\n if (val._V === 2) reject(val._W);\n val.then(function (val) {\n res(i, val);\n }, reject);\n return;\n } else {\n var then = val.then;\n if (typeof then === 'function') {\n var p = new Promise(then.bind(val));\n p.then(function (val) {\n res(i, val);\n }, reject);\n return;\n }\n }\n }\n args[i] = val;\n if (--remaining === 0) {\n resolve(args);\n }\n }\n for (var i = 0; i < args.length; i++) {\n res(i, args[i]);\n }\n });\n};\n\nPromise.reject = function (value) {\n return new Promise(function (resolve, reject) {\n reject(value);\n });\n};\n\nPromise.race = function (values) {\n return new Promise(function (resolve, reject) {\n iterableToArray(values).forEach(function(value){\n Promise.resolve(value).then(resolve, reject);\n });\n });\n};\n\n/* Prototype Methods */\n\nPromise.prototype['catch'] = function (onRejected) {\n return this.then(null, onRejected);\n};\n","'use strict';\n\n// This file contains then/promise specific extensions that are only useful\n// for node.js interop\n\nvar Promise = require('./core.js');\nvar asap = require('asap');\n\nmodule.exports = Promise;\n\n/* Static Functions */\n\nPromise.denodeify = function (fn, argumentCount) {\n if (\n typeof argumentCount === 'number' && argumentCount !== Infinity\n ) {\n return denodeifyWithCount(fn, argumentCount);\n } else {\n return denodeifyWithoutCount(fn);\n }\n};\n\nvar callbackFn = (\n 'function (err, res) {' +\n 'if (err) { rj(err); } else { rs(res); }' +\n '}'\n);\nfunction denodeifyWithCount(fn, argumentCount) {\n var args = [];\n for (var i = 0; i < argumentCount; i++) {\n args.push('a' + i);\n }\n var body = [\n 'return function (' + args.join(',') + ') {',\n 'var self = this;',\n 'return new Promise(function (rs, rj) {',\n 'var res = fn.call(',\n ['self'].concat(args).concat([callbackFn]).join(','),\n ');',\n 'if (res &&',\n '(typeof res === \"object\" || typeof res === \"function\") &&',\n 'typeof res.then === \"function\"',\n ') {rs(res);}',\n '});',\n '};'\n ].join('');\n return Function(['Promise', 'fn'], body)(Promise, fn);\n}\nfunction denodeifyWithoutCount(fn) {\n var fnLength = Math.max(fn.length - 1, 3);\n var args = [];\n for (var i = 0; i < fnLength; i++) {\n args.push('a' + i);\n }\n var body = [\n 'return function (' + args.join(',') + ') {',\n 'var self = this;',\n 'var args;',\n 'var argLength = arguments.length;',\n 'if (arguments.length > ' + fnLength + ') {',\n 'args = new Array(arguments.length + 1);',\n 'for (var i = 0; i < arguments.length; i++) {',\n 'args[i] = arguments[i];',\n '}',\n '}',\n 'return new Promise(function (rs, rj) {',\n 'var cb = ' + callbackFn + ';',\n 'var res;',\n 'switch (argLength) {',\n args.concat(['extra']).map(function (_, index) {\n return (\n 'case ' + (index) + ':' +\n 'res = fn.call(' + ['self'].concat(args.slice(0, index)).concat('cb').join(',') + ');' +\n 'break;'\n );\n }).join(''),\n 'default:',\n 'args[argLength] = cb;',\n 'res = fn.apply(self, args);',\n '}',\n \n 'if (res &&',\n '(typeof res === \"object\" || typeof res === \"function\") &&',\n 'typeof res.then === \"function\"',\n ') {rs(res);}',\n '});',\n '};'\n ].join('');\n\n return Function(\n ['Promise', 'fn'],\n body\n )(Promise, fn);\n}\n\nPromise.nodeify = function (fn) {\n return function () {\n var args = Array.prototype.slice.call(arguments);\n var callback =\n typeof args[args.length - 1] === 'function' ? args.pop() : null;\n var ctx = this;\n try {\n return fn.apply(this, arguments).nodeify(callback, ctx);\n } catch (ex) {\n if (callback === null || typeof callback == 'undefined') {\n return new Promise(function (resolve, reject) {\n reject(ex);\n });\n } else {\n asap(function () {\n callback.call(ctx, ex);\n })\n }\n }\n }\n};\n\nPromise.prototype.nodeify = function (callback, ctx) {\n if (typeof callback != 'function') return this;\n\n this.then(function (value) {\n asap(function () {\n callback.call(ctx, null, value);\n });\n }, function (err) {\n asap(function () {\n callback.call(ctx, err);\n });\n });\n};\n","\"use strict\";\n\n// rawAsap provides everything we need except exception management.\nvar rawAsap = require(\"./raw\");\n// RawTasks are recycled to reduce GC churn.\nvar freeTasks = [];\n// We queue errors to ensure they are thrown in right order (FIFO).\n// Array-as-queue is good enough here, since we are just dealing with exceptions.\nvar pendingErrors = [];\nvar requestErrorThrow = rawAsap.makeRequestCallFromTimer(throwFirstError);\n\nfunction throwFirstError() {\n if (pendingErrors.length) {\n throw pendingErrors.shift();\n }\n}\n\n/**\n * Calls a task as soon as possible after returning, in its own event, with priority\n * over other events like animation, reflow, and repaint. An error thrown from an\n * event will not interrupt, nor even substantially slow down the processing of\n * other events, but will be rather postponed to a lower priority event.\n * @param {{call}} task A callable object, typically a function that takes no\n * arguments.\n */\nmodule.exports = asap;\nfunction asap(task) {\n var rawTask;\n if (freeTasks.length) {\n rawTask = freeTasks.pop();\n } else {\n rawTask = new RawTask();\n }\n rawTask.task = task;\n rawAsap(rawTask);\n}\n\n// We wrap tasks with recyclable task objects. A task object implements\n// `call`, just like a function.\nfunction RawTask() {\n this.task = null;\n}\n\n// The sole purpose of wrapping the task is to catch the exception and recycle\n// the task object after its single use.\nRawTask.prototype.call = function () {\n try {\n this.task.call();\n } catch (error) {\n if (asap.onerror) {\n // This hook exists purely for testing purposes.\n // Its name will be periodically randomized to break any code that\n // depends on its existence.\n asap.onerror(error);\n } else {\n // In a web browser, exceptions are not fatal. However, to avoid\n // slowing down the queue of pending tasks, we rethrow the error in a\n // lower priority turn.\n pendingErrors.push(error);\n requestErrorThrow();\n }\n } finally {\n this.task = null;\n freeTasks[freeTasks.length] = this;\n }\n};\n","'use strict';\n\nvar Promise = require('./core.js');\n\nmodule.exports = Promise;\nPromise.enableSynchronous = function () {\n Promise.prototype.isPending = function() {\n return this.getState() == 0;\n };\n\n Promise.prototype.isFulfilled = function() {\n return this.getState() == 1;\n };\n\n Promise.prototype.isRejected = function() {\n return this.getState() == 2;\n };\n\n Promise.prototype.getValue = function () {\n if (this._V === 3) {\n return this._W.getValue();\n }\n\n if (!this.isFulfilled()) {\n throw new Error('Cannot get a value of an unfulfilled promise.');\n }\n\n return this._W;\n };\n\n Promise.prototype.getReason = function () {\n if (this._V === 3) {\n return this._W.getReason();\n }\n\n if (!this.isRejected()) {\n throw new Error('Cannot get a rejection reason of a non-rejected promise.');\n }\n\n return this._W;\n };\n\n Promise.prototype.getState = function () {\n if (this._V === 3) {\n return this._W.getState();\n }\n if (this._V === -1 || this._V === -2) {\n return 0;\n }\n\n return this._V;\n };\n};\n\nPromise.disableSynchronous = function() {\n Promise.prototype.isPending = undefined;\n Promise.prototype.isFulfilled = undefined;\n Promise.prototype.isRejected = undefined;\n Promise.prototype.getValue = undefined;\n Promise.prototype.getReason = undefined;\n Promise.prototype.getState = undefined;\n};\n","/**\n * @module Errors/Connectivity\n */\n\n/**\n * Define errors for Connectivity Test\n */\n\nimport { NetworkTestError } from '../../errors';\nimport { ErrorNames } from '../../errors/types';\n\n/**\n * Base class for errors used throughout Network Connectivity test.\n */\nexport class ConnectivityError extends NetworkTestError {\n constructor(message: string, name?: string) {\n super(message, name || ErrorNames.CONNECTIVITY_ERROR);\n }\n}\n\n/**\n * API Connectivity Error\n */\nexport class APIConnectivityError extends ConnectivityError {\n constructor() {\n const message = 'Failed to connect to OpenTOK API Server';\n super(message, ErrorNames.API_CONNECTIVITY_ERROR);\n }\n}\n\n/**\n * Session Errors\n */\nexport class ConnectToSessionError extends ConnectivityError {\n constructor(message?: string, name?: string) {\n const defaultMessage = 'Failed to connect to the session due to a network error.';\n super(message || defaultMessage, name || ErrorNames.CONNECT_TO_SESSION_ERROR);\n }\n}\n\nexport class ConnectToSessionTokenError extends ConnectToSessionError {\n constructor() {\n super('Failed to connect to the session due to an invalid token.',\n ErrorNames.CONNECT_TO_SESSION_TOKEN_ERROR);\n }\n}\n\nexport class ConnectToSessionSessionIdError extends ConnectToSessionError {\n constructor() {\n super('Failed to connect to the session due to an invalid session ID.',\n ErrorNames.CONNECT_TO_SESSION_ID_ERROR);\n }\n}\n\nexport class ConnectToSessionNetworkError extends ConnectToSessionError {\n constructor() {\n super('Failed to connect to the session due to a network error.',\n ErrorNames.CONNECT_TO_SESSION_NETWORK_ERROR);\n }\n}\n\n/**\n * Missing Device Errors\n */\n\nexport class MediaDeviceError extends ConnectivityError {\n constructor(message?: string, name?: string) {\n const defaultMessage = 'OpenTok failed to find media devices for this browser.';\n super(message || defaultMessage, name || ErrorNames.MEDIA_DEVICE_ERROR);\n }\n}\n\nexport class FailedToObtainMediaDevices extends MediaDeviceError {\n constructor() {\n super('Failed to obtain media devices.', ErrorNames.FAILED_TO_OBTAIN_MEDIA_DEVICES);\n }\n}\n\nexport class NoVideoCaptureDevicesError extends MediaDeviceError {\n constructor() {\n super('This browser has no video capture devices', ErrorNames.NO_VIDEO_CAPTURE_DEVICES);\n }\n}\n\nexport class NoAudioCaptureDevicesError extends MediaDeviceError {\n constructor() {\n super('This browser has no audio capture devices.', ErrorNames.NO_AUDIO_CAPTURE_DEVICES);\n }\n}\n\n/**\n * Publishing Errors\n */\n\nexport class PublishToSessionError extends ConnectivityError {\n constructor(message?: string, name?: string) {\n const defaultMessage = 'Encountered an unknown error while attempting to publish to a session.';\n super(message || defaultMessage, name || ErrorNames.PUBLISH_TO_SESSION_ERROR);\n }\n}\n\nexport class FailedMessagingServerTestError extends PublishToSessionError {\n constructor() {\n const message = 'Failed to connect to media server due to messaging server connection failure';\n super(message, ErrorNames.FAILED_MESSAGING_SERVER_TEST);\n }\n}\n\nexport class FailedToCreateLocalPublisher extends PublishToSessionError {\n constructor() {\n super('Failed to create a local publisher object.', ErrorNames.FAILED_TO_CREATE_LOCAL_PUBLISHER);\n }\n}\n\nexport class PublishToSessionNotConnectedError extends PublishToSessionError {\n constructor() {\n super('Precall failed to publish to the session because it was not connected.',\n ErrorNames.PUBLISH_TO_SESSION_NOT_CONNECTED);\n }\n}\n\nexport class PublishToSessionPermissionOrTimeoutError extends PublishToSessionError {\n constructor() {\n super('Precall failed to publish to the session due a permissions error or timeout.',\n ErrorNames.PUBLISH_TO_SESSION_PERMISSION_OR_TIMEOUT_ERROR);\n }\n}\n\nexport class PublishToSessionNetworkError extends PublishToSessionError {\n constructor() {\n super('Precall failed to publish to the session due a network error.',\n ErrorNames.PUBLISH_TO_SESSION_NETWORK_ERROR);\n }\n}\n\n/**\n * Subscribing Errors\n */\nexport class SubscribeToSessionError extends ConnectivityError {\n constructor(message?: string) {\n const defaultMessage = 'Encountered an unknown error while attempting to subscribe to a session.';\n super(message || defaultMessage, ErrorNames.SUBSCRIBE_TO_SESSION_ERROR);\n }\n}\n\n/**\n * Logger Server Error\n */\nexport class LoggingServerConnectionError extends ConnectivityError {\n constructor(){\n super('Failed to connect to the OpenTok logging server.', ErrorNames.LOGGING_SERVER_CONNECTION_ERROR);\n }\n}\n","/**\n * @module Errors/Connectivity/Mapping\n */\n\n/**\n * Map Connectivity Errors to Failure Types\n */\n\nimport { ConnectivityError } from './index';\nimport { ErrorNames } from '../../errors/types';\n\nexport enum FailureType {\n Api = 'api',\n Messaging = 'messaging',\n OpentokJs = 'OpenTok.js',\n Media = 'media',\n Logging = 'logging',\n ConnectivityError = 'OpenTok.js',\n}\n\nexport type FailureCase = {\n type: FailureType,\n error: ConnectivityError,\n};\n\nconst mapErrorToCase = (error: ConnectivityError): FailureCase => {\n\n const getType = (): FailureType => {\n switch (error.name) {\n case ErrorNames.API_CONNECTIVITY_ERROR:\n case ErrorNames.CONNECT_TO_SESSION_NETWORK_ERROR:\n return FailureType.Api;\n case ErrorNames.CONNECT_TO_SESSION_ERROR:\n case ErrorNames.CONNECT_TO_SESSION_TOKEN_ERROR:\n case ErrorNames.CONNECT_TO_SESSION_ID_ERROR:\n return FailureType.Messaging;\n case ErrorNames.MEDIA_DEVICE_ERROR:\n case ErrorNames.FAILED_TO_OBTAIN_MEDIA_DEVICES:\n case ErrorNames.NO_VIDEO_CAPTURE_DEVICES:\n case ErrorNames.NO_AUDIO_CAPTURE_DEVICES:\n case ErrorNames.FAILED_TO_CREATE_LOCAL_PUBLISHER:\n case ErrorNames.PUBLISH_TO_SESSION_NOT_CONNECTED:\n case ErrorNames.PUBLISH_TO_SESSION_PERMISSION_OR_TIMEOUT_ERROR:\n case ErrorNames.PUBLISH_TO_SESSION_NETWORK_ERROR:\n return FailureType.OpentokJs;\n case ErrorNames.PUBLISH_TO_SESSION_ERROR:\n case ErrorNames.SUBSCRIBE_TO_SESSION_ERROR:\n case ErrorNames.FAILED_MESSAGING_SERVER_TEST:\n return FailureType.Media;\n case ErrorNames.LOGGING_SERVER_CONNECTION_ERROR:\n return FailureType.Logging;\n default:\n return FailureType.OpentokJs;\n }\n };\n return { error, type: getType() };\n};\n\nexport const mapErrors = (...errors: ConnectivityError[]): FailureCase[] => errors.map(mapErrorToCase);\n","/**\n * @module Test/Publishing\n * @preferred\n *\n * Defines the methods required for the Publishing Test Flow\n */\n\n/**\n * Publishing Test Flow\n */\n\n/* tslint:disable */\nimport OTKAnalytics = require('opentok-solutions-logging');\n/* tslint:enable */\nimport * as Promise from 'promise';\nimport {\n NetworkTestOptions,\n} from '../index';\nimport { OT } from '../types/opentok';\nimport { AverageStats, AV, Bandwidth, HasAudioVideo } from './types/stats';\nimport { UpdateCallback, UpdateCallbackStats } from '../types/callbacks';\nimport { pick } from '../util';\nimport * as e from './errors/';\nimport { OTErrorType, errorHasName } from '../errors/types';\nimport subscriberMOS from './helpers/subscriberMOS';\nimport MOSState from './helpers/MOSState';\nimport config from './helpers/config';\nimport isSupportedBrowser from './helpers/isSupportedBrowser';\n\ninterface QualityTestResultsBuilder {\n state: MOSState;\n subscriber: OT.Subscriber;\n credentials: OT.SessionCredentials;\n mosScore?: number;\n bandwidth?: Bandwidth;\n}\n\nexport interface QualityTestResults extends HasAudioVideo { }\n\ntype MOSResultsCallback = (state: MOSState) => void;\ntype DeviceMap = { [deviceId: string]: OT.Device };\ntype AvailableDevices = { audio: DeviceMap, video: DeviceMap };\ntype PublisherSubscriber = { publisher: OT.Publisher, subscriber: OT.Subscriber };\n\nlet audioOnly = false; // By default, the initial test is audio-video\nlet testTimeout: number;\nlet stopTest: Function | undefined;\nlet stopTestTimeoutId: number;\nlet stopTestTimeoutCompleted = false;\nlet stopTestCalled = false;\n\n/**\n * If not already connected, connect to the OpenTok Session\n */\nfunction connectToSession(session: OT.Session, token: string): Promise {\n return new Promise((resolve, reject) => {\n if (session.connection) {\n resolve(session);\n } else {\n session.connect(token, (error?: OT.OTError) => {\n if (error) {\n if (errorHasName(error, OTErrorType.OT_AUTHENTICATION_ERROR)) {\n reject(new e.ConnectToSessionTokenError());\n } else if (errorHasName(error, OTErrorType.OT_INVALID_SESSION_ID)) {\n reject(new e.ConnectToSessionSessionIdError());\n } else if (errorHasName(error, OTErrorType.OT_CONNECT_FAILED)) {\n reject(new e.ConnectToSessionNetworkError());\n } else {\n reject(new e.ConnectToSessionError());\n }\n }\n resolve(session);\n });\n }\n });\n}\n\n/**\n * Ensure that audio and video devices are available\n */\nfunction validateDevices(OT: OT.Client): Promise {\n return new Promise((resolve, reject) => {\n OT.getDevices((error?: OT.OTError, devices: OT.Device[] = []) => {\n\n if (error) {\n reject(new e.FailedToObtainMediaDevices());\n } else {\n\n const availableDevices: AvailableDevices = devices.reduce(\n (acc: AvailableDevices, device: OT.Device) => {\n const type: AV = device.kind === 'audioInput' ? 'audio' : 'video';\n return { ...acc, [type]: { ...acc[type], [device.deviceId]: device } };\n },\n { audio: {}, video: {} },\n );\n\n if (!Object.keys(availableDevices.audio).length) {\n reject(new e.NoAudioCaptureDevicesError());\n } else {\n resolve(availableDevices);\n }\n }\n });\n });\n}\n\n/**\n * Create a test publisher and subscribe to the publihser's stream\n */\nfunction publishAndSubscribe(OT: OT.Client, options?: NetworkTestOptions) {\n return (session: OT.Session): Promise =>\n new Promise((resolve, reject) => {\n type StreamCreatedEvent = OT.Event<'streamCreated', OT.Publisher> & { stream: OT.Stream };\n const containerDiv = document.createElement('div');\n containerDiv.style.position = 'fixed';\n containerDiv.style.bottom = '-1px';\n containerDiv.style.width = '1px';\n containerDiv.style.height = '1px';\n containerDiv.style.opacity = '0';\n document.body.appendChild(containerDiv);\n validateDevices(OT)\n .then((availableDevices: AvailableDevices) => {\n if (!Object.keys(availableDevices.video).length) {\n audioOnly = true;\n }\n const publisherOptions: OT.PublisherProperties = {\n resolution: '1280x720',\n width: '100%',\n height: '100%',\n insertMode: 'append',\n showControls: false,\n };\n if (options && options.audioSource) {\n publisherOptions.audioSource = options.audioSource;\n }\n if (options && options.videoSource) {\n publisherOptions.videoSource = options.videoSource;\n }\n if (audioOnly) {\n publisherOptions.videoSource = null;\n }\n const publisher = OT.initPublisher(containerDiv, publisherOptions, (error?: OT.OTError) => {\n if (error) {\n reject(new e.InitPublisherError(error.message));\n } else {\n session.publish(publisher, (publishError?: OT.OTError) => {\n if (publishError) {\n if (errorHasName(publishError, OTErrorType.NOT_CONNECTED)) {\n return reject(new e.PublishToSessionNotConnectedError());\n }\n if (errorHasName(publishError, OTErrorType.UNABLE_TO_PUBLISH)) {\n return reject(new e.PublishToSessionPermissionOrTimeoutError());\n }\n return reject(new e.PublishToSessionError());\n // return reject(new e.PublishToSessionError(publishError.message));\n }\n });\n }\n });\n publisher.on('streamCreated', (event: StreamCreatedEvent) => {\n const subscriber =\n session.subscribe(event.stream,\n containerDiv,\n { testNetwork: true, insertMode: 'append' },\n (subscribeError?: OT.OTError) => {\n return subscribeError ?\n reject(new e.SubscribeToSessionError(subscribeError.message)) :\n resolve({ publisher, subscriber });\n });\n });\n })\n .catch(reject);\n });\n}\n/**\n * Connect to the OpenTok session, create a publisher, and subsribe to the publisher's stream\n */\nfunction subscribeToTestStream(\n OT: OT.Client,\n session: OT.Session,\n credentials: OT.SessionCredentials,\n options?: NetworkTestOptions): Promise {\n return new Promise((resolve, reject) => {\n connectToSession(session, credentials.token)\n .then(publishAndSubscribe(OT, options))\n .then(resolve)\n .catch(reject);\n });\n}\n\nfunction buildResults(builder: QualityTestResultsBuilder): QualityTestResults {\n const baseProps: (keyof AverageStats)[] = ['bitrate', 'packetLossRatio', 'supported', 'reason', 'mos'];\n builder.state.stats.audio.mos = builder.state.audioQualityScore();\n builder.state.stats.video.mos = builder.state.videoQualityScore();\n return {\n audio: pick(baseProps, builder.state.stats.audio),\n video: pick(baseProps.concat(['frameRate', 'recommendedResolution', 'recommendedFrameRate']),\n builder.state.stats.video),\n };\n}\n\nfunction isAudioQualityAcceptable(results: QualityTestResults): boolean {\n return !!results.audio.bitrate && (results.audio.bitrate > config.qualityThresholds.audio[0].bps)\n && (!!results.audio.packetLossRatio &&\n (results.audio.packetLossRatio < config.qualityThresholds.audio[0].plr)\n || results.audio.packetLossRatio === 0);\n}\n\n/**\n * Clean subscriber objects before disconnecting from the session\n * @param session\n * @param subscriber\n */\nfunction cleanSubscriber(session: OT.Session, subscriber: OT.Subscriber) {\n return new Promise((resolve, reject) => {\n subscriber.on('destroyed', () => {\n resolve();\n });\n if (!subscriber) {\n resolve();\n }\n session.unsubscribe(subscriber);\n });\n}\n\n/**\n * Clean publisher objects before disconnecting from the session\n * @param publisher\n */\nfunction cleanPublisher(publisher: OT.Publisher) {\n return new Promise((resolve, reject) => {\n publisher.on('destroyed', () => {\n resolve();\n });\n if (!publisher) {\n resolve();\n }\n publisher.destroy();\n });\n}\n\nfunction checkSubscriberQuality(\n OT: OT.Client,\n session: OT.Session,\n credentials: OT.SessionCredentials,\n options?: NetworkTestOptions,\n onUpdate?: UpdateCallback,\n audioOnlyFallback?: boolean,\n): Promise {\n\n let mosEstimatorTimeoutId: number;\n\n return new Promise((resolve, reject) => {\n subscribeToTestStream(OT, session, credentials, options)\n .then(({ publisher, subscriber }: PublisherSubscriber) => {\n if (!subscriber) {\n reject(new e.MissingSubscriberError());\n } else {\n try {\n const builder: QualityTestResultsBuilder = {\n state: new MOSState(audioOnlyFallback),\n ... { subscriber },\n ... { credentials },\n };\n\n const getStatsListener = (error?: OT.OTError, stats?: OT.SubscriberStats) => {\n const updateStats = (subscriberStats: OT.SubscriberStats): UpdateCallbackStats => ({\n ...subscriberStats,\n phase: audioOnly ? 'audio-only' : 'audio-video',\n });\n stats && onUpdate && onUpdate(updateStats(stats));\n };\n\n const processResults = () => {\n const audioVideoResults: QualityTestResults = buildResults(builder);\n if (!audioOnly && !isAudioQualityAcceptable(audioVideoResults)) {\n audioOnly = true;\n checkSubscriberQuality(OT, session, credentials, options, onUpdate, true)\n .then((results: QualityTestResults) => {\n resolve(results);\n });\n } else {\n session.on('sessionDisconnected', () => {\n resolve(audioVideoResults);\n session.off();\n });\n cleanSubscriber(session, subscriber)\n .then(() => {\n if (options && options.skipPublisherCleaningOnSuccess) return Promise.resolve();\n\n return cleanPublisher(publisher);\n })\n .then(() => session.disconnect());\n }\n };\n\n stopTest = () => {\n processResults();\n };\n\n const resultsCallback: MOSResultsCallback = (state: MOSState) => {\n clearTimeout(mosEstimatorTimeoutId);\n processResults();\n };\n\n subscriberMOS(builder.state, subscriber, getStatsListener, resultsCallback);\n\n mosEstimatorTimeoutId = window.setTimeout(processResults, testTimeout);\n\n window.clearTimeout(stopTestTimeoutId);\n stopTestTimeoutId = window.setTimeout(() => {\n stopTestTimeoutCompleted = true;\n if (stopTestCalled && stopTest) {\n stopTest();\n }\n }, 5000);\n\n } catch (exception) {\n reject(new e.SubscriberGetStatsError());\n }\n }\n })\n .catch(reject);\n });\n}\n\n/**\n * Ensure that the test is being run in a supported browser.\n */\nfunction validateBrowser(): Promise {\n return new Promise((resolve, reject) => {\n const { supported, browser } = isSupportedBrowser();\n return supported ? resolve() : reject(new e.UnsupportedBrowserError(browser));\n });\n}\n\n/**\n * This method checks to see if the client can publish to an OpenTok session.\n */\nexport function testQuality(\n OT: OT.Client,\n credentials: OT.SessionCredentials,\n otLogging: OTKAnalytics,\n options?: NetworkTestOptions,\n onUpdate?: UpdateCallback,\n): Promise {\n stopTestTimeoutCompleted = false;\n stopTestCalled = false;\n return new Promise((resolve, reject) => {\n\n audioOnly = !!(options && options.audioOnly);\n testTimeout = audioOnly ? config.getStatsAudioOnlyDuration :\n config.getStatsVideoAndAudioTestDuration;\n if (options && options.timeout) {\n testTimeout = Math.min(testTimeout, options.timeout, 30000);\n }\n const onSuccess = (results: QualityTestResults) => {\n stopTest = undefined;\n otLogging.logEvent({ action: 'testQuality', variation: 'Success' });\n resolve(results);\n };\n\n const onError = (error: Error) => {\n stopTest = undefined;\n otLogging.logEvent({ action: 'testQuality', variation: 'Failure' });\n reject(error);\n };\n\n validateBrowser()\n .then(() => {\n let sessionOptions: OT.InitSessionOptions = {};\n if (options && options.initSessionOptions) {\n sessionOptions = options.initSessionOptions;\n }\n if (options && options.proxyServerUrl) {\n if (!OT.hasOwnProperty('setProxyUrl')) { // Fallback for OT.version < 2.17.4\n sessionOptions.proxyUrl = options.proxyServerUrl;\n }\n }\n const session = OT.initSession(credentials.apiKey, credentials.sessionId, sessionOptions);\n checkSubscriberQuality(OT, session, credentials, options, onUpdate)\n .then(onSuccess)\n .catch(onError);\n })\n .catch(onError);\n });\n}\n\nexport function stopQualityTest() {\n stopTestCalled = true;\n if (stopTestTimeoutCompleted && stopTest) {\n stopTest();\n }\n}\n","/**\n * @module Errors/Quality\n */\n\n/**\n * Define errors for Connectivity Test\n */\n\nimport { NetworkTestError } from '../../errors';\nimport { ErrorNames } from '../../errors/types';\n\n /**\n * Base class for errors used throughout Network Quality test.\n */\nexport class QualityTestError extends NetworkTestError {\n constructor(message: string, name: string) {\n super(message, name || ErrorNames.QUALITY_TEST_ERROR);\n }\n}\n\n/**\n * Browser Error\n */\nexport class UnsupportedBrowserError extends QualityTestError {\n constructor(browser: string) {\n const message =\n `Your current browser (${browser}) does not support the audio-video quality test. Please run the test in a supported browser.`;\n super(message, ErrorNames.UNSUPPORTED_BROWSER);\n }\n}\n\n/**\n * Session Errors\n */\nexport class ConnectToSessionError extends QualityTestError {\n constructor(message?: string, name?: string) {\n const defaultMessage = 'Failed to connect to the session due to a network error.';\n super(message || defaultMessage, name || ErrorNames.CONNECT_TO_SESSION_ERROR);\n }\n}\n\nexport class ConnectToSessionTokenError extends ConnectToSessionError {\n constructor() {\n super('Failed to connect to the session due to an invalid token.',\n ErrorNames.CONNECT_TO_SESSION_TOKEN_ERROR);\n }\n}\n\nexport class ConnectToSessionSessionIdError extends ConnectToSessionError {\n constructor() {\n super('Failed to connect to the session due to an invalid session ID.',\n ErrorNames.CONNECT_TO_SESSION_ID_ERROR);\n }\n}\n\nexport class ConnectToSessionNetworkError extends ConnectToSessionError {\n constructor() {\n super('Failed to connect to the session due to a network error.',\n ErrorNames.CONNECT_TO_SESSION_NETWORK_ERROR);\n }\n}\n\n/**\n * Missing Device Errors\n */\n\nexport class MediaDeviceError extends QualityTestError {\n constructor(message?: string, name?: string) {\n const defaultMessage = 'OpenTok failed to find media devices for this browser.';\n super(message || defaultMessage, name || ErrorNames.MEDIA_DEVICE_ERROR);\n }\n}\n\nexport class FailedToObtainMediaDevices extends QualityTestError {\n constructor() {\n super('Failed to obtain media devices.', ErrorNames.FAILED_TO_OBTAIN_MEDIA_DEVICES);\n }\n}\n\nexport class NoVideoCaptureDevicesError extends QualityTestError {\n constructor() {\n super('This browser has no video capture devices', ErrorNames.NO_VIDEO_CAPTURE_DEVICES);\n }\n}\n\nexport class NoAudioCaptureDevicesError extends QualityTestError {\n constructor() {\n super('This browser has no audio capture devices.', ErrorNames.NO_AUDIO_CAPTURE_DEVICES);\n }\n}\n\n/**\n * Publisher Errors\n */\n\nexport class PublishToSessionError extends QualityTestError {\n constructor(message?: string, name?: string) {\n const defaultMessage = 'Encountered an unknown error while attempting to publish to a session.';\n super(message || defaultMessage, name || ErrorNames.PUBLISH_TO_SESSION_ERROR);\n }\n}\n\nexport class InitPublisherError extends PublishToSessionError {\n constructor(message?: string) {\n super(message || 'Failed to initialize publisher.', ErrorNames.INIT_PUBLISHER_ERROR);\n }\n}\n\nexport class PublishToSessionNotConnectedError extends PublishToSessionError {\n constructor() {\n super('Precall failed to publish to the session because it was not connected.',\n ErrorNames.PUBLISH_TO_SESSION_NOT_CONNECTED);\n }\n}\n\nexport class PublishToSessionPermissionOrTimeoutError extends PublishToSessionError {\n constructor() {\n super('Precall failed to publish to the session due a permissions error or timeout.',\n ErrorNames.PUBLISH_TO_SESSION_PERMISSION_OR_TIMEOUT_ERROR);\n }\n}\n\n/**\n * Subscriber Errors\n */\nexport class SubscribeToSessionError extends QualityTestError {\n constructor(message?: string, name?: string) {\n const defaultMessage = 'Encountered an unknown error while attempting to publish to a session.';\n super(message || defaultMessage, name || ErrorNames.SUBSCRIBE_TO_SESSION_ERROR);\n }\n}\n\nexport class SubscriberGetStatsError extends SubscribeToSessionError {\n constructor() {\n super('Failed to get network stats for a subscriber.', ErrorNames.SUBSCRIBER_GET_STATS_ERROR);\n }\n}\n\nexport class MissingSubscriberError extends SubscribeToSessionError {\n constructor() {\n super('Call checkSubscribeToSession before calling checkSubscriberQuality.',\n ErrorNames.MISSING_SUBSCRIBER_ERROR);\n }\n}\n","import isBitrateSteadyState from './isBitrateSteadyState';\nimport calculateThroughput from './calculateThroughput';\nimport MOSState from './MOSState';\nimport { OT } from '../../types/opentok';\nimport { AV } from '../types/stats';\nimport { getOr, last, nth } from '../../util';\n\nexport type StatsListener = (error?: OT.OTError, stats?: OT.SubscriberStats) => void;\n\nconst getPacketsLost = (ts: OT.TrackStats): number => getOr(0, 'packetsLost', ts);\nconst getPacketsReceived = (ts: OT.TrackStats): number => getOr(0, 'packetsReceived', ts);\nconst getTotalPackets = (ts: OT.TrackStats): number => getPacketsLost(ts) + getPacketsReceived(ts);\nconst calculateTotalPackets = (type: AV, current: OT.SubscriberStats, last: OT.SubscriberStats) =>\n getTotalPackets(current[type]) - getTotalPackets(last[type]);\nconst calculateBitRate = (type: AV, current: OT.SubscriberStats, last: OT.SubscriberStats): number => {\n const interval = current.timestamp - last.timestamp;\n return current[type] && current[type].bytesReceived ?\n (8 * (current[type].bytesReceived - last[type].bytesReceived)) / (interval / 1000) : 0;\n};\n\nfunction calculateVideoScore(subscriber: OT.Subscriber, stats: OT.SubscriberStats[]): number {\n const MIN_VIDEO_BITRATE = 30000;\n const targetBitrateForPixelCount = (pixelCount: number) => {\n // power function maps resolution to target bitrate, based on rumor config\n // values, with r^2 = 0.98. We're ignoring frame rate, assume 30.\n const y = 2.069924867 * (Math.log10(pixelCount) ** 0.6250223771);\n return 10 ** y;\n };\n\n const currentStats = last(stats);\n const lastStats = nth(-2, stats);\n\n if (!currentStats || !lastStats || !subscriber.stream) {\n return 1;\n }\n\n const totalPackets = calculateTotalPackets('video', currentStats, lastStats);\n const packetLoss = getPacketsLost(currentStats.video) - getPacketsLost(lastStats.video) / totalPackets;\n const interval = currentStats.timestamp - lastStats.timestamp;\n const baseBitrate = calculateBitRate('video', currentStats, lastStats);\n const pixelCount = subscriber.stream.videoDimensions.width * subscriber.stream.videoDimensions.height;\n const targetBitrate = targetBitrateForPixelCount(pixelCount);\n\n if (baseBitrate < MIN_VIDEO_BITRATE) {\n return 1;\n }\n const bitrate = Math.min(baseBitrate, targetBitrate);\n\n let score =\n ((Math.log(bitrate / MIN_VIDEO_BITRATE) / Math.log(targetBitrate / MIN_VIDEO_BITRATE)) * 4) + 1;\n score = Math.min(score, 4.5);\n return score;\n}\n\nfunction calculateAudioScore(subscriber: OT.Subscriber, stats: OT.SubscriberStats[]): number {\n\n const audioScore = (roundTripTime: number, packetLossRatio: number) => {\n const LOCAL_DELAY = 20; // 20 msecs: typical frame duration\n const h = (x: number): number => x < 0 ? 0 : 1;\n const a = 0; // ILBC: a=10\n const b = 19.8;\n const c = 29.7;\n\n /**\n * Calculate the transmission rating factor, R\n */\n const calculateR = (): number => {\n const d = roundTripTime + LOCAL_DELAY;\n const delayImpairment = ((0.024 * d) + 0.11) * (d - 177.3) * h(d - 177.3);\n const equipmentImpairment = (a + b) * Math.log(1 + (c * packetLossRatio));\n return 94.2 - delayImpairment - equipmentImpairment;\n };\n\n /**\n * Calculate the Mean Opinion Score based on R\n */\n const calculateMOS = (R: number): number => {\n if (R < 0) {\n return 1;\n }\n if (R > 100) {\n return 4.5;\n }\n return 1 + (0.035 * R) + ((7.10 / 1000000) * R) * (R - 60) * (100 - R);\n };\n\n return calculateMOS(calculateR());\n };\n\n const currentStats = last(stats);\n const lastStats = nth(-2, stats);\n\n if (!currentStats || !lastStats || !subscriber.stream) {\n return 0;\n }\n\n const totalAudioPackets = calculateTotalPackets('audio', currentStats, lastStats);\n if (totalAudioPackets === 0) {\n return 0;\n }\n const packetLossRatio = (getPacketsLost(currentStats.audio) - getPacketsLost(lastStats.audio))/ totalAudioPackets;\n return audioScore(0, packetLossRatio);\n}\n\nexport default function subscriberMOS(\n mosState: MOSState,\n subscriber: OT.Subscriber,\n getStatsListener: StatsListener,\n callback: (state: MOSState) => void) {\n mosState.intervalId = window.setInterval(\n () => {\n subscriber.getStats((error?: OT.OTError, stats?: OT.SubscriberStats) => {\n if (!stats) {\n return null;\n }\n\n /**\n * We occaisionally start to receive faulty stat during long-running\n * tests. If this occurs, let's end the test early and report the\n * results as they are, as we should have sufficient data to\n * calculate a score at this point.\n *\n * We know that we're receiving \"faulty\" stats when we see a negative\n * value for bytesReceived.\n */\n const getPacketsLost = (ts: OT.TrackStats): number => getOr(0, 'packetsLost', ts);\n if (stats.audio.bytesReceived < 0 || getOr(1, 'video.bytesReceived', stats) < 0) {\n mosState.clearInterval();\n return callback(mosState);\n }\n\n stats && mosState.statsLog.push(stats);\n\n if (getStatsListener && typeof getStatsListener === 'function') {\n getStatsListener(error, stats);\n }\n\n if (mosState.statsLog.length < 2) {\n return null;\n }\n\n mosState.stats = calculateThroughput(mosState);\n const videoScore = calculateVideoScore(subscriber, mosState.statsLog);\n mosState.videoScoresLog.push(videoScore);\n const audioScore = calculateAudioScore(subscriber, mosState.statsLog);\n mosState.audioScoresLog.push(audioScore);\n\n mosState.pruneScores();\n\n // If bandwidth has reached a steady state, end the test early\n if (isBitrateSteadyState(mosState.statsLog)) {\n mosState.clearInterval();\n return callback(mosState);\n }\n\n return null;\n });\n }, MOSState.scoreInterval);\n\n subscriber.on('destroyed', mosState.clearInterval.bind(mosState));\n return mosState;\n}\n","import getLatestSampleWindow from './getLatestSampleWindow';\nimport calculateQualityStats from './calculateQualityStats';\nimport config from './config';\nimport { AV } from '../types/stats';\nimport { OT } from '../../types/opentok';\n\nexport default function isBitrateSteadyState(statsList: OT.SubscriberStats[]): boolean {\n const latestSamples = getLatestSampleWindow(statsList);\n const steadyStateAllowedDelta = config.steadyStateAllowedDelta;\n let isSteadyState = true;\n\n if (latestSamples.length < config.minimumVideoAndAudioTestSampleSize) {\n return false;\n }\n\n const statsBitrates = calculateQualityStats(latestSamples);\n\n const avTypes: AV[] = ['video', 'audio'];\n avTypes.forEach((avType: 'audio' | 'video') => {\n for (let i = 1; i < statsBitrates[avType].length; i += 1) {\n const currBitrate = statsBitrates[avType][i].averageBitrate;\n const prevBitrate = statsBitrates[avType][i - 1].averageBitrate;\n const bitrateDelta = currBitrate - prevBitrate;\n const allowableBitrateDelta = (prevBitrate * steadyStateAllowedDelta);\n\n if (bitrateDelta > allowableBitrateDelta) {\n isSteadyState = false;\n }\n }\n });\n\n return isSteadyState;\n}\n","import getLatestSampleWindow from './getLatestSampleWindow';\nimport calculateQualityStats from './calculateQualityStats';\nimport getQualityEvaluation from './getQualityEvaluation';\nimport { AV, AverageStats, AverageStatsBase, HasAudioVideo, QualityStats } from '../types/stats';\nimport config from './config';\nimport MOSState from './MOSState';\nimport { getOr } from '../../util';\n\nfunction getAverageBitrateAndPlr(type: AV, statsList: QualityStats[]): AverageStats {\n let sumBps = 0;\n let sumPlr = 0;\n let sumFrameRate = 0;\n\n statsList.forEach((stat) => {\n sumBps += stat.averageBitrate;\n sumPlr += stat.packetLossRatio;\n if (type === 'video') {\n sumFrameRate += Number(getOr(0, 'frameRate', stat));\n }\n });\n\n const averageStats: AverageStatsBase = {\n bitrate: sumBps / statsList.length,\n packetLossRatio: sumPlr / statsList.length,\n };\n const { supported, reason, recommendedResolution, recommendedFrameRate } =\n getQualityEvaluation(averageStats, type);\n const videoStats =\n type === 'video' ? {\n recommendedResolution,\n recommendedFrameRate,\n frameRate: sumFrameRate / statsList.length,\n } : {};\n\n return { ...averageStats, supported, reason, ...videoStats };\n}\n\nexport default function calculateThroughput(state: MOSState): HasAudioVideo {\n\n const sampleWindow = getLatestSampleWindow(state.statsLog);\n const qualityStats = calculateQualityStats(sampleWindow);\n\n const averageAudioStats = () => {\n if (!state.hasAudioTrack()) {\n return {\n supported: false,\n reason: config.strings.noMic,\n };\n }\n return getAverageBitrateAndPlr('audio', qualityStats.audio);\n };\n\n const averageVideoStats = () => {\n if (state.audioOnlyFallback) {\n return {\n supported: false,\n reason: config.strings.bandwidthLow,\n };\n }\n if (!state.hasVideoTrack()) {\n return {\n supported: false,\n reason: config.strings.noCam,\n };\n }\n return getAverageBitrateAndPlr('video', qualityStats.video);\n };\n\n return {\n audio: averageAudioStats(),\n video: averageVideoStats(),\n };\n}\n","import config from './config';\nimport { get } from '../../util';\nimport { AV, AverageStatsBase } from '../types/stats';\n\nexport interface QualityEvaluationResults{\n supported: boolean;\n recommendedFrameRate?: number;\n recommendedResolution?: string;\n reason?: string;\n}\n\nexport default function getQualityEvaluation(stats: AverageStatsBase, type: AV): QualityEvaluationResults {\n const qualityThresholds = config.qualityThresholds;\n const bitrate = stats.bitrate;\n const packetLoss = stats.packetLossRatio;\n const thresholds = qualityThresholds[type];\n let supported = false;\n let recommendedFrameRate : number = 30;\n let recommendedResolution : string = '';\n let recommendedSetting : string;\n\n for (let i = 0; i < thresholds.length; i += 1) {\n const threshold = thresholds[i];\n if (bitrate >= threshold.bps && packetLoss <= threshold.plr) {\n supported = true;\n if (type === 'video') {\n recommendedSetting = get('recommendedSetting', threshold);\n // recommendedSetting is of the form '640x480 @ 30FPS'\n recommendedFrameRate = Number(recommendedSetting\n .substring(recommendedSetting.indexOf('@') + 1).replace('FPS', ''));\n recommendedResolution =\n recommendedSetting.substring(0, recommendedSetting.indexOf('@') - 1);\n }\n break;\n }\n }\n\n const result: QualityEvaluationResults = {\n supported,\n recommendedFrameRate,\n recommendedResolution,\n };\n\n if (!supported) {\n result.reason = config.strings.bandwidthLow;\n } else if (supported && type === 'video') {\n result.recommendedFrameRate = recommendedFrameRate;\n result.recommendedResolution = recommendedResolution;\n }\n\n return result;\n}\n","import { get } from '../../util';\n\nexport type Browser =\n 'Chrome' |\n 'Firefox' |\n 'not a browser' |\n 'unsupported browser' |\n 'WebKit browser without WebRTC support' |\n 'Safari' |\n 'Internet Explorer' |\n 'Edge' |\n 'non-Chromium Edge' |\n 'Opera';\n\nfunction detectBrowser(): Browser {\n\n const navigator = window && window.navigator;\n\n // Fail early if it's not a browser\n if (typeof window === 'undefined' || !window.navigator) {\n return 'not a browser';\n }\n\n // Firefox.\n if (get('mozGetUserMedia', navigator)) {\n return 'Firefox';\n }\n if (get('webkitGetUserMedia', navigator)) {\n // Chrome, Chromium, Webview, Opera, and Edge 79+ all use the chrome shim\n if (window.hasOwnProperty('webkitRTCPeerConnection')) {\n if (navigator.userAgent.match(/Edg/)) {\n return 'Edge';\n }\n if (navigator.userAgent.match(/Opera|OPR\\//)) {\n return 'Opera';\n }\n return 'Chrome';\n }\n if (navigator.userAgent.match(/Version\\/(\\d+).(\\d+)/)) {\n return 'Safari';\n }\n return 'WebKit browser without WebRTC support';\n }\n\n if (navigator.mediaDevices && navigator.userAgent.match(/edge\\/(\\d+).(\\d+)$/)) { // Edge.\n return 'non-Chromium Edge';\n }\n\n if (navigator.userAgent.indexOf('MSIE ') > 0 ||\n !!navigator.userAgent.match(/Trident.*rv\\:11\\./)) {\n return 'Internet Explorer';\n }\n\n if (navigator.mediaDevices && navigator.userAgent.match(/AppleWebKit\\/(\\d+)\\./)) {\n // Safari, with webkitGetUserMedia removed.\n return 'Safari';\n }\n // Default fallthrough: not supported.\n return 'unsupported browser';\n}\n\nexport default function isSupportedBrowser(): { supported: boolean, browser: Browser } {\n const supportedBrowsers = ['Chrome', 'Firefox', 'Internet Explorer', 'Safari', 'Edge'];\n const browser = detectBrowser();\n const supported = supportedBrowsers.indexOf(browser) > -1;\n return { browser, supported };\n}\n","function _classCallCheck(n,e){if(!(n instanceof e))throw new TypeError(\"Cannot call a class as a function\")}var _createClass=function(){function n(n,e){for(var t=0;t FailureCase[]; diff --git a/dist/NetworkTest/testConnectivity/index.d.ts b/dist/NetworkTest/testConnectivity/index.d.ts new file mode 100644 index 00000000..886f3b68 --- /dev/null +++ b/dist/NetworkTest/testConnectivity/index.d.ts @@ -0,0 +1,19 @@ +/** + * @module Test/Connectivity + * @preferred + * + * Defines the methods required for the Connectivity Test Flow + */ +import * as Promise from 'promise'; +import OTKAnalytics = require('opentok-solutions-logging'); +import { NetworkTestOptions } from '../index'; +import { OT } from '../types/opentok'; +import { FailureCase } from './errors/mapping'; +export declare type ConnectivityTestResults = { + success: boolean; + failedTests: FailureCase[]; +}; +/** + * This method checks to see if the client can connect to TokBox servers required for using OpenTok + */ +export declare function testConnectivity(OT: OT.Client, credentials: OT.SessionCredentials, otLogging: OTKAnalytics, options?: NetworkTestOptions): Promise; diff --git a/dist/NetworkTest/testQuality/errors/index.d.ts b/dist/NetworkTest/testQuality/errors/index.d.ts new file mode 100644 index 00000000..0c6487f1 --- /dev/null +++ b/dist/NetworkTest/testQuality/errors/index.d.ts @@ -0,0 +1,76 @@ +/** + * @module Errors/Quality + */ +/** + * Define errors for Connectivity Test + */ +import { NetworkTestError } from '../../errors'; +/** + * Base class for errors used throughout Network Quality test. + */ +export declare class QualityTestError extends NetworkTestError { + constructor(message: string, name: string); +} +/** + * Browser Error + */ +export declare class UnsupportedBrowserError extends QualityTestError { + constructor(browser: string); +} +/** + * Session Errors + */ +export declare class ConnectToSessionError extends QualityTestError { + constructor(message?: string, name?: string); +} +export declare class ConnectToSessionTokenError extends ConnectToSessionError { + constructor(); +} +export declare class ConnectToSessionSessionIdError extends ConnectToSessionError { + constructor(); +} +export declare class ConnectToSessionNetworkError extends ConnectToSessionError { + constructor(); +} +/** + * Missing Device Errors + */ +export declare class MediaDeviceError extends QualityTestError { + constructor(message?: string, name?: string); +} +export declare class FailedToObtainMediaDevices extends QualityTestError { + constructor(); +} +export declare class NoVideoCaptureDevicesError extends QualityTestError { + constructor(); +} +export declare class NoAudioCaptureDevicesError extends QualityTestError { + constructor(); +} +/** + * Publisher Errors + */ +export declare class PublishToSessionError extends QualityTestError { + constructor(message?: string, name?: string); +} +export declare class InitPublisherError extends PublishToSessionError { + constructor(message?: string); +} +export declare class PublishToSessionNotConnectedError extends PublishToSessionError { + constructor(); +} +export declare class PublishToSessionPermissionOrTimeoutError extends PublishToSessionError { + constructor(); +} +/** + * Subscriber Errors + */ +export declare class SubscribeToSessionError extends QualityTestError { + constructor(message?: string, name?: string); +} +export declare class SubscriberGetStatsError extends SubscribeToSessionError { + constructor(); +} +export declare class MissingSubscriberError extends SubscribeToSessionError { + constructor(); +} diff --git a/dist/NetworkTest/testQuality/helpers/MOSState.d.ts b/dist/NetworkTest/testQuality/helpers/MOSState.d.ts new file mode 100644 index 00000000..d4566e39 --- /dev/null +++ b/dist/NetworkTest/testQuality/helpers/MOSState.d.ts @@ -0,0 +1,24 @@ +import { OT } from '../../types/opentok'; +import { AverageStats, Bandwidth, HasAudioVideo } from '../types/stats'; +export default class MOSState { + statsLog: OT.SubscriberStats[]; + audioScoresLog: number[]; + videoScoresLog: number[]; + stats: HasAudioVideo; + bandwidth: Bandwidth; + intervalId?: number; + audioOnlyFallback: boolean; + constructor(audioOnly?: boolean); + static readonly maxLogLength: number; + static readonly scoreInterval: number; + readonly hasAudioTrack: () => boolean; + readonly hasVideoTrack: () => boolean; + private audioScore; + private videoScore; + clearInterval(): void; + private pruneAudioScores; + private pruneVideoScores; + pruneScores(): void; + audioQualityScore(): number; + videoQualityScore(): number; +} diff --git a/dist/NetworkTest/testQuality/helpers/calculateQualityStats.d.ts b/dist/NetworkTest/testQuality/helpers/calculateQualityStats.d.ts new file mode 100644 index 00000000..8c41cdad --- /dev/null +++ b/dist/NetworkTest/testQuality/helpers/calculateQualityStats.d.ts @@ -0,0 +1,3 @@ +import { SubscriberStats } from '../../types/opentok/subscriber'; +import { HasAudioVideo, QualityStats } from '../types/stats'; +export default function calculateQualityStats(latestSamples: SubscriberStats[]): HasAudioVideo; diff --git a/dist/NetworkTest/testQuality/helpers/calculateThroughput.d.ts b/dist/NetworkTest/testQuality/helpers/calculateThroughput.d.ts new file mode 100644 index 00000000..a7be2c7e --- /dev/null +++ b/dist/NetworkTest/testQuality/helpers/calculateThroughput.d.ts @@ -0,0 +1,3 @@ +import { AverageStats, HasAudioVideo } from '../types/stats'; +import MOSState from './MOSState'; +export default function calculateThroughput(state: MOSState): HasAudioVideo; diff --git a/dist/NetworkTest/testQuality/helpers/config.d.ts b/dist/NetworkTest/testQuality/helpers/config.d.ts new file mode 100644 index 00000000..10a1693d --- /dev/null +++ b/dist/NetworkTest/testQuality/helpers/config.d.ts @@ -0,0 +1,30 @@ +export interface AudioThreshold { + bps: number; + plr: number; +} +export interface VideoThreshold extends AudioThreshold { + recommendedSetting: string; +} +export declare type QualityTestConfig = { + getStatsInterval: number; + getStatsVideoAndAudioTestDuration: number; + getStatsAudioOnlyDuration: number; + subscribeOptions: { + testNetwork: boolean; + audioVolume: number; + }; + minimumVideoAndAudioTestSampleSize: number; + steadyStateSampleWindow: number; + steadyStateAllowedDelta: number; + qualityThresholds: { + audio: AudioThreshold[]; + video: VideoThreshold[]; + }; + strings: { + bandwidthLow: string; + noCam: string; + noMic: string; + }; +}; +declare const config: QualityTestConfig; +export default config; diff --git a/dist/NetworkTest/testQuality/helpers/getLatestSampleWindow.d.ts b/dist/NetworkTest/testQuality/helpers/getLatestSampleWindow.d.ts new file mode 100644 index 00000000..ef63de4f --- /dev/null +++ b/dist/NetworkTest/testQuality/helpers/getLatestSampleWindow.d.ts @@ -0,0 +1,2 @@ +import { SubscriberStats } from '../../types/opentok/subscriber'; +export default function getLatestSampleWindow(stats: SubscriberStats[]): SubscriberStats[]; diff --git a/dist/NetworkTest/testQuality/helpers/getQualityEvaluation.d.ts b/dist/NetworkTest/testQuality/helpers/getQualityEvaluation.d.ts new file mode 100644 index 00000000..1d6d47f3 --- /dev/null +++ b/dist/NetworkTest/testQuality/helpers/getQualityEvaluation.d.ts @@ -0,0 +1,8 @@ +import { AV, AverageStatsBase } from '../types/stats'; +export interface QualityEvaluationResults { + supported: boolean; + recommendedFrameRate?: number; + recommendedResolution?: string; + reason?: string; +} +export default function getQualityEvaluation(stats: AverageStatsBase, type: AV): QualityEvaluationResults; diff --git a/dist/NetworkTest/testQuality/helpers/isBitrateSteadyState.d.ts b/dist/NetworkTest/testQuality/helpers/isBitrateSteadyState.d.ts new file mode 100644 index 00000000..e93f31b0 --- /dev/null +++ b/dist/NetworkTest/testQuality/helpers/isBitrateSteadyState.d.ts @@ -0,0 +1,2 @@ +import { OT } from '../../types/opentok'; +export default function isBitrateSteadyState(statsList: OT.SubscriberStats[]): boolean; diff --git a/dist/NetworkTest/testQuality/helpers/isSupportedBrowser.d.ts b/dist/NetworkTest/testQuality/helpers/isSupportedBrowser.d.ts new file mode 100644 index 00000000..049db717 --- /dev/null +++ b/dist/NetworkTest/testQuality/helpers/isSupportedBrowser.d.ts @@ -0,0 +1,5 @@ +export declare type Browser = 'Chrome' | 'Firefox' | 'not a browser' | 'unsupported browser' | 'WebKit browser without WebRTC support' | 'Safari' | 'Internet Explorer' | 'Edge' | 'non-Chromium Edge' | 'Opera'; +export default function isSupportedBrowser(): { + supported: boolean; + browser: Browser; +}; diff --git a/dist/NetworkTest/testQuality/helpers/subscriberMOS.d.ts b/dist/NetworkTest/testQuality/helpers/subscriberMOS.d.ts new file mode 100644 index 00000000..491eb8f6 --- /dev/null +++ b/dist/NetworkTest/testQuality/helpers/subscriberMOS.d.ts @@ -0,0 +1,4 @@ +import MOSState from './MOSState'; +import { OT } from '../../types/opentok'; +export declare type StatsListener = (error?: OT.OTError, stats?: OT.SubscriberStats) => void; +export default function subscriberMOS(mosState: MOSState, subscriber: OT.Subscriber, getStatsListener: StatsListener, callback: (state: MOSState) => void): MOSState; diff --git a/dist/NetworkTest/testQuality/index.d.ts b/dist/NetworkTest/testQuality/index.d.ts new file mode 100644 index 00000000..11eff33a --- /dev/null +++ b/dist/NetworkTest/testQuality/index.d.ts @@ -0,0 +1,22 @@ +/** + * @module Test/Publishing + * @preferred + * + * Defines the methods required for the Publishing Test Flow + */ +/** + * Publishing Test Flow + */ +import OTKAnalytics = require('opentok-solutions-logging'); +import * as Promise from 'promise'; +import { NetworkTestOptions } from '../index'; +import { OT } from '../types/opentok'; +import { AverageStats, HasAudioVideo } from './types/stats'; +import { UpdateCallback, UpdateCallbackStats } from '../types/callbacks'; +export interface QualityTestResults extends HasAudioVideo { +} +/** + * This method checks to see if the client can publish to an OpenTok session. + */ +export declare function testQuality(OT: OT.Client, credentials: OT.SessionCredentials, otLogging: OTKAnalytics, options?: NetworkTestOptions, onUpdate?: UpdateCallback): Promise; +export declare function stopQualityTest(): void; diff --git a/dist/NetworkTest/testQuality/types/stats.d.ts b/dist/NetworkTest/testQuality/types/stats.d.ts new file mode 100644 index 00000000..78af06f7 --- /dev/null +++ b/dist/NetworkTest/testQuality/types/stats.d.ts @@ -0,0 +1,31 @@ +export declare type AV = 'audio' | 'video'; +export interface HasAudioVideo { + audio: A; + video: A; +} +export interface Kbps { + kbps: number; +} +export interface KbpsMap extends HasAudioVideo { +} +export interface Bandwidth extends HasAudioVideo { +} +export interface QualityStats { + averageBitrate: number; + packetLossRatio: number; + frameRate?: number; +} +export interface AverageStats { + bitrate?: number; + packetLossRatio?: number; + supported?: boolean; + reason?: string; + frameRate?: number; + recommendedFrameRate?: number; + recommendedResolution?: string; + mos?: number; +} +export interface AverageStatsBase { + bitrate: number; + packetLossRatio: number; +} diff --git a/dist/NetworkTest/types/callbacks.d.ts b/dist/NetworkTest/types/callbacks.d.ts new file mode 100644 index 00000000..ae261ff4 --- /dev/null +++ b/dist/NetworkTest/types/callbacks.d.ts @@ -0,0 +1,5 @@ +import { OT } from './opentok'; +export declare type UpdateCallback = (stats: OT.SubscriberStats) => void; +export declare type UpdateCallbackStats = OT.SubscriberStats & { + phase: string; +}; diff --git a/dist/NetworkTest/types/opentok/connection.d.ts b/dist/NetworkTest/types/opentok/connection.d.ts new file mode 100644 index 00000000..593a619c --- /dev/null +++ b/dist/NetworkTest/types/opentok/connection.d.ts @@ -0,0 +1,5 @@ +export interface Connection { + connectionId: string; + creationTime: number; + data: string; +} diff --git a/dist/NetworkTest/types/opentok/error.d.ts b/dist/NetworkTest/types/opentok/error.d.ts new file mode 100644 index 00000000..729ea57b --- /dev/null +++ b/dist/NetworkTest/types/opentok/error.d.ts @@ -0,0 +1,4 @@ +export interface OTError { + name: string; + message: string; +} diff --git a/dist/NetworkTest/types/opentok/events.d.ts b/dist/NetworkTest/types/opentok/events.d.ts new file mode 100644 index 00000000..fb2326dd --- /dev/null +++ b/dist/NetworkTest/types/opentok/events.d.ts @@ -0,0 +1,17 @@ +import { Dimensions } from './widget'; +export interface Event { + type: Type; + cancelable: boolean; + target: Target; + isDefaultPrevented(): boolean; + preventDefault(): void; +} +export interface VideoDimensionsChangedEvent extends Event<'videoDimensionsChanged', Target> { + oldValue: Dimensions; + newValue: Dimensions; +} +export interface OTEventEmitter { + on(eventName: EventName, callback: (event: EventMap[EventName]) => void): void; + once(eventName: EventName, callback: (event: EventMap[EventName]) => void): void; + off(eventName?: EventName, callback?: (event: EventMap[EventName]) => void): void; +} diff --git a/dist/NetworkTest/types/opentok/index.d.ts b/dist/NetworkTest/types/opentok/index.d.ts new file mode 100644 index 00000000..9d82946e --- /dev/null +++ b/dist/NetworkTest/types/opentok/index.d.ts @@ -0,0 +1,77 @@ +import * as OTSession from './session'; +import * as OTStream from './stream'; +import * as OTConnection from './connection'; +import * as OTPublisher from './publisher'; +import * as OTSubscriber from './subscriber'; +import * as OTError from './error'; +import * as OTEvent from './events'; +export declare namespace OT { + /** + * OpenTok.js Client SDK + */ + interface Client { + properties?: { + version: string; + buildHash: string; + debug: boolean; + websiteURL: string; + cdnURL: string; + loggingURL: string; + apiURL: string; + supportSSL: boolean; + cdnURLSSL: string; + loggingURLSSL: string; + apiURLSSL: string; + minimumVersion: { + firefox: number; + chrome: number; + }; + sentryDSN: string; + enableErrorReporting: boolean; + assetURL: string; + cssURL: string; + }; + checkScreenSharingCapability(callback: (response: ScreenSharingCapabilityResponse) => void): void; + checkSystemRequirements(): number; + getDevices(callback: (error: OTError | undefined, devices?: Device[]) => void): void; + initPublisher(targetElement?: HTMLElement | string, properties?: OTPublisher.PublisherProperties, callback?: (error?: Error) => void): Publisher; + initSession(partnerId: string, sessionId: string, options?: OTSession.initSessionOptions): Session; + registerScreenSharingExtension(kind: string, id: string, version: number): void; + reportIssue(callback: (error?: OTError, reportId?: string) => void): void; + setLogLevel(level: number): void; + setProxyUrl(proxyUrl: string): void; + upgradeSystemRequirements(): void; + } + interface SessionCredentials { + apiKey: string; + sessionId: string; + token: string; + } + interface Device { + kind: 'audioInput' | 'videoInput'; + deviceId: string; + label: string; + } + interface ScreenSharingCapabilityResponse { + extensionInstalled: boolean; + supported: boolean; + supportedSources: { + application: boolean; + screen: boolean; + window: boolean; + }; + extensionRegistered?: string; + } + type Session = OTSession.Session; + type InitSessionOptions = OTSession.initSessionOptions; + type Event = OTEvent.Event; + type Connection = OTConnection.Connection; + type Stream = OTStream.Stream; + type Publisher = OTPublisher.Publisher; + type PublisherProperties = OTPublisher.PublisherProperties; + type Subscriber = OTSubscriber.Subscriber; + type SubscriberStats = OTSubscriber.SubscriberStats; + type SubscriberProperties = OTSubscriber.SubscriberProperties; + type TrackStats = OTSubscriber.TrackStats; + type OTError = OTError.OTError; +} diff --git a/dist/NetworkTest/types/opentok/publisher.d.ts b/dist/NetworkTest/types/opentok/publisher.d.ts new file mode 100644 index 00000000..a31e1e04 --- /dev/null +++ b/dist/NetworkTest/types/opentok/publisher.d.ts @@ -0,0 +1,92 @@ +import { Stream } from './stream'; +import { Session } from './session'; +import { Event, OTEventEmitter, VideoDimensionsChangedEvent } from './events'; +import { Dimensions, WidgetProperties, WidgetStyle } from './widget'; +export interface OutgoingTrackStats { + bytesSent: number; + packetsLost: number; + packetsSent: number; +} +export interface PublisherStats { + audio: OutgoingTrackStats; + video: OutgoingTrackStats & { + frameRate: number; + }; + timestamp: number; +} +export interface PublisherStatContainer { + subscriberId?: string; + connectionId?: string; + stats: PublisherStats; +} +export declare type PublisherStatsArr = PublisherStatContainer[]; +export interface PublisherStyle extends WidgetStyle { + archiveStatusDisplayMode: 'auto' | 'off'; +} +export interface GetUserMediaProperties { + audioSource?: string | null | boolean | MediaStreamTrack; + disableAudioProcessing?: boolean; + facingMode?: 'user' | 'environment' | 'left' | 'right'; + frameRate?: 30 | 15 | 7 | 1; + maxResolution?: Dimensions; + resolution?: ('1280x960' | '1280x720' | '640x480' | '640x360' | '320x240' | '320x180'); + videoSource?: string | null | boolean | MediaStreamTrack; +} +export interface PublisherProperties extends WidgetProperties, GetUserMediaProperties { + audioBitrate?: number; + audioFallbackEnabled?: boolean; + audioSource?: string | null; + disableAudioProcessing?: boolean; + frameRate?: 30 | 15 | 7 | 1; + maxResolution?: Dimensions; + mirror?: boolean; + name?: string; + publishAudio?: boolean; + publishVideo?: boolean; + resolution?: ('1280x960' | '1280x720' | '640x480' | '640x360' | '320x240' | '320x180'); + style?: Partial; + usePreviousDeviceSelection?: boolean; + videoSource?: string | null; +} +export interface Publisher extends OTEventEmitter<{ + accessAllowed: Event<'accessAllowed', Publisher>; + accessDenied: Event<'accessDenied', Publisher>; + accessDialogClosed: Event<'accessDialogClosed', Publisher>; + accessDialogOpened: Event<'accessDialogOpened', Publisher>; + audioLevelUpdated: Event<'audioLevelUpdated', Publisher> & { + audioLevel: number; + }; + destroyed: Event<'destroyed', Publisher>; + getStats(callback: (error?: Error, stats?: PublisherStatsArr) => void): void; + mediaStopped: Event<'mediaStopped', Publisher> & { + track: MediaStreamTrack | undefined; + }; + streamCreated: Event<'streamCreated', Publisher> & { + stream: Stream; + }; + streamDestroyed: Event<'streamDestroyed', Publisher> & { + stream: Stream; + reason: string; + }; + videoDimensionsChanged: VideoDimensionsChangedEvent; + videoElementCreated: Event<'videoElementCreated', Publisher> & { + element: HTMLVideoElement | HTMLObjectElement; + }; +}> { + accessAllowed: boolean; + element?: HTMLElement | undefined; + id?: string; + stream?: Stream; + session?: Session; + destroy(): void; + getImgData(): string | null; + getStyle(): PublisherProperties; + publishAudio(value: boolean): void; + publishVideo(value: boolean): void; + cycleVideo(): Promise<{ + deviceId: string; + }>; + setStyle