From 2ef6abbfb8c1ea50e7780f7571033262df73c44d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 12 Oct 2022 18:59:10 +0100 Subject: [PATCH] Move from `browser-request` to `fetch` (#23427) --- package.json | 4 +- src/vector/app.tsx | 4 +- src/vector/getconfig.ts | 56 +++++-------- src/vector/platform/WebPlatform.ts | 35 +++----- test/app-tests/loading-test.tsx | 36 ++++---- test/unit-tests/vector/getconfig-test.ts | 84 ++++++++----------- .../vector/platform/WebPlatform-test.ts | 17 ++-- yarn.lock | 81 ++++++++++++++++-- 8 files changed, 169 insertions(+), 148 deletions(-) diff --git a/package.json b/package.json index 3f70239dc7..0231c95643 100644 --- a/package.json +++ b/package.json @@ -119,6 +119,7 @@ "eslint-plugin-react-hooks": "^4.3.0", "extract-text-webpack-plugin": "^4.0.0-beta.0", "fake-indexeddb": "^3.1.2", + "fetch-mock-jest": "^1.5.1", "file-loader": "^5.1.0", "fs-extra": "^0.30.0", "html-webpack-plugin": "^4.5.2", @@ -130,7 +131,7 @@ "jest-sonar-reporter": "^2.0.0", "json-loader": "^0.5.7", "loader-utils": "^1.4.0", - "matrix-mock-request": "^2.0.0", + "matrix-mock-request": "^2.5.0", "matrix-react-test-utils": "^0.2.3", "matrix-web-i18n": "^1.3.0", "mini-css-extract-plugin": "^1", @@ -188,7 +189,6 @@ "\\.(gif|png|ttf|woff2)$": "/node_modules/matrix-react-sdk/__mocks__/imageMock.js", "\\.svg$": "/node_modules/matrix-react-sdk/__mocks__/svg.js", "\\$webapp/i18n/languages.json": "/node_modules/matrix-react-sdk/__mocks__/languages.json", - "^browser-request$": "/node_modules/matrix-react-sdk/__mocks__/browser-request.js", "^react$": "/node_modules/react", "^react-dom$": "/node_modules/react-dom", "^matrix-js-sdk$": "/node_modules/matrix-js-sdk/src", diff --git a/src/vector/app.tsx b/src/vector/app.tsx index 1c38a2c84a..b63e22a891 100644 --- a/src/vector/app.tsx +++ b/src/vector/app.tsx @@ -18,8 +18,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -// To ensure we load the browser-request version -import "matrix-js-sdk"; // eslint-disable-line no-restricted-imports +// To ensure we load the browser-matrix version first +import "matrix-js-sdk/src/browser-index"; import React from 'react'; import PlatformPeg from 'matrix-react-sdk/src/PlatformPeg'; diff --git a/src/vector/getconfig.ts b/src/vector/getconfig.ts index e73145576b..3d4bbd7bd4 100644 --- a/src/vector/getconfig.ts +++ b/src/vector/getconfig.ts @@ -14,8 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -import request from 'browser-request'; - import type { IConfigOptions } from "matrix-react-sdk/src/IConfigOptions"; // Load the config file. First try to load up a domain-specific config of the @@ -32,44 +30,28 @@ export async function getVectorConfig(relativeLocation=''): Promise { - return new Promise(function(resolve, reject) { - request( - { method: "GET", url: configJsonFilename, qs: { cachebuster: Date.now() } }, - (err, response, body) => { - try { - if (err || response.status < 200 || response.status >= 300) { - // Lack of a config isn't an error, we should - // just use the defaults. - // Also treat a blank config as no config, assuming - // the status code is 0, because we don't get 404s - // from file: URIs so this is the only way we can - // not fail if the file doesn't exist when loading - // from a file:// URI. - if (response) { - if (response.status == 404 || (response.status == 0 && body == '')) { - resolve({}); - } - } - reject({ err: err, response: response }); - return; - } - - // We parse the JSON ourselves rather than use the JSON - // parameter, since this throws a parse error on empty - // which breaks if there's no config.json and we're - // loading from the filesystem (see above). - resolve(JSON.parse(body)); - } catch (e) { - reject({ err: e }); - } - }, - ); +async function getConfig(configJsonFilename: string): Promise { + const url = new URL(configJsonFilename, window.location.href); + url.searchParams.set("cachebuster", Date.now().toString()); + const res = await fetch(url, { + cache: "no-cache", + method: "GET", }); + + if (res.status === 404 || res.status === 0) { + // Lack of a config isn't an error, we should just use the defaults. + // Also treat a blank config as no config, assuming the status code is 0, because we don't get 404s from file: + // URIs so this is the only way we can not fail if the file doesn't exist when loading from a file:// URI. + return {} as IConfigOptions; + } + + if (res.ok) { + return res.json(); + } } diff --git a/src/vector/platform/WebPlatform.ts b/src/vector/platform/WebPlatform.ts index 110358a479..2800f27a4c 100644 --- a/src/vector/platform/WebPlatform.ts +++ b/src/vector/platform/WebPlatform.ts @@ -17,7 +17,6 @@ limitations under the License. */ import { UpdateCheckStatus, UpdateStatus } from "matrix-react-sdk/src/BasePlatform"; -import request from 'browser-request'; import dis from 'matrix-react-sdk/src/dispatcher/dispatcher'; import { _t } from 'matrix-react-sdk/src/languageHandler'; import { hideToast as hideUpdateToast, showToast as showUpdateToast } from "matrix-react-sdk/src/toasts/UpdateToast"; @@ -87,31 +86,17 @@ export default class WebPlatform extends VectorBasePlatform { }); } - private getMostRecentVersion(): Promise { - // We add a cachebuster to the request to make sure that we know about - // the most recent version on the origin server. That might not - // actually be the version we'd get on a reload (particularly in the - // presence of intermediate caching proxies), but still: we're trying - // to tell the user that there is a new version. - - return new Promise((resolve, reject) => { - request( - { - method: "GET", - url: "version", - qs: { cachebuster: Date.now() }, - }, - (err, response, body) => { - if (err || response.status < 200 || response.status >= 300) { - if (err === null) err = { status: response.status }; - reject(err); - return; - } - - resolve(getNormalizedAppVersion(body.trim())); - }, - ); + private async getMostRecentVersion(): Promise { + const res = await fetch("version", { + method: "GET", + cache: "no-cache", }); + + if (res.ok) { + return getNormalizedAppVersion(await res.text()); + } + + return Promise.reject({ status: res.status }); } public getAppVersion(): Promise { diff --git a/test/app-tests/loading-test.tsx b/test/app-tests/loading-test.tsx index f4387f2a55..8657c9b1a3 100644 --- a/test/app-tests/loading-test.tsx +++ b/test/app-tests/loading-test.tsx @@ -41,6 +41,7 @@ import { RoomView as RoomViewClass } from 'matrix-react-sdk/src/components/struc import LoginComponent from 'matrix-react-sdk/src/components/structures/auth/Login'; import WelcomeComponent from "matrix-react-sdk/src/components/views/auth/Welcome"; import EmbeddedPage from "matrix-react-sdk/src/components/structures/EmbeddedPage"; +import { AutoDiscovery } from 'matrix-js-sdk/src/matrix'; const DEFAULT_HS_URL='http://my_server'; const DEFAULT_IS_URL='http://my_is'; @@ -60,7 +61,7 @@ describe('loading:', function() { beforeEach(function() { httpBackend = new MockHttpBackend(); - jssdk.request(httpBackend.requestFn); + window.fetch = httpBackend.fetchFn; parentDiv = document.createElement('div'); // uncomment this to actually add the div to the UI, to help with @@ -73,21 +74,25 @@ describe('loading:', function() { afterEach(async function() { console.log(`${Date.now()}: loading: afterEach`); - if (parentDiv) { - ReactDOM.unmountComponentAtNode(parentDiv); - parentDiv.remove(); - parentDiv = null; + try { + if (matrixChat) { + ReactDOM.unmountComponentAtNode(parentDiv); + parentDiv.remove(); + parentDiv = null; + } + + // unmounting should have cleared the MatrixClientPeg + expect(MatrixClientPeg.get()).toBe(null); + + // clear the indexeddbs so we can start from a clean slate next time. + await Promise.all([ + test_utils.deleteIndexedDB('matrix-js-sdk:crypto'), + test_utils.deleteIndexedDB('matrix-js-sdk:riot-web-sync'), + ]); + cleanLocalstorage(); + } catch (e) { + console.error(e); } - - // unmounting should have cleared the MatrixClientPeg - expect(MatrixClientPeg.get()).toBe(null); - - // clear the indexeddbs so we can start from a clean slate next time. - await Promise.all([ - test_utils.deleteIndexedDB('matrix-js-sdk:crypto'), - test_utils.deleteIndexedDB('matrix-js-sdk:riot-web-sync'), - ]); - cleanLocalstorage(); console.log(`${Date.now()}: loading: afterEach complete`); }); @@ -421,7 +426,6 @@ describe('loading:', function() { describe('Guest auto-registration:', function() { it('shows a welcome page by default', function() { - loadApp(); return sleep(1).then(() => { diff --git a/test/unit-tests/vector/getconfig-test.ts b/test/unit-tests/vector/getconfig-test.ts index 360b888d86..5702385106 100644 --- a/test/unit-tests/vector/getconfig-test.ts +++ b/test/unit-tests/vector/getconfig-test.ts @@ -14,14 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -import request from 'browser-request'; +import fetchMock from "fetch-mock-jest"; import { getVectorConfig } from "../../../src/vector/getconfig"; -describe('getVectorConfig()', () => { - const setRequestMockImplementationOnce = (err?: unknown, response?: { status: number }, body?: string) => - request.mockImplementationOnce((_opts, callback) => callback(err, response, body)); +fetchMock.config.overwriteRoutes = true; +describe('getVectorConfig()', () => { const prevDocumentDomain = document.domain; const elementDomain = 'app.element.io'; const now = 1234567890; @@ -38,6 +37,7 @@ describe('getVectorConfig()', () => { // stable value for cachebuster jest.spyOn(Date, 'now').mockReturnValue(now); jest.clearAllMocks(); + fetchMock.mockClear(); }); afterAll(() => { @@ -46,85 +46,67 @@ describe('getVectorConfig()', () => { }); it('requests specific config for document domain', async () => { - setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(specificConfig)) - setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig)) + fetchMock.getOnce("express:/config.app.element.io.json", specificConfig); + fetchMock.getOnce("express:/config.json", generalConfig); - await getVectorConfig(); - - expect(request.mock.calls[0][0]).toEqual({ method: "GET", url: 'config.app.element.io.json', qs: { cachebuster: now } }) + await expect(getVectorConfig()).resolves.toEqual(specificConfig); }); it('adds trailing slash to relativeLocation when not an empty string', async () => { - setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(specificConfig)) - setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig)) + fetchMock.getOnce("express:../config.app.element.io.json", specificConfig); + fetchMock.getOnce("express:../config.json", generalConfig); - await getVectorConfig('..'); - - expect(request.mock.calls[0][0]).toEqual(expect.objectContaining({ url: '../config.app.element.io.json' })) - expect(request.mock.calls[1][0]).toEqual(expect.objectContaining({ url: '../config.json' })) - }); - - it('returns parsed specific config when it is non-empty', async () => { - setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(specificConfig)) - setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig)) - - const result = await getVectorConfig(); - expect(result).toEqual(specificConfig); + await expect(getVectorConfig("..")).resolves.toEqual(specificConfig); }); it('returns general config when specific config succeeds but is empty', async () => { - setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify({})) - setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig)) + fetchMock.getOnce("express:/config.app.element.io.json", {}); + fetchMock.getOnce("express:/config.json", generalConfig); - const result = await getVectorConfig(); - expect(result).toEqual(generalConfig); + await expect(getVectorConfig()).resolves.toEqual(generalConfig); }); it('returns general config when specific config 404s', async () => { - setRequestMockImplementationOnce(undefined, { status: 404 }) - setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig)) + fetchMock.getOnce("express:/config.app.element.io.json", { status: 404 }); + fetchMock.getOnce("express:/config.json", generalConfig); - const result = await getVectorConfig(); - expect(result).toEqual(generalConfig); + await expect(getVectorConfig()).resolves.toEqual(generalConfig); }); it('returns general config when specific config is fetched from a file and is empty', async () => { - setRequestMockImplementationOnce(undefined, { status: 0 }, '') - setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig)) + fetchMock.getOnce("express:/config.app.element.io.json", 0); + fetchMock.getOnce("express:/config.json", generalConfig); - const result = await getVectorConfig(); - expect(result).toEqual(generalConfig); + await expect(getVectorConfig()).resolves.toEqual(generalConfig); }); it('returns general config when specific config returns a non-200 status', async () => { - setRequestMockImplementationOnce(undefined, { status: 401 }) - setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig)) + fetchMock.getOnce("express:/config.app.element.io.json", { status: 401 }); + fetchMock.getOnce("express:/config.json", generalConfig); - const result = await getVectorConfig(); - expect(result).toEqual(generalConfig); + await expect(getVectorConfig()).resolves.toEqual(generalConfig); }); it('returns general config when specific config returns an error', async () => { - setRequestMockImplementationOnce('err1') - setRequestMockImplementationOnce(undefined, { status: 200 }, JSON.stringify(generalConfig)) + fetchMock.getOnce("express:/config.app.element.io.json", { throws: "err1" }); + fetchMock.getOnce("express:/config.json", generalConfig); - const result = await getVectorConfig(); - expect(result).toEqual(generalConfig); + await expect(getVectorConfig()).resolves.toEqual(generalConfig); }); it('rejects with an error when general config rejects', async () => { - setRequestMockImplementationOnce('err-specific'); - setRequestMockImplementationOnce('err-general'); + fetchMock.getOnce("express:/config.app.element.io.json", { throws: "err-specific" }); + fetchMock.getOnce("express:/config.json", { throws: "err-general" }); - await expect(() => getVectorConfig()).rejects.toEqual({"err": "err-general", "response": undefined}); + await expect(getVectorConfig()).rejects.toBe("err-general"); }); it('rejects with an error when config is invalid JSON', async () => { - setRequestMockImplementationOnce('err-specific'); - setRequestMockImplementationOnce(undefined, { status: 200 }, '{"invalid": "json",}'); + fetchMock.getOnce("express:/config.app.element.io.json", { throws: "err-specific" }); + fetchMock.getOnce("express:/config.json", '{"invalid": "json",}'); - await expect(() => getVectorConfig()).rejects.toEqual({ - err: new SyntaxError("Unexpected token } in JSON at position 19"), - }); + // We can't assert it'll be a SyntaxError as node-fetch behaves differently + // https://github.com/wheresrhys/fetch-mock/issues/270 + await expect(getVectorConfig()).rejects.toThrow("Unexpected token } in JSON at position 19"); }); }); diff --git a/test/unit-tests/vector/platform/WebPlatform-test.ts b/test/unit-tests/vector/platform/WebPlatform-test.ts index 7889adabe9..534da3f5ce 100644 --- a/test/unit-tests/vector/platform/WebPlatform-test.ts +++ b/test/unit-tests/vector/platform/WebPlatform-test.ts @@ -14,12 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -import request from 'browser-request'; +import fetchMock from "fetch-mock-jest"; import { UpdateCheckStatus } from 'matrix-react-sdk/src/BasePlatform'; import { MatrixClientPeg } from 'matrix-react-sdk/src/MatrixClientPeg'; import WebPlatform from '../../../../src/vector/platform/WebPlatform'; +fetchMock.config.overwriteRoutes = true; + describe('WebPlatform', () => { beforeEach(() => { jest.clearAllMocks(); @@ -120,9 +122,6 @@ describe('WebPlatform', () => { const envVersion = process.env.VERSION; const prodVersion = '1.10.13'; - const setRequestMockImplementation = (err?: unknown, response?: { status: number }, body?: string) => - request.mockImplementation((_opts, callback) => callback(err, response, body)); - beforeEach(() => { jest.spyOn(MatrixClientPeg, 'userRegisteredWithinLastHours').mockReturnValue(false); }) @@ -157,7 +156,7 @@ describe('WebPlatform', () => { describe('pollForUpdate()', () => { it('should return not available and call showNoUpdate when current version matches most recent version', async () => { process.env.VERSION = prodVersion; - setRequestMockImplementation(undefined, { status: 200}, prodVersion); + fetchMock.getOnce("/version", prodVersion); const platform = new WebPlatform(); const showUpdate = jest.fn(); @@ -171,7 +170,7 @@ describe('WebPlatform', () => { it('should strip v prefix from versions before comparing', async () => { process.env.VERSION = prodVersion; - setRequestMockImplementation(undefined, { status: 200}, `v${prodVersion}`); + fetchMock.getOnce("/version", `v${prodVersion}`); const platform = new WebPlatform(); const showUpdate = jest.fn(); @@ -186,7 +185,7 @@ describe('WebPlatform', () => { it('should return ready and call showUpdate when current version differs from most recent version', async () => { process.env.VERSION = '0.0.0'; // old version - setRequestMockImplementation(undefined, { status: 200}, prodVersion); + fetchMock.getOnce("/version", prodVersion); const platform = new WebPlatform(); const showUpdate = jest.fn(); @@ -201,7 +200,7 @@ describe('WebPlatform', () => { it('should return ready without showing update when user registered in last 24', async () => { process.env.VERSION = '0.0.0'; // old version jest.spyOn(MatrixClientPeg, 'userRegisteredWithinLastHours').mockReturnValue(true); - setRequestMockImplementation(undefined, { status: 200}, prodVersion); + fetchMock.getOnce("/version", prodVersion); const platform = new WebPlatform(); const showUpdate = jest.fn(); @@ -214,7 +213,7 @@ describe('WebPlatform', () => { }); it('should return error when version check fails', async () => { - setRequestMockImplementation('oups'); + fetchMock.getOnce("/version", { throws: "oups" }); const platform = new WebPlatform(); const showUpdate = jest.fn(); diff --git a/yarn.lock b/yarn.lock index 2a7028bd52..d7c4cc455f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -47,7 +47,7 @@ resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.19.3.tgz#707b939793f867f5a73b2666e6d9a3396eb03151" integrity sha512-prBHMK4JYYK+wDjJF1q99KK4JLL+egWS4nmNqdlMUgCExMZ+iZW0hGhyC3VEbsPjvaN0TBhW//VIFwBrk8sEiw== -"@babel/core@^7.11.6", "@babel/core@^7.12.10", "@babel/core@^7.12.3": +"@babel/core@^7.0.0", "@babel/core@^7.11.6", "@babel/core@^7.12.10", "@babel/core@^7.12.3": version "7.19.3" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.19.3.tgz#2519f62a51458f43b682d61583c3810e7dcee64c" integrity sha512-WneDJxdsjEvyKtXKsaBGbDeiyOjR5vYq4HcShxnIbG0qixpoHjI3MqeZM9NDvsojNCEBItQE4juOo/bU6e72gQ== @@ -3978,6 +3978,11 @@ core-js@^2.4.0: resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== +core-js@^3.0.0: + version "3.25.5" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.25.5.tgz#e86f651a2ca8a0237a5f064c2fe56cef89646e27" + integrity sha512-nbm6eZSjm+ZuBQxCUPQKQCoUEfFOXjUZ8dTTyikyKaWrTYmAVbykQfwsKE5dBK88u3QCkCrzsx/PPlKfhsvgpw== + core-js@^3.4: version "3.24.1" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.24.1.tgz#cf7724d41724154010a6576b7b57d94c5d66e64f" @@ -5670,6 +5675,29 @@ fbjs@^0.8.4: setimmediate "^1.0.5" ua-parser-js "^0.7.30" +fetch-mock-jest@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/fetch-mock-jest/-/fetch-mock-jest-1.5.1.tgz#0e13df990d286d9239e284f12b279ed509bf53cd" + integrity sha512-+utwzP8C+Pax1GSka3nFXILWMY3Er2L+s090FOgqVNrNCPp0fDqgXnAHAJf12PLHi0z4PhcTaZNTz8e7K3fjqQ== + dependencies: + fetch-mock "^9.11.0" + +fetch-mock@^9.11.0: + version "9.11.0" + resolved "https://registry.yarnpkg.com/fetch-mock/-/fetch-mock-9.11.0.tgz#371c6fb7d45584d2ae4a18ee6824e7ad4b637a3f" + integrity sha512-PG1XUv+x7iag5p/iNHD4/jdpxL9FtVSqRMUQhPab4hVDt80T1MH5ehzVrL2IdXO9Q2iBggArFvPqjUbHFuI58Q== + dependencies: + "@babel/core" "^7.0.0" + "@babel/runtime" "^7.0.0" + core-js "^3.0.0" + debug "^4.1.1" + glob-to-regexp "^0.4.0" + is-subset "^0.1.1" + lodash.isequal "^4.5.0" + path-to-regexp "^2.2.1" + querystring "^0.2.0" + whatwg-url "^6.5.0" + fflate@^0.4.1: version "0.4.8" resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.4.8.tgz#f90b82aefbd8ac174213abb338bd7ef848f0f5ae" @@ -6139,7 +6167,7 @@ glob-to-regexp@^0.3.0: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha512-Iozmtbqv0noj0uDDqoL0zNq0VBEfK2YFoMAZoxJe4cwphvLR+JskfF30QhXHOR4m3KrE6NLRYw+U9MRXvifyig== -glob-to-regexp@^0.4.1: +glob-to-regexp@^0.4.0, glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== @@ -7179,6 +7207,11 @@ is-string@^1.0.5, is-string@^1.0.7: dependencies: has-tostringtag "^1.0.0" +is-subset@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6" + integrity sha512-6Ybun0IkarhmEqxXCNw/C0bna6Zb/TkfUX9UbwJtK6ObwAVCxmAP308WWTHviM/zAqXk05cdhYsUsZeGQh99iw== + is-symbol@^1.0.2, is-symbol@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" @@ -8095,6 +8128,11 @@ lodash.debounce@^4.0.8: resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== +lodash.isequal@^4.5.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" + integrity sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ== + lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -8105,6 +8143,11 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.sortby@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" + integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA== + lodash.truncate@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193" @@ -8285,10 +8328,10 @@ matrix-events-sdk@^0.0.1-beta.7: request "^2.88.2" unhomoglyph "^1.0.6" -matrix-mock-request@^2.0.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/matrix-mock-request/-/matrix-mock-request-2.1.2.tgz#11e38ed1233dced88a6f2bfba1684d5c5b3aa2c2" - integrity sha512-/OXCIzDGSLPJ3fs+uzDrtaOHI/Sqp4iEuniRn31U8S06mPXbvAnXknHqJ4c6A/KVwJj/nPFbGXpK4wPM038I6A== +matrix-mock-request@^2.5.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/matrix-mock-request/-/matrix-mock-request-2.5.0.tgz#78da2590e82be2e31edcf9814833af5e5f8d2f1a" + integrity sha512-7T3gklpW+4rfHsTnp/FDML7aWoBrXhAh8+1ltinQfAh9TDj6y382z/RUMR7i03d1WDzt/ed1UTihqO5GDoOq9Q== dependencies: expect "^28.1.0" @@ -9421,6 +9464,11 @@ path-to-regexp@0.1.7: resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" integrity sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ== +path-to-regexp@^2.2.1: + version "2.4.0" + resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-2.4.0.tgz#35ce7f333d5616f1c1e1bfe266c3aba2e5b2e704" + integrity sha512-G6zHoVqC6GGTQkZwF4lkuEyMbVOjoBKAEybQUypI1WTkqinCOrq2x6U2+phkJ1XsEMTy4LjtwPI7HW+NVrRR2w== + path-type@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-3.0.0.tgz#cef31dc8e0a1a3bb0d105c0cd97cf3bf47f4e36f" @@ -10488,6 +10536,11 @@ querystring@0.2.0: resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g== +querystring@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.1.tgz#40d77615bb09d16902a85c3e38aa8b5ed761c2dd" + integrity sha512-wkvS7mL/JMugcup3/rMitHmd9ecIGd2lhFhK9N3UUQ450h66d1r3Y9nvXzQAW1Lq+wyx61k/1pfKS5KuKiyEbg== + querystringify@^2.1.1: version "2.2.0" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" @@ -12181,6 +12234,13 @@ tough-cookie@~2.5.0: psl "^1.1.28" punycode "^2.1.1" +tr46@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09" + integrity sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA== + dependencies: + punycode "^2.1.0" + tr46@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/tr46/-/tr46-2.1.0.tgz#fa87aa81ca5d5941da8cbf1f9b749dc969a4e240" @@ -12903,6 +12963,15 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" +whatwg-url@^6.5.0: + version "6.5.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" + integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ== + dependencies: + lodash.sortby "^4.7.0" + tr46 "^1.0.1" + webidl-conversions "^4.0.2" + whatwg-url@^8.4.0: version "8.7.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77"