diff --git a/package.json b/package.json index 787e776bd..bc6ab9b70 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "content-type": "^1.0.4", "loglevel": "^1.7.1", "matrix-events-sdk": "^0.0.1-beta.7", + "matrix-widget-api": "^1.0.0", "p-retry": "4", "qs": "^6.9.6", "request": "^2.88.2", @@ -101,6 +102,7 @@ "exorcist": "^2.0.0", "fake-indexeddb": "^4.0.0", "jest": "^28.0.0", + "jest-environment-jsdom": "^28.1.3", "jest-localstorage-mock": "^2.4.6", "jest-sonar-reporter": "^2.0.0", "jsdoc": "^3.6.6", diff --git a/spec/unit/embedded.spec.ts b/spec/unit/embedded.spec.ts new file mode 100644 index 000000000..edac107b9 --- /dev/null +++ b/spec/unit/embedded.spec.ts @@ -0,0 +1,264 @@ +/** + * @jest-environment jsdom + */ + +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// We have to use EventEmitter here to mock part of the matrix-widget-api +// project, which doesn't know about our TypeEventEmitter implementation at all +// eslint-disable-next-line no-restricted-imports +import { EventEmitter } from "events"; +import { MockedObject } from "jest-mock"; +import { + WidgetApi, + WidgetApiToWidgetAction, + MatrixCapabilities, + ITurnServer, +} from "matrix-widget-api"; + +import { createRoomWidgetClient } from "../../src/matrix"; +import { MatrixClient, ClientEvent, ITurnServer as IClientTurnServer } from "../../src/client"; +import { SyncState } from "../../src/sync"; +import { ICapabilities } from "../../src/embedded"; +import { MatrixEvent } from "../../src/models/event"; +import { ToDeviceBatch } from "../../src/models/ToDeviceMessage"; +import { DeviceInfo } from "../../src/crypto/deviceinfo"; + +class MockWidgetApi extends EventEmitter { + public start = jest.fn(); + public requestCapability = jest.fn(); + public requestCapabilities = jest.fn(); + public requestCapabilityToSendState = jest.fn(); + public requestCapabilityToReceiveState = jest.fn(); + public requestCapabilityToSendToDevice = jest.fn(); + public requestCapabilityToReceiveToDevice = jest.fn(); + public sendStateEvent = jest.fn(); + public sendToDevice = jest.fn(); + public readStateEvents = jest.fn(() => []); + public getTurnServers = jest.fn(() => []); + + public transport = { reply: jest.fn() }; +} + +describe("RoomWidgetClient", () => { + let widgetApi: MockedObject; + let client: MatrixClient; + + beforeEach(() => { + widgetApi = new MockWidgetApi() as unknown as MockedObject; + }); + + afterEach(() => { + client.stopClient(); + }); + + const makeClient = async (capabilities: ICapabilities): Promise => { + const baseUrl = "https://example.org"; + client = createRoomWidgetClient(widgetApi, capabilities, "!1:example.org", { baseUrl }); + expect(widgetApi.start).toHaveBeenCalled(); // needs to have been called early in order to not miss messages + widgetApi.emit("ready"); + await client.startClient(); + }; + + describe("state events", () => { + const event = new MatrixEvent({ + type: "org.example.foo", + event_id: "$sfkjfsksdkfsd", + room_id: "!1:example.org", + sender: "@alice:example.org", + state_key: "bar", + content: { hello: "world" }, + }).getEffectiveEvent(); + + it("sends", async () => { + await makeClient({ sendState: [{ eventType: "org.example.foo", stateKey: "bar" }] }); + expect(widgetApi.requestCapabilityToSendState).toHaveBeenCalledWith("org.example.foo", "bar"); + await client.sendStateEvent("!1:example.org", "org.example.foo", { hello: "world" }, "bar"); + expect(widgetApi.sendStateEvent).toHaveBeenCalledWith("org.example.foo", "bar", { hello: "world" }); + }); + + it("refuses to send to other rooms", async () => { + await makeClient({ sendState: [{ eventType: "org.example.foo", stateKey: "bar" }] }); + expect(widgetApi.requestCapabilityToSendState).toHaveBeenCalledWith("org.example.foo", "bar"); + await expect(client.sendStateEvent("!2:example.org", "org.example.foo", { hello: "world" }, "bar")) + .rejects.toBeDefined(); + }); + + it("receives", async () => { + await makeClient({ receiveState: [{ eventType: "org.example.foo", stateKey: "bar" }] }); + expect(widgetApi.requestCapabilityToReceiveState).toHaveBeenCalledWith("org.example.foo", "bar"); + + const emittedEvent = new Promise(resolve => client.once(ClientEvent.Event, resolve)); + const emittedSync = new Promise(resolve => client.once(ClientEvent.Sync, resolve)); + widgetApi.emit( + `action:${WidgetApiToWidgetAction.SendEvent}`, + new CustomEvent(`action:${WidgetApiToWidgetAction.SendEvent}`, { detail: { data: event } }), + ); + + // The client should've emitted about the received event + expect((await emittedEvent).getEffectiveEvent()).toEqual(event); + expect(await emittedSync).toEqual(SyncState.Syncing); + // It should've also inserted the event into the room object + const room = client.getRoom("!1:example.org"); + expect(room.currentState.getStateEvents("org.example.foo", "bar").getEffectiveEvent()).toEqual(event); + }); + + it("backfills", async () => { + widgetApi.readStateEvents.mockImplementation(async (eventType, limit, stateKey) => + eventType === "org.example.foo" && (limit ?? Infinity) > 0 && stateKey === "bar" + ? [event] + : [], + ); + + await makeClient({ receiveState: [{ eventType: "org.example.foo", stateKey: "bar" }] }); + expect(widgetApi.requestCapabilityToReceiveState).toHaveBeenCalledWith("org.example.foo", "bar"); + + const room = client.getRoom("!1:example.org"); + expect(room.currentState.getStateEvents("org.example.foo", "bar").getEffectiveEvent()).toEqual(event); + }); + }); + + describe("to-device messages", () => { + const unencryptedContentMap = { + "@alice:example.org": { "*": { hello: "alice!" } }, + "@bob:example.org": { bobDesktop: { hello: "bob!" } }, + }; + + it("sends unencrypted (sendToDevice)", async () => { + await makeClient({ sendToDevice: ["org.example.foo"] }); + expect(widgetApi.requestCapabilityToSendToDevice).toHaveBeenCalledWith("org.example.foo"); + + await client.sendToDevice("org.example.foo", unencryptedContentMap); + expect(widgetApi.sendToDevice).toHaveBeenCalledWith("org.example.foo", false, unencryptedContentMap); + }); + + it("sends unencrypted (queueToDevice)", async () => { + await makeClient({ sendToDevice: ["org.example.foo"] }); + expect(widgetApi.requestCapabilityToSendToDevice).toHaveBeenCalledWith("org.example.foo"); + + const batch: ToDeviceBatch = { + eventType: "org.example.foo", + batch: [ + { userId: "@alice:example.org", deviceId: "*", payload: { hello: "alice!" } }, + { userId: "@bob:example.org", deviceId: "bobDesktop", payload: { hello: "bob!" } }, + ], + }; + await client.queueToDevice(batch); + expect(widgetApi.sendToDevice).toHaveBeenCalledWith("org.example.foo", false, unencryptedContentMap); + }); + + it("sends encrypted (encryptAndSendToDevices)", async () => { + await makeClient({ sendToDevice: ["org.example.foo"] }); + expect(widgetApi.requestCapabilityToSendToDevice).toHaveBeenCalledWith("org.example.foo"); + + const payload = { type: "org.example.foo", hello: "world" }; + await client.encryptAndSendToDevices( + [ + { userId: "@alice:example.org", deviceInfo: new DeviceInfo("aliceWeb") }, + { userId: "@bob:example.org", deviceInfo: new DeviceInfo("bobDesktop") }, + ], + payload, + ); + expect(widgetApi.sendToDevice).toHaveBeenCalledWith("org.example.foo", true, { + "@alice:example.org": { aliceWeb: payload }, + "@bob:example.org": { bobDesktop: payload }, + }); + }); + + it.each([ + { encrypted: false, title: "unencrypted" }, + { encrypted: true, title: "encrypted" }, + ])("receives $title", async ({ encrypted }) => { + await makeClient({ receiveToDevice: ["org.example.foo"] }); + expect(widgetApi.requestCapabilityToReceiveToDevice).toHaveBeenCalledWith("org.example.foo"); + + const event = { + type: "org.example.foo", + sender: "@alice:example.org", + encrypted, + content: { hello: "world" }, + }; + + const emittedEvent = new Promise(resolve => client.once(ClientEvent.ToDeviceEvent, resolve)); + const emittedSync = new Promise(resolve => client.once(ClientEvent.Sync, resolve)); + widgetApi.emit( + `action:${WidgetApiToWidgetAction.SendToDevice}`, + new CustomEvent(`action:${WidgetApiToWidgetAction.SendToDevice}`, { detail: { data: event } }), + ); + + expect((await emittedEvent).getEffectiveEvent()).toEqual({ + type: event.type, + sender: event.sender, + content: event.content, + }); + expect((await emittedEvent).isEncrypted()).toEqual(encrypted); + expect(await emittedSync).toEqual(SyncState.Syncing); + }); + }); + + it("gets TURN servers", async () => { + const server1: ITurnServer = { + uris: [ + "turn:turn.example.com:3478?transport=udp", + "turn:10.20.30.40:3478?transport=tcp", + "turns:10.20.30.40:443?transport=tcp", + ], + username: "1443779631:@user:example.com", + password: "JlKfBy1QwLrO20385QyAtEyIv0=", + }; + const server2: ITurnServer = { + uris: [ + "turn:turn.example.com:3478?transport=udp", + "turn:10.20.30.40:3478?transport=tcp", + "turns:10.20.30.40:443?transport=tcp", + ], + username: "1448999322:@user:example.com", + password: "hunter2", + }; + const clientServer1: IClientTurnServer = { + urls: server1.uris, + username: server1.username, + credential: server1.password, + }; + const clientServer2: IClientTurnServer = { + urls: server2.uris, + username: server2.username, + credential: server2.password, + }; + + let emitServer2: () => void; + const getServer2 = new Promise(resolve => emitServer2 = () => resolve(server2)); + widgetApi.getTurnServers.mockImplementation(async function* () { + yield server1; + yield await getServer2; + }); + + await makeClient({ turnServers: true }); + expect(widgetApi.requestCapability).toHaveBeenCalledWith(MatrixCapabilities.MSC3846TurnServers); + + // The first server should've arrived immediately + expect(client.getTurnServers()).toEqual([clientServer1]); + + // Subsequent servers arrive asynchronously and should emit an event + const emittedServer = new Promise(resolve => + client.once(ClientEvent.TurnServers, resolve), + ); + emitServer2(); + expect(await emittedServer).toEqual([clientServer2]); + expect(client.getTurnServers()).toEqual([clientServer2]); + }); +}); diff --git a/src/ToDeviceMessageQueue.ts b/src/ToDeviceMessageQueue.ts index 12827d8bb..66f471c41 100644 --- a/src/ToDeviceMessageQueue.ts +++ b/src/ToDeviceMessageQueue.ts @@ -15,7 +15,7 @@ limitations under the License. */ import { logger } from "./logger"; -import { MatrixClient } from "./matrix"; +import { MatrixClient } from "./client"; import { IndexedToDeviceBatch, ToDeviceBatch, ToDeviceBatchWithTxnId, ToDevicePayload } from "./models/ToDeviceMessage"; import { MatrixScheduler } from "./scheduler"; diff --git a/src/client.ts b/src/client.ts index 45e1cd173..0aa83d740 100644 --- a/src/client.ts +++ b/src/client.ts @@ -72,6 +72,7 @@ import { CryptoEvent, CryptoEventHandlerMap, fixBackupKey, + ICryptoCallbacks, IBootstrapCrossSigningOpts, ICheckOwnCrossSigningTrustOpts, IMegolmSessionData, @@ -101,29 +102,9 @@ import { } from "./crypto/keybackup"; import { IIdentityServerProvider } from "./@types/IIdentityServerProvider"; import { MatrixScheduler } from "./scheduler"; -import { - IAuthData, - ICryptoCallbacks, - IMinimalEvent, - IRoomEvent, - IStateEvent, - NotificationCountType, - BeaconEvent, - BeaconEventHandlerMap, - RoomEvent, - RoomEventHandlerMap, - RoomMemberEvent, - RoomMemberEventHandlerMap, - RoomStateEvent, - RoomStateEventHandlerMap, - INotificationsResponse, - IFilterResponse, - ITagsResponse, - IStatusResponse, - IPushRule, - PushRuleActionName, - IAuthDict, -} from "./matrix"; +import { BeaconEvent, BeaconEventHandlerMap } from "./models/beacon"; +import { IAuthData, IAuthDict } from "./interactive-auth"; +import { IMinimalEvent, IRoomEvent, IStateEvent } from "./sync-accumulator"; import { CrossSigningKey, IAddSecretStorageKeyOpts, @@ -138,7 +119,9 @@ import { VerificationRequest } from "./crypto/verification/request/VerificationR import { VerificationBase as Verification } from "./crypto/verification/Base"; import * as ContentHelpers from "./content-helpers"; import { CrossSigningInfo, DeviceTrustLevel, ICacheCallbacks, UserTrustLevel } from "./crypto/CrossSigning"; -import { Room } from "./models/room"; +import { Room, NotificationCountType, RoomEvent, RoomEventHandlerMap } from "./models/room"; +import { RoomMemberEvent, RoomMemberEventHandlerMap } from "./models/room-member"; +import { RoomStateEvent, RoomStateEventHandlerMap } from "./models/room-state"; import { IAddThreePidOnlyBody, IBindThreePidBody, @@ -156,6 +139,10 @@ import { ISearchOpts, ISendEventResponse, IUploadOpts, + INotificationsResponse, + IFilterResponse, + ITagsResponse, + IStatusResponse, } from "./@types/requests"; import { EventType, @@ -185,7 +172,16 @@ import { } from "./@types/search"; import { ISynapseAdminDeactivateResponse, ISynapseAdminWhoisResponse } from "./@types/synapse"; import { IHierarchyRoom } from "./@types/spaces"; -import { IPusher, IPusherRequest, IPushRules, PushRuleAction, PushRuleKind, RuleId } from "./@types/PushRules"; +import { + IPusher, + IPusherRequest, + IPushRule, + IPushRules, + PushRuleAction, + PushRuleActionName, + PushRuleKind, + RuleId, +} from "./@types/PushRules"; import { IThreepid } from "./@types/threepids"; import { CryptoStore } from "./crypto/store/base"; import { diff --git a/src/crypto/CrossSigning.ts b/src/crypto/CrossSigning.ts index 4e5c9f452..6121d45fd 100644 --- a/src/crypto/CrossSigning.ts +++ b/src/crypto/CrossSigning.ts @@ -29,7 +29,7 @@ import { DeviceInfo } from "./deviceinfo"; import { SecretStorage } from "./SecretStorage"; import { ICrossSigningKey, ISignedKey, MatrixClient } from "../client"; import { OlmDevice } from "./OlmDevice"; -import { ICryptoCallbacks } from "../matrix"; +import { ICryptoCallbacks } from "."; import { ISignatures } from "../@types/signed"; import { CryptoStore } from "./store/base"; import { ISecretStorageKeyInfo } from "./api"; diff --git a/src/crypto/EncryptionSetup.ts b/src/crypto/EncryptionSetup.ts index 61ba34eaf..456c1cac4 100644 --- a/src/crypto/EncryptionSetup.ts +++ b/src/crypto/EncryptionSetup.ts @@ -19,16 +19,15 @@ import { MatrixEvent } from "../models/event"; import { createCryptoStoreCacheCallbacks, ICacheCallbacks } from "./CrossSigning"; import { IndexedDBCryptoStore } from './store/indexeddb-crypto-store'; import { Method, PREFIX_UNSTABLE } from "../http-api"; -import { Crypto, IBootstrapCrossSigningOpts } from "./index"; +import { Crypto, ICryptoCallbacks, IBootstrapCrossSigningOpts } from "./index"; import { ClientEvent, - CrossSigningKeys, ClientEventHandlerMap, + CrossSigningKeys, ICrossSigningKey, - ICryptoCallbacks, ISignedKey, KeySignatures, -} from "../matrix"; +} from "../client"; import { ISecretStorageKeyInfo } from "./api"; import { IKeyBackupInfo } from "./keybackup"; import { TypedEventEmitter } from "../models/typed-event-emitter"; diff --git a/src/crypto/SecretStorage.ts b/src/crypto/SecretStorage.ts index 0eef2ee7d..09bf467bb 100644 --- a/src/crypto/SecretStorage.ts +++ b/src/crypto/SecretStorage.ts @@ -19,8 +19,9 @@ import * as olmlib from './olmlib'; import { encodeBase64 } from './olmlib'; import { randomString } from '../randomstring'; import { calculateKeyCheck, decryptAES, encryptAES, IEncryptedPayload } from './aes'; -import { ClientEvent, ICryptoCallbacks, MatrixEvent } from '../matrix'; -import { ClientEventHandlerMap, MatrixClient } from "../client"; +import { ICryptoCallbacks } from "."; +import { MatrixEvent } from "../models/event"; +import { ClientEvent, ClientEventHandlerMap, MatrixClient } from "../client"; import { IAddSecretStorageKeyOpts, ISecretStorageKeyInfo } from './api'; import { TypedEventEmitter } from '../models/typed-event-emitter'; diff --git a/src/crypto/index.ts b/src/crypto/index.ts index 451d2d8c8..e6d473786 100644 --- a/src/crypto/index.ts +++ b/src/crypto/index.ts @@ -126,6 +126,29 @@ export interface IBootstrapCrossSigningOpts { authUploadDeviceSigningKeys?(makeRequest: (authData: any) => Promise<{}>): Promise; } +export interface ICryptoCallbacks { + getCrossSigningKey?: (keyType: string, pubKey: string) => Promise; + saveCrossSigningKeys?: (keys: Record) => void; + shouldUpgradeDeviceVerifications?: ( + users: Record + ) => Promise; + getSecretStorageKey?: ( + keys: {keys: Record}, name: string + ) => Promise<[string, Uint8Array] | null>; + cacheSecretStorageKey?: ( + keyId: string, keyInfo: ISecretStorageKeyInfo, key: Uint8Array + ) => void; + onSecretRequested?: ( + userId: string, deviceId: string, + requestId: string, secretName: string, deviceTrust: DeviceTrustLevel + ) => Promise; + getDehydrationKey?: ( + keyInfo: ISecretStorageKeyInfo, + checkFunc: (key: Uint8Array) => void, + ) => Promise; + getBackupKey?: () => Promise; +} + /* eslint-disable camelcase */ interface IRoomKey { room_id: string; diff --git a/src/embedded.ts b/src/embedded.ts new file mode 100644 index 000000000..047cd0279 --- /dev/null +++ b/src/embedded.ts @@ -0,0 +1,258 @@ +/* +Copyright 2022 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { + WidgetApi, + WidgetApiToWidgetAction, + MatrixCapabilities, + IWidgetApiRequest, + IWidgetApiAcknowledgeResponseData, + ISendEventToWidgetActionRequest, + ISendToDeviceToWidgetActionRequest, +} from "matrix-widget-api"; + +import { ISendEventResponse } from "./@types/requests"; +import { EventType } from "./@types/event"; +import { logger } from "./logger"; +import { MatrixClient, ClientEvent, IMatrixClientCreateOpts, IStartClientOpts } from "./client"; +import { SyncApi, SyncState } from "./sync"; +import { SlidingSyncSdk } from "./sliding-sync-sdk"; +import { MatrixEvent } from "./models/event"; +import { User } from "./models/user"; +import { Room } from "./models/room"; +import { ToDeviceBatch } from "./models/ToDeviceMessage"; +import { DeviceInfo } from "./crypto/deviceinfo"; +import { IOlmDevice } from "./crypto/algorithms/megolm"; + +interface IStateEventRequest { + eventType: string; + stateKey?: string; +} + +export interface ICapabilities { + // TODO: Add fields for messages and other non-state events + + sendState?: IStateEventRequest[]; + receiveState?: IStateEventRequest[]; + + sendToDevice?: string[]; + receiveToDevice?: string[]; + + turnServers?: boolean; +} + +export class RoomWidgetClient extends MatrixClient { + private room: Room; + private widgetApiReady = new Promise(resolve => this.widgetApi.once("ready", resolve)); + private lifecycle: AbortController; + private syncState: SyncState | null = null; + + constructor( + private readonly widgetApi: WidgetApi, + private readonly capabilities: ICapabilities, + private readonly roomId: string, + opts: IMatrixClientCreateOpts, + ) { + super(opts); + + // Request capabilities for the functionality this client needs to support + this.capabilities.sendState?.forEach(({ eventType, stateKey }) => + this.widgetApi.requestCapabilityToSendState(eventType, stateKey), + ); + this.capabilities.receiveState?.forEach(({ eventType, stateKey }) => + this.widgetApi.requestCapabilityToReceiveState(eventType, stateKey), + ); + this.capabilities.sendToDevice?.forEach(eventType => + this.widgetApi.requestCapabilityToSendToDevice(eventType), + ); + this.capabilities.receiveToDevice?.forEach(eventType => + this.widgetApi.requestCapabilityToReceiveToDevice(eventType), + ); + if (this.capabilities.turnServers) { + this.widgetApi.requestCapability(MatrixCapabilities.MSC3846TurnServers); + } + + this.widgetApi.on(`action:${WidgetApiToWidgetAction.SendEvent}`, this.onEvent); + this.widgetApi.on(`action:${WidgetApiToWidgetAction.SendToDevice}`, this.onToDevice); + + // Open communication with the host + this.widgetApi.start(); + } + + public async startClient(opts: IStartClientOpts = {}): Promise { + this.lifecycle = new AbortController(); + + // Create our own user object artificially (instead of waiting for sync) + // so it's always available, even if the user is not in any rooms etc. + const userId = this.getUserId(); + if (userId) { + this.store.storeUser(new User(userId)); + } + + // Even though we have no access token and cannot sync, the sync class + // still has some valuable helper methods that we make use of, so we + // instantiate it anyways + if (opts.slidingSync) { + this.syncApi = new SlidingSyncSdk(opts.slidingSync, this, opts); + } else { + this.syncApi = new SyncApi(this, opts); + } + + this.room = this.syncApi.createRoom(this.roomId); + this.store.storeRoom(this.room); + + await this.widgetApiReady; + + // Backfill the requested events + // We only get the most recent event for every type + state key combo, + // so it doesn't really matter what order we inject them in + await Promise.all( + this.capabilities.receiveState?.map(async ({ eventType, stateKey }) => { + const rawEvents = await this.widgetApi.readStateEvents(eventType, undefined, stateKey); + const events = rawEvents.map(rawEvent => new MatrixEvent(rawEvent)); + + await this.syncApi.injectRoomEvents(this.room, [], events); + events.forEach(event => { + this.emit(ClientEvent.Event, event); + logger.info(`Backfilled event ${event.getId()} ${event.getType()} ${event.getStateKey()}`); + }); + }) ?? [], + ); + this.setSyncState(SyncState.Prepared); + logger.info("Finished backfilling events"); + + // Watch for TURN servers, if requested + if (this.capabilities.turnServers) this.watchTurnServers(); + } + + public stopClient() { + this.widgetApi.off(`action:${WidgetApiToWidgetAction.SendEvent}`, this.onEvent); + this.widgetApi.off(`action:${WidgetApiToWidgetAction.SendToDevice}`, this.onToDevice); + + super.stopClient(); + this.lifecycle.abort(); // Signal to other async tasks that the client has stopped + } + + public async sendStateEvent( + roomId: string, + eventType: string, + content: any, + stateKey = "", + ): Promise { + if (roomId !== this.roomId) throw new Error(`Can't send events to ${roomId}`); + return await this.widgetApi.sendStateEvent(eventType, stateKey, content); + } + + public async sendToDevice( + eventType: string, + contentMap: { [userId: string]: { [deviceId: string]: Record } }, + ): Promise<{}> { + await this.widgetApi.sendToDevice(eventType, false, contentMap); + return {}; + } + + public async queueToDevice({ eventType, batch }: ToDeviceBatch): Promise { + const contentMap: { [userId: string]: { [deviceId: string]: object } } = {}; + for (const { userId, deviceId, payload } of batch) { + if (!contentMap[userId]) contentMap[userId] = {}; + contentMap[userId][deviceId] = payload; + } + + await this.widgetApi.sendToDevice(eventType, false, contentMap); + } + + public async encryptAndSendToDevices( + userDeviceInfoArr: IOlmDevice[], + payload: object, + ): Promise { + const contentMap: { [userId: string]: { [deviceId: string]: object } } = {}; + for (const { userId, deviceInfo: { deviceId } } of userDeviceInfoArr) { + if (!contentMap[userId]) contentMap[userId] = {}; + contentMap[userId][deviceId] = payload; + } + + await this.widgetApi.sendToDevice((payload as { type: string }).type, true, contentMap); + } + + // Overridden since we get TURN servers automatically over the widget API, + // and this method would otherwise complain about missing an access token + public async checkTurnServers(): Promise { + return this.turnServers.length > 0; + } + + // Overridden since we 'sync' manually without the sync API + public getSyncState(): SyncState { + return this.syncState; + } + + private setSyncState(state: SyncState) { + const oldState = this.syncState; + this.syncState = state; + this.emit(ClientEvent.Sync, state, oldState); + } + + private async ack(ev: CustomEvent): Promise { + await this.widgetApi.transport.reply(ev.detail, {}); + } + + private onEvent = async (ev: CustomEvent) => { + ev.preventDefault(); + const event = new MatrixEvent(ev.detail.data); + await this.syncApi.injectRoomEvents(this.room, [], [event]); + this.emit(ClientEvent.Event, event); + this.setSyncState(SyncState.Syncing); + logger.info(`Received event ${event.getId()} ${event.getType()} ${event.getStateKey()}`); + await this.ack(ev); + }; + + private onToDevice = async (ev: CustomEvent) => { + ev.preventDefault(); + + const event = new MatrixEvent({ + type: ev.detail.data.type, + sender: ev.detail.data.sender, + content: ev.detail.data.content, + }); + // Mark the event as encrypted if it was, using fake contents and keys since those are unknown to us + if (ev.detail.data.encrypted) event.makeEncrypted(EventType.RoomMessageEncrypted, {}, "", ""); + + this.emit(ClientEvent.ToDeviceEvent, event); + this.setSyncState(SyncState.Syncing); + await this.ack(ev); + }; + + private async watchTurnServers() { + const servers = this.widgetApi.getTurnServers(); + const onClientStopped = () => servers.return(undefined); + this.lifecycle.signal.addEventListener("abort", onClientStopped); + + try { + for await (const server of servers) { + this.turnServers = [{ + urls: server.uris, + username: server.username, + credential: server.password, + }]; + this.emit(ClientEvent.TurnServers, this.turnServers); + logger.log(`Received TURN server: ${server.uris}`); + } + } catch (e) { + logger.warn("Error watching TURN servers", e); + } finally { + this.lifecycle.signal.removeEventListener("abort", onClientStopped); + } + } +} diff --git a/src/matrix.ts b/src/matrix.ts index 646f88798..4fbd30773 100644 --- a/src/matrix.ts +++ b/src/matrix.ts @@ -1,5 +1,5 @@ /* -Copyright 2015-2021 The Matrix.org Foundation C.I.C. +Copyright 2015-2022 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,14 +14,16 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { WidgetApi } from "matrix-widget-api"; + import { MemoryCryptoStore } from "./crypto/store/memory-crypto-store"; import { MemoryStore } from "./store/memory"; import { MatrixScheduler } from "./scheduler"; import { MatrixClient, ICreateClientOpts } from "./client"; -import { DeviceTrustLevel } from "./crypto/CrossSigning"; -import { ISecretStorageKeyInfo } from "./crypto/api"; +import { RoomWidgetClient, ICapabilities } from "./embedded"; export * from "./client"; +export * from "./embedded"; export * from "./http-api"; export * from "./autodiscovery"; export * from "./sync-accumulator"; @@ -51,6 +53,7 @@ export * from './@types/requests'; export * from './@types/search'; export * from './models/room-summary'; export * as ContentHelpers from "./content-helpers"; +export type { ICryptoCallbacks } from "./crypto"; // used to be located here export { createNewMatrixCall } from "./webrtc/call"; export type { MatrixCall } from "./webrtc/call"; export { @@ -108,27 +111,17 @@ export function setCryptoStoreFactory(fac) { cryptoStoreFactory = fac; } -export interface ICryptoCallbacks { - getCrossSigningKey?: (keyType: string, pubKey: string) => Promise; - saveCrossSigningKeys?: (keys: Record) => void; - shouldUpgradeDeviceVerifications?: ( - users: Record - ) => Promise; - getSecretStorageKey?: ( - keys: {keys: Record}, name: string - ) => Promise<[string, Uint8Array] | null>; - cacheSecretStorageKey?: ( - keyId: string, keyInfo: ISecretStorageKeyInfo, key: Uint8Array - ) => void; - onSecretRequested?: ( - userId: string, deviceId: string, - requestId: string, secretName: string, deviceTrust: DeviceTrustLevel - ) => Promise; - getDehydrationKey?: ( - keyInfo: ISecretStorageKeyInfo, - checkFunc: (key: Uint8Array) => void, - ) => Promise; - getBackupKey?: () => Promise; +function amendClientOpts(opts: ICreateClientOpts | string): ICreateClientOpts { + if (typeof opts === "string") opts = { baseUrl: opts }; + + opts.request = opts.request ?? requestInstance; + opts.store = opts.store ?? new MemoryStore({ + localStorage: global.localStorage, + }); + opts.scheduler = opts.scheduler ?? new MatrixScheduler(); + opts.cryptoStore = opts.cryptoStore ?? cryptoStoreFactory(); + + return opts; } /** @@ -154,19 +147,17 @@ export interface ICryptoCallbacks { * @see {@link module:client.MatrixClient} for the full list of options for * opts. */ -export function createClient(opts: ICreateClientOpts | string) { - if (typeof opts === "string") { - opts = { - "baseUrl": opts, - }; - } - opts.request = opts.request || requestInstance; - opts.store = opts.store || new MemoryStore({ - localStorage: global.localStorage, - }); - opts.scheduler = opts.scheduler || new MatrixScheduler(); - opts.cryptoStore = opts.cryptoStore || cryptoStoreFactory(); - return new MatrixClient(opts); +export function createClient(opts: ICreateClientOpts | string): MatrixClient { + return new MatrixClient(amendClientOpts(opts)); +} + +export function createRoomWidgetClient( + widgetApi: WidgetApi, + capabilities: ICapabilities, + roomId: string, + opts: ICreateClientOpts | string, +): MatrixClient { + return new RoomWidgetClient(widgetApi, capabilities, roomId, amendClientOpts(opts)); } /** diff --git a/src/models/beacon.ts b/src/models/beacon.ts index 9df62bbe2..12db328b1 100644 --- a/src/models/beacon.ts +++ b/src/models/beacon.ts @@ -17,7 +17,7 @@ limitations under the License. import { MBeaconEventContent } from "../@types/beacon"; import { M_TIMESTAMP } from "../@types/location"; import { BeaconInfoState, BeaconLocationState, parseBeaconContent, parseBeaconInfoContent } from "../content-helpers"; -import { MatrixEvent } from "../matrix"; +import { MatrixEvent } from "./event"; import { sortEventsByLatestContentTimestamp } from "../utils"; import { TypedEventEmitter } from "./typed-event-emitter"; diff --git a/src/models/thread.ts b/src/models/thread.ts index c451ccb8d..a68b88c04 100644 --- a/src/models/thread.ts +++ b/src/models/thread.ts @@ -16,13 +16,14 @@ limitations under the License. import { Optional } from "matrix-events-sdk"; -import { MatrixClient, MatrixEventEvent, RelationType, RoomEvent } from "../matrix"; +import { MatrixClient } from "../client"; import { TypedReEmitter } from "../ReEmitter"; import { IRelationsRequestOpts } from "../@types/requests"; -import { IThreadBundledRelationship, MatrixEvent } from "./event"; +import { RelationType } from "../@types/event"; +import { IThreadBundledRelationship, MatrixEvent, MatrixEventEvent } from "./event"; import { Direction, EventTimeline } from "./event-timeline"; import { EventTimelineSet, EventTimelineSetHandlerMap } from './event-timeline-set'; -import { Room } from './room'; +import { Room, RoomEvent } from './room'; import { TypedEventEmitter } from "./typed-event-emitter"; import { RoomState } from "./room-state"; import { ServerControlledNamespacedValue } from "../NamespacedValue"; diff --git a/src/sliding-sync-sdk.ts b/src/sliding-sync-sdk.ts index 39fafec90..e2dfcc381 100644 --- a/src/sliding-sync-sdk.ts +++ b/src/sliding-sync-sdk.ts @@ -35,7 +35,8 @@ import { SlidingSyncEvent, SlidingSyncState, } from "./sliding-sync"; -import { EventType, IPushRules } from "./matrix"; +import { EventType } from "./@types/event"; +import { IPushRules } from "./@types/PushRules"; import { PushProcessor } from "./pushprocessor"; // Number of consecutive failed syncs that will lead to a syncState of ERROR as opposed @@ -290,7 +291,7 @@ export class SlidingSyncSdk { logger.debug("initial flag not set but no stored room exists for room ", roomId, roomData); return; } - room = createRoom(this.client, roomId, this.opts); + room = this.createRoom(roomId); } this.processRoomData(this.client, room, roomData); } @@ -388,6 +389,60 @@ export class SlidingSyncSdk { return this.syncStateData; } + // Helper functions which set up JS SDK structs are below and are identical to the sync v2 counterparts + + public createRoom(roomId: string): Room { // XXX cargoculted from sync.ts + const { timelineSupport } = this.client; + const room = new Room(roomId, this.client, this.client.getUserId(), { + lazyLoadMembers: this.opts.lazyLoadMembers, + pendingEventOrdering: this.opts.pendingEventOrdering, + timelineSupport, + }); + this.client.reEmitter.reEmit(room, [ + RoomEvent.Name, + RoomEvent.Redaction, + RoomEvent.RedactionCancelled, + RoomEvent.Receipt, + RoomEvent.Tags, + RoomEvent.LocalEchoUpdated, + RoomEvent.AccountData, + RoomEvent.MyMembership, + RoomEvent.Timeline, + RoomEvent.TimelineReset, + ]); + this.registerStateListeners(room); + return room; + } + + private registerStateListeners(room: Room): void { // XXX cargoculted from sync.ts + // we need to also re-emit room state and room member events, so hook it up + // to the client now. We need to add a listener for RoomState.members in + // order to hook them correctly. + this.client.reEmitter.reEmit(room.currentState, [ + RoomStateEvent.Events, + RoomStateEvent.Members, + RoomStateEvent.NewMember, + RoomStateEvent.Update, + ]); + room.currentState.on(RoomStateEvent.NewMember, (event, state, member) => { + member.user = this.client.getUser(member.userId); + this.client.reEmitter.reEmit(member, [ + RoomMemberEvent.Name, + RoomMemberEvent.Typing, + RoomMemberEvent.PowerLevel, + RoomMemberEvent.Membership, + ]); + }); + } + + /* + private deregisterStateListeners(room: Room): void { // XXX cargoculted from sync.ts + // could do with a better way of achieving this. + room.currentState.removeAllListeners(RoomStateEvent.Events); + room.currentState.removeAllListeners(RoomStateEvent.Members); + room.currentState.removeAllListeners(RoomStateEvent.NewMember); + } */ + private shouldAbortSync(error: MatrixError): boolean { if (error.errcode === "M_UNKNOWN_TOKEN") { // The logout already happened, we just need to stop. @@ -434,7 +489,7 @@ export class SlidingSyncSdk { if (roomData.invite_state) { const inviteStateEvents = mapEvents(this.client, room.roomId, roomData.invite_state); - this.processRoomEvents(room, inviteStateEvents); + this.injectRoomEvents(room, inviteStateEvents); if (roomData.initial) { room.recalculate(); this.client.store.storeRoom(room); @@ -504,11 +559,11 @@ export class SlidingSyncSdk { // reason to stop incrementally tracking notifications and // reset the timeline. this.client.resetNotifTimelineSet(); - registerStateListeners(this.client, room); + this.registerStateListeners(room); } } */ - this.processRoomEvents(room, stateEvents, timelineEvents, false); + this.injectRoomEvents(room, stateEvents, timelineEvents, false); // we deliberately don't add ephemeral events to the timeline room.addEphemeralEvents(ephemeralEvents); @@ -545,6 +600,7 @@ export class SlidingSyncSdk { } /** + * Injects events into a room's model. * @param {Room} room * @param {MatrixEvent[]} stateEventList A list of state events. This is the state * at the *START* of the timeline list if it is supplied. @@ -552,7 +608,7 @@ export class SlidingSyncSdk { * @param {boolean} fromCache whether the sync response came from cache * is earlier in time. Higher index is later. */ - private processRoomEvents( + public injectRoomEvents( room: Room, stateEventList: MatrixEvent[], timelineEventList?: MatrixEvent[], @@ -771,61 +827,6 @@ function ensureNameEvent(client: MatrixClient, roomId: string, roomData: MSC3575 return roomData; } -// Helper functions which set up JS SDK structs are below and are identical to the sync v2 counterparts, -// just outside the class. - -function createRoom(client: MatrixClient, roomId: string, opts: Partial): Room { // XXX cargoculted from sync.ts - const { timelineSupport } = client; - const room = new Room(roomId, client, client.getUserId(), { - lazyLoadMembers: opts.lazyLoadMembers, - pendingEventOrdering: opts.pendingEventOrdering, - timelineSupport, - }); - client.reEmitter.reEmit(room, [ - RoomEvent.Name, - RoomEvent.Redaction, - RoomEvent.RedactionCancelled, - RoomEvent.Receipt, - RoomEvent.Tags, - RoomEvent.LocalEchoUpdated, - RoomEvent.AccountData, - RoomEvent.MyMembership, - RoomEvent.Timeline, - RoomEvent.TimelineReset, - ]); - registerStateListeners(client, room); - return room; -} - -function registerStateListeners(client: MatrixClient, room: Room): void { // XXX cargoculted from sync.ts - // we need to also re-emit room state and room member events, so hook it up - // to the client now. We need to add a listener for RoomState.members in - // order to hook them correctly. - client.reEmitter.reEmit(room.currentState, [ - RoomStateEvent.Events, - RoomStateEvent.Members, - RoomStateEvent.NewMember, - RoomStateEvent.Update, - ]); - room.currentState.on(RoomStateEvent.NewMember, function(event, state, member) { - member.user = client.getUser(member.userId); - client.reEmitter.reEmit(member, [ - RoomMemberEvent.Name, - RoomMemberEvent.Typing, - RoomMemberEvent.PowerLevel, - RoomMemberEvent.Membership, - ]); - }); -} - -/* -function deregisterStateListeners(room: Room): void { // XXX cargoculted from sync.ts - // could do with a better way of achieving this. - room.currentState.removeAllListeners(RoomStateEvent.Events); - room.currentState.removeAllListeners(RoomStateEvent.Members); - room.currentState.removeAllListeners(RoomStateEvent.NewMember); -} */ - function mapEvents(client: MatrixClient, roomId: string, events: object[], decrypt = true): MatrixEvent[] { const mapper = client.getEventMapper({ decrypt }); return (events as Array).map(function(e) { diff --git a/src/sync.ts b/src/sync.ts index 4abd4fb5b..3e1240821 100644 --- a/src/sync.ts +++ b/src/sync.ts @@ -398,7 +398,7 @@ export class SyncApi { // events so that clients can start back-paginating. room.getLiveTimeline().setPaginationToken(leaveObj.timeline.prev_batch, EventTimeline.BACKWARDS); - await this.processRoomEvents(room, stateEvents, events); + await this.injectRoomEvents(room, stateEvents, events); room.recalculate(); client.store.storeRoom(room); @@ -430,7 +430,7 @@ export class SyncApi { response.messages.chunk = response.messages.chunk || []; response.state = response.state || []; - // FIXME: Mostly duplicated from processRoomEvents but not entirely + // FIXME: Mostly duplicated from injectRoomEvents but not entirely // because "state" in this API is at the BEGINNING of the chunk const oldStateEvents = utils.deepCopy(response.state) .map(client.getEventMapper()); @@ -1252,7 +1252,7 @@ export class SyncApi { const room = inviteObj.room; const stateEvents = this.mapSyncEventsFormat(inviteObj.invite_state, room); - await this.processRoomEvents(room, stateEvents); + await this.injectRoomEvents(room, stateEvents); if (inviteObj.isBrandNewRoom) { room.recalculate(); client.store.storeRoom(room); @@ -1358,7 +1358,7 @@ export class SyncApi { } } - await this.processRoomEvents(room, stateEvents, events, syncEventData.fromCache); + await this.injectRoomEvents(room, stateEvents, events, syncEventData.fromCache); // set summary after processing events, // because it will trigger a name calculation @@ -1410,7 +1410,7 @@ export class SyncApi { const events = this.mapSyncEventsFormat(leaveObj.timeline, room); const accountDataEvents = this.mapSyncEventsFormat(leaveObj.account_data); - await this.processRoomEvents(room, stateEvents, events); + await this.injectRoomEvents(room, stateEvents, events); room.addAccountData(accountDataEvents); room.recalculate(); @@ -1649,14 +1649,15 @@ export class SyncApi { } /** + * Injects events into a room's model. * @param {Room} room * @param {MatrixEvent[]} stateEventList A list of state events. This is the state * at the *START* of the timeline list if it is supplied. * @param {MatrixEvent[]} [timelineEventList] A list of timeline events, including threaded. Lower index - * @param {boolean} fromCache whether the sync response came from cache * is earlier in time. Higher index is later. + * @param {boolean} fromCache whether the sync response came from cache */ - private async processRoomEvents( + public async injectRoomEvents( room: Room, stateEventList: MatrixEvent[], timelineEventList?: MatrixEvent[], diff --git a/src/utils.ts b/src/utils.ts index 6cf459097..18d6c29a0 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -24,7 +24,7 @@ import unhomoglyph from "unhomoglyph"; import promiseRetry from "p-retry"; import type * as NodeCrypto from "crypto"; -import { MatrixEvent } from "."; +import { MatrixEvent } from "./models/event"; import { M_TIMESTAMP } from "./@types/location"; /** diff --git a/src/webrtc/call.ts b/src/webrtc/call.ts index e1932cdf9..9bd2473ae 100644 --- a/src/webrtc/call.ts +++ b/src/webrtc/call.ts @@ -557,6 +557,13 @@ export class MatrixCall extends TypedEventEmitter candidate.toJSON()) }; logger.debug(`Call ${this.callId} attempting to send ${candidates.length} candidates`); try { await this.sendVoipEvent(EventType.CallCandidates, content); diff --git a/src/webrtc/callEventHandler.ts b/src/webrtc/callEventHandler.ts index 1d1307399..7c926be6b 100644 --- a/src/webrtc/callEventHandler.ts +++ b/src/webrtc/callEventHandler.ts @@ -269,6 +269,8 @@ export class CallEventHandler { } catch (e) { if (e.code === GroupCallErrorCode.UnknownDevice) { groupCall?.emit(GroupCallEvent.Error, e); + } else { + logger.error(e); } } this.calls.set(call.callId, call); diff --git a/src/webrtc/groupCallEventHandler.ts b/src/webrtc/groupCallEventHandler.ts index b30f0dd8a..9d7f2e5af 100644 --- a/src/webrtc/groupCallEventHandler.ts +++ b/src/webrtc/groupCallEventHandler.ts @@ -16,7 +16,7 @@ limitations under the License. import { MatrixEvent } from '../models/event'; import { RoomStateEvent } from '../models/room-state'; -import { MatrixClient } from '../client'; +import { MatrixClient, ClientEvent } from '../client'; import { GroupCall, GroupCallIntent, @@ -25,9 +25,9 @@ import { } from "./groupCall"; import { Room } from "../models/room"; import { RoomState } from "../models/room-state"; +import { RoomMember } from "../models/room-member"; import { logger } from '../logger'; import { EventType } from "../@types/event"; -import { ClientEvent, RoomMember } from '../matrix'; export enum GroupCallEventHandlerEvent { Incoming = "GroupCall.incoming", diff --git a/yarn.lock b/yarn.lock index 9476b4d34..09cc30f64 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1455,6 +1455,11 @@ dependencies: "@sinonjs/commons" "^1.7.0" +"@tootallnate/once@2": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" + integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== + "@types/babel-types@*", "@types/babel-types@^7.0.0": version "7.0.11" resolved "https://registry.yarnpkg.com/@types/babel-types/-/babel-types-7.0.11.tgz#263b113fa396fac4373188d73225297fb86f19a9" @@ -1517,6 +1522,11 @@ resolved "https://registry.yarnpkg.com/@types/content-type/-/content-type-1.1.5.tgz#aa02dca40864749a9e2bf0161a6216da57e3ede5" integrity sha512-dgMN+syt1xb7Hk8LU6AODOfPlvz5z1CbXpPuJE5ZrX9STfBOIXF09pEB8N7a97WT9dbngt3ksDCm6GW6yMrxfQ== +"@types/events@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@types/events/-/events-3.0.0.tgz#2862f3f58a9a7f7c3e78d79f130dd4d71c25c2a7" + integrity sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== + "@types/graceful-fs@^4.1.3": version "4.1.5" resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" @@ -1551,6 +1561,15 @@ jest-matcher-utils "^28.0.0" pretty-format "^28.0.0" +"@types/jsdom@^16.2.4": + version "16.2.15" + resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-16.2.15.tgz#6c09990ec43b054e49636cba4d11d54367fc90d6" + integrity sha512-nwF87yjBKuX/roqGYerZZM0Nv1pZDMAT5YhOHYeM/72Fic+VEqJh4nyoqoapzJnW3pUlfxPY5FhgsJtM+dRnQQ== + dependencies: + "@types/node" "*" + "@types/parse5" "^6.0.3" + "@types/tough-cookie" "*" + "@types/json-schema@^7.0.9": version "7.0.11" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" @@ -1589,6 +1608,11 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.45.tgz#155b13a33c665ef2b136f7f245fa525da419e810" integrity sha512-3rKg/L5x0rofKuuUt5zlXzOnKyIHXmIu5R8A0TuNDMF2062/AOIDBciFIjToLEJ/9F9DzkHNot+BpNsMI1OLdQ== +"@types/parse5@^6.0.3": + version "6.0.3" + resolved "https://registry.yarnpkg.com/@types/parse5/-/parse5-6.0.3.tgz#705bb349e789efa06f43f128cef51240753424cb" + integrity sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g== + "@types/prettier@^2.1.5": version "2.6.4" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.4.tgz#ad899dad022bab6b5a9f0a0fe67c2f7a4a8950ed" @@ -1724,6 +1748,11 @@ JSONStream@^1.0.3: jsonparse "^1.2.0" through ">=2.2.7 <3" +abab@^2.0.5, abab@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" + integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== + ace-builds@^1.4.13: version "1.8.1" resolved "https://registry.yarnpkg.com/ace-builds/-/ace-builds-1.8.1.tgz#5d318fa13d7e6ea947f8a50e42c570c573b29529" @@ -1736,6 +1765,14 @@ acorn-globals@^3.0.0: dependencies: acorn "^4.0.4" +acorn-globals@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" + integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== + dependencies: + acorn "^7.1.1" + acorn-walk "^7.1.1" + acorn-jsx@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -1750,7 +1787,7 @@ acorn-node@^1.2.0, acorn-node@^1.3.0, acorn-node@^1.5.2, acorn-node@^1.8.2: acorn-walk "^7.0.0" xtend "^4.0.2" -acorn-walk@^7.0.0: +acorn-walk@^7.0.0, acorn-walk@^7.1.1: version "7.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== @@ -1765,7 +1802,7 @@ acorn@^4.0.4, acorn@~4.0.2: resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" integrity sha512-fu2ygVGuMmlzG8ZeRJ0bvR41nsAkxxhbyk8bZ1SS521Z7vmgJFTQQlfz/Mp/nJexGBz+v8sC9bM6+lNgskt4Ug== -acorn@^7.0.0: +acorn@^7.0.0, acorn@^7.1.1: version "7.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== @@ -1775,6 +1812,13 @@ acorn@^8.5.0, acorn@^8.7.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + ajv@^6.10.0, ajv@^6.12.3, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -2191,6 +2235,11 @@ browser-pack@^6.0.1: through2 "^2.0.0" umd "^3.0.0" +browser-process-hrtime@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" + integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== + browser-request@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/browser-request/-/browser-request-0.3.3.tgz#9ece5b5aca89a29932242e18bf933def9876cc17" @@ -2592,7 +2641,7 @@ combine-source-map@^0.8.0, combine-source-map@~0.8.0: lodash.memoize "~3.0.3" source-map "~0.5.3" -combined-stream@^1.0.6, combined-stream@~1.0.6: +combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== @@ -2746,6 +2795,23 @@ crypto-browserify@^3.0.0: randombytes "^2.0.0" randomfill "^1.0.3" +cssom@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" + integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw== + +cssom@~0.3.6: + version "0.3.8" + resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" + integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== + +cssstyle@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" + integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== + dependencies: + cssom "~0.3.6" + d@1, d@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" @@ -2766,11 +2832,27 @@ dashdash@^1.12.0: dependencies: assert-plus "^1.0.0" +data-urls@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143" + integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ== + dependencies: + abab "^2.0.6" + whatwg-mimetype "^3.0.0" + whatwg-url "^11.0.0" + de-indent@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" integrity sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg== +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -2785,24 +2867,22 @@ debug@^3.2.7: dependencies: ms "^2.1.1" -debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - decamelize@^1.0.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== +decimal.js@^10.3.1: + version "10.3.1" + resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" + integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== + dedent@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== -deep-is@^0.1.3: +deep-is@^0.1.3, deep-is@~0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== @@ -2929,6 +3009,13 @@ domexception@^1.0.1: dependencies: webidl-conversions "^4.0.2" +domexception@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" + integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== + dependencies: + webidl-conversions "^7.0.0" + duplexer2@^0.1.2, duplexer2@~0.1.0, duplexer2@~0.1.2: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1" @@ -3085,6 +3172,18 @@ escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== +escodegen@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" + integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== + dependencies: + esprima "^4.0.1" + estraverse "^5.2.0" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.6.1" + eslint-config-google@^0.14.0: version "0.14.0" resolved "https://registry.yarnpkg.com/eslint-config-google/-/eslint-config-google-0.14.0.tgz#4f5f8759ba6e11b424294a219dbfa18c508bcc1a" @@ -3218,7 +3317,7 @@ espree@^9.3.2: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.3.0" -esprima@^4.0.0, esprima@~4.0.0: +esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== @@ -3269,7 +3368,7 @@ event-emitter@^0.3.5: d "1" es5-ext "~0.10.14" -events@^3.0.0: +events@^3.0.0, events@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== @@ -3373,7 +3472,7 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@^2.0.6: +fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== @@ -3492,6 +3591,15 @@ form-data@^2.5.0: combined-stream "^1.0.6" mime-types "^2.1.12" +form-data@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" + integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + form-data@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" @@ -3735,6 +3843,13 @@ hmac-drbg@^1.0.1: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.1" +html-encoding-sniffer@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" + integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA== + dependencies: + whatwg-encoding "^2.0.0" + html-escaper@^2.0.0: version "2.0.2" resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" @@ -3745,6 +3860,15 @@ htmlescape@^1.1.0: resolved "https://registry.yarnpkg.com/htmlescape/-/htmlescape-1.1.1.tgz#3a03edc2214bca3b66424a3e7959349509cb0351" integrity sha512-eVcrzgbR4tim7c7soKQKtxa/kQM4TzjnlU83rcZ9bHU6t31ehfV7SktN6McWgwPWg+JYMA/O3qpGxBvFq1z2Jg== +http-proxy-agent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" + integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== + dependencies: + "@tootallnate/once" "2" + agent-base "6" + debug "4" + http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -3759,11 +3883,26 @@ https-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" integrity sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg== +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + human-signals@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +iconv-lite@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + ieee754@^1.1.4: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" @@ -3970,6 +4109,11 @@ is-plain-object@^5.0.0: resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q== +is-potential-custom-element-name@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" + integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== + is-promise@^2.0.0, is-promise@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" @@ -4206,6 +4350,20 @@ jest-each@^28.1.3: jest-util "^28.1.3" pretty-format "^28.1.3" +jest-environment-jsdom@^28.1.3: + version "28.1.3" + resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-28.1.3.tgz#2d4e5d61b7f1d94c3bddfbb21f0308ee506c09fb" + integrity sha512-HnlGUmZRdxfCByd3GM2F100DgQOajUBzEitjGqIREcb45kGjZvRrKUdlaF6escXBdcXNl0OBh+1ZrfeZT3GnAg== + dependencies: + "@jest/environment" "^28.1.3" + "@jest/fake-timers" "^28.1.3" + "@jest/types" "^28.1.3" + "@types/jsdom" "^16.2.4" + "@types/node" "*" + jest-mock "^28.1.3" + jest-util "^28.1.3" + jsdom "^19.0.0" + jest-environment-node@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-28.1.3.tgz#7e74fe40eb645b9d56c0c4b70ca4357faa349be5" @@ -4527,6 +4685,39 @@ jsdoc@^3.6.6: taffydb "2.6.2" underscore "~1.13.2" +jsdom@^19.0.0: + version "19.0.0" + resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-19.0.0.tgz#93e67c149fe26816d38a849ea30ac93677e16b6a" + integrity sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A== + dependencies: + abab "^2.0.5" + acorn "^8.5.0" + acorn-globals "^6.0.0" + cssom "^0.5.0" + cssstyle "^2.3.0" + data-urls "^3.0.1" + decimal.js "^10.3.1" + domexception "^4.0.0" + escodegen "^2.0.0" + form-data "^4.0.0" + html-encoding-sniffer "^3.0.0" + http-proxy-agent "^5.0.0" + https-proxy-agent "^5.0.0" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.0" + parse5 "6.0.1" + saxes "^5.0.1" + symbol-tree "^3.2.4" + tough-cookie "^4.0.0" + w3c-hr-time "^1.0.2" + w3c-xmlserializer "^3.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^2.0.0" + whatwg-mimetype "^3.0.0" + whatwg-url "^10.0.0" + ws "^8.2.3" + xml-name-validator "^4.0.0" + jsesc@^2.5.1: version "2.5.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" @@ -4647,6 +4838,14 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" @@ -4813,6 +5012,14 @@ matrix-mock-request@^2.1.2: dependencies: expect "^28.1.0" +matrix-widget-api@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-1.0.0.tgz#0cde6839cca66ad817ab12aca3490ccc8bac97d1" + integrity sha512-cy8p/8EteRPTFIAw7Q9EgPUJc2jD19ZahMR8bMKf2NkILDcjuPMC0UWnsJyB3fSnlGw+VbGepttRpULM31zX8Q== + dependencies: + "@types/events" "^3.0.0" + events "^3.2.0" + md5.js@^1.3.4: version "1.3.5" resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" @@ -5016,6 +5223,11 @@ npm-run-path@^4.0.1: dependencies: path-key "^3.0.0" +nwsapi@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.1.tgz#10a9f268fbf4c461249ebcfe38e359aa36e2577c" + integrity sha512-JYOWTeFoS0Z93587vRJgASD5Ut11fYl5NyihP3KrYBvMe1FRRs6RN7m20SA/16GM4P6hTnZjT+UmDOt38UeXNg== + oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" @@ -5069,6 +5281,18 @@ onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" +optionator@^0.8.1: + version "0.8.3" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" + integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.6" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + word-wrap "~1.2.3" + optionator@^0.9.1: version "0.9.1" resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" @@ -5200,6 +5424,11 @@ parse-json@^5.2.0: json-parse-even-better-errors "^2.3.0" lines-and-columns "^1.1.6" +parse5@6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + path-browserify@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd" @@ -5295,6 +5524,11 @@ prelude-ls@^1.2.1: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== + pretty-format@^28.0.0, pretty-format@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.3.tgz#c9fba8cedf99ce50963a11b27d982a9ae90970d5" @@ -5349,7 +5583,7 @@ pseudomap@^1.0.2: resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== -psl@^1.1.28: +psl@^1.1.28, psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== @@ -5815,11 +6049,18 @@ safe-buffer@~5.1.0, safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: +"safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +saxes@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" + integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw== + dependencies: + xmlchars "^2.2.0" + sdp-transform@^2.14.1: version "2.14.1" resolved "https://registry.yarnpkg.com/sdp-transform/-/sdp-transform-2.14.1.tgz#2bb443583d478dee217df4caa284c46b870d5827" @@ -6143,6 +6384,11 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== +symbol-tree@^3.2.4: + version "3.2.4" + resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" + integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== + syntax-error@^1.1.1: version "1.4.0" resolved "https://registry.yarnpkg.com/syntax-error/-/syntax-error-1.4.0.tgz#2d9d4ff5c064acb711594a3e3b95054ad51d907c" @@ -6247,6 +6493,15 @@ token-stream@0.0.1: resolved "https://registry.yarnpkg.com/token-stream/-/token-stream-0.0.1.tgz#ceeefc717a76c4316f126d0b9dbaa55d7e7df01a" integrity sha512-nfjOAu/zAWmX9tgwi5NRp7O7zTDUD1miHiB40klUnAh9qnL1iXdgzcz/i5dMaL5jahcBAaSfmNOBBJBLJW8TEg== +tough-cookie@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" + integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== + dependencies: + psl "^1.1.33" + punycode "^2.1.1" + universalify "^0.1.2" + tough-cookie@~2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" @@ -6262,6 +6517,13 @@ tr46@^2.1.0: dependencies: punycode "^2.1.1" +tr46@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" + integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA== + dependencies: + punycode "^2.1.1" + tr46@~0.0.3: version "0.0.3" resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" @@ -6350,6 +6612,13 @@ type-check@^0.4.0, type-check@~0.4.0: dependencies: prelude-ls "^1.2.1" +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== + dependencies: + prelude-ls "~1.1.2" + type-detect@4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" @@ -6488,6 +6757,11 @@ universal-user-agent@^6.0.0: resolved "https://registry.yarnpkg.com/universal-user-agent/-/universal-user-agent-6.0.0.tgz#3381f8503b251c0d9cd21bc1de939ec9df5480ee" integrity sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w== +universalify@^0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + update-browserslist-db@^1.0.4: version "1.0.5" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz#be06a5eedd62f107b7c19eb5bcefb194411abf38" @@ -6604,6 +6878,20 @@ vue2-ace-editor@^0.0.15: dependencies: brace "^0.11.0" +w3c-hr-time@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" + integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== + dependencies: + browser-process-hrtime "^1.0.0" + +w3c-xmlserializer@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz#06cdc3eefb7e4d0b20a560a5a3aeb0d2d9a65923" + integrity sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg== + dependencies: + xml-name-validator "^4.0.0" + walker@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" @@ -6626,6 +6914,39 @@ webidl-conversions@^6.1.0: resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-6.1.0.tgz#9111b4d7ea80acd40f5270d666621afa78b69514" integrity sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w== +webidl-conversions@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" + integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== + +whatwg-encoding@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" + integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg== + dependencies: + iconv-lite "0.6.3" + +whatwg-mimetype@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" + integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== + +whatwg-url@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-10.0.0.tgz#37264f720b575b4a311bd4094ed8c760caaa05da" + integrity sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w== + dependencies: + tr46 "^3.0.0" + webidl-conversions "^7.0.0" + +whatwg-url@^11.0.0: + version "11.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018" + integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ== + dependencies: + tr46 "^3.0.0" + webidl-conversions "^7.0.0" + whatwg-url@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" @@ -6686,7 +7007,7 @@ with@^5.0.0: acorn "^3.1.0" acorn-globals "^3.0.0" -word-wrap@^1.2.3: +word-wrap@^1.2.3, word-wrap@~1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== @@ -6718,11 +7039,26 @@ write-file-atomic@^4.0.1: imurmurhash "^0.1.4" signal-exit "^3.0.7" +ws@^8.2.3: + version "8.8.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0" + integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA== + +xml-name-validator@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" + integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== + xml@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" integrity sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw== +xmlchars@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" + integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== + xmlcreate@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/xmlcreate/-/xmlcreate-2.0.4.tgz#0c5ab0f99cdd02a81065fa9cd8f8ae87624889be"