diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index df3a62ea7..8b1c7d475 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -38,4 +38,5 @@ cee7f7a280a8c20bafc21c0a2911f60851f7a7ca 7ed65407e6cdf292ce3cf659310c68d19dcd52b2 # Switch to ESLint from JSHint (Google eslint rules as a base) e057956ede9ad1a931ff8050c411aca7907e0394 - +# prettier +349c2c2587c2885bb69eda4aa078b5383724cf5e diff --git a/package.json b/package.json index 128de735e..e3216884f 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ ], "dependencies": { "@babel/runtime": "^7.12.5", + "@matrix-org/matrix-sdk-crypto-js": "^0.1.0-alpha.2", "another-json": "^0.2.0", "bs58": "^5.0.0", "content-type": "^1.0.4", @@ -64,7 +65,7 @@ "qs": "^6.9.6", "sdp-transform": "^2.14.1", "unhomoglyph": "^1.0.6", - "uuid": "7" + "uuid": "9" }, "devDependencies": { "@babel/cli": "^7.12.10", @@ -97,7 +98,7 @@ "browserify": "^17.0.0", "docdash": "^2.0.0", "domexception": "^4.0.0", - "eslint": "8.28.0", + "eslint": "8.29.0", "eslint-config-google": "^0.14.0", "eslint-config-prettier": "^8.5.0", "eslint-import-resolver-typescript": "^3.5.1", @@ -113,7 +114,7 @@ "jest-localstorage-mock": "^2.4.6", "jest-mock": "^29.0.0", "matrix-mock-request": "^2.5.0", - "prettier": "2.8.0", + "prettier": "2.8.1", "rimraf": "^3.0.2", "terser": "^5.5.1", "tsify": "^5.0.2", diff --git a/spec/integ/matrix-client-event-timeline.spec.ts b/spec/integ/matrix-client-event-timeline.spec.ts index b02955511..5edd5c4b7 100644 --- a/spec/integ/matrix-client-event-timeline.spec.ts +++ b/spec/integ/matrix-client-event-timeline.spec.ts @@ -1018,6 +1018,97 @@ describe("MatrixClient event timelines", function () { }); }); + it("should ensure thread events are ordered correctly", async () => { + // Test data for a second reply to the first thread + const THREAD_REPLY2 = utils.mkEvent({ + room: roomId, + user: userId, + type: "m.room.message", + content: { + "body": "thread reply 2", + "msgtype": "m.text", + "m.relates_to": { + // We can't use the const here because we change server support mode for test + rel_type: "io.element.thread", + event_id: THREAD_ROOT.event_id, + }, + }, + event: true, + }); + THREAD_REPLY2.localTimestamp += 1000; + + // Test data for a second reply to the first thread + const THREAD_REPLY3 = utils.mkEvent({ + room: roomId, + user: userId, + type: "m.room.message", + content: { + "body": "thread reply 3", + "msgtype": "m.text", + "m.relates_to": { + // We can't use the const here because we change server support mode for test + rel_type: "io.element.thread", + event_id: THREAD_ROOT.event_id, + }, + }, + event: true, + }); + THREAD_REPLY3.localTimestamp += 2000; + + // Test data for the first thread, with the second reply + const THREAD_ROOT_UPDATED = { + ...THREAD_ROOT, + unsigned: { + ...THREAD_ROOT.unsigned, + "m.relations": { + ...THREAD_ROOT.unsigned!["m.relations"], + "io.element.thread": { + ...THREAD_ROOT.unsigned!["m.relations"]!["io.element.thread"], + count: 3, + latest_event: THREAD_REPLY3.event, + }, + }, + }, + }; + + // @ts-ignore + client.clientOpts.experimentalThreadSupport = true; + Thread.setServerSideSupport(FeatureSupport.Stable); + Thread.setServerSideListSupport(FeatureSupport.Stable); + Thread.setServerSideFwdPaginationSupport(FeatureSupport.Stable); + + client.fetchRoomEvent = () => Promise.resolve(THREAD_ROOT_UPDATED); + + await client.stopClient(); // we don't need the client to be syncing at this time + const room = client.getRoom(roomId)!; + + const prom = emitPromise(room, ThreadEvent.Update); + // Assume we're seeing the reply while loading backlog + room.addLiveEvents([THREAD_REPLY2]); + httpBackend + .when( + "GET", + "/_matrix/client/v1/rooms/!foo%3Abar/relations/" + + encodeURIComponent(THREAD_ROOT_UPDATED.event_id!) + + "/" + + encodeURIComponent(THREAD_RELATION_TYPE.name), + ) + .respond(200, { + chunk: [THREAD_REPLY3.event, THREAD_REPLY2.event, THREAD_REPLY], + }); + await flushHttp(prom); + // but while loading the metadata, a new reply has arrived + room.addLiveEvents([THREAD_REPLY3]); + const thread = room.getThread(THREAD_ROOT_UPDATED.event_id!)!; + // then the events should still be all in the right order + expect(thread.events.map((it) => it.getId())).toEqual([ + THREAD_ROOT.event_id, + THREAD_REPLY.event_id, + THREAD_REPLY2.getId(), + THREAD_REPLY3.getId(), + ]); + }); + describe("paginateEventTimeline for thread list timeline", function () { const RANDOM_TOKEN = "7280349c7bee430f91defe2a38a0a08c"; diff --git a/spec/integ/rust-crypto.spec.ts b/spec/integ/rust-crypto.spec.ts new file mode 100644 index 000000000..e018c2102 --- /dev/null +++ b/spec/integ/rust-crypto.spec.ts @@ -0,0 +1,90 @@ +/* +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 "fake-indexeddb/auto"; +import { IDBFactory } from "fake-indexeddb"; + +import { createClient } from "../../src"; + +afterEach(() => { + // reset fake-indexeddb after each test, to make sure we don't leak connections + // cf https://github.com/dumbmatter/fakeIndexedDB#wipingresetting-the-indexeddb-for-a-fresh-state + // eslint-disable-next-line no-global-assign + indexedDB = new IDBFactory(); +}); + +describe("MatrixClient.initRustCrypto", () => { + it("should raise if userId or deviceId is unknown", async () => { + const unknownUserClient = createClient({ + baseUrl: "http://test.server", + deviceId: "aliceDevice", + }); + await expect(() => unknownUserClient.initRustCrypto()).rejects.toThrow("unknown userId"); + + const unknownDeviceClient = createClient({ + baseUrl: "http://test.server", + userId: "@alice:test", + }); + await expect(() => unknownDeviceClient.initRustCrypto()).rejects.toThrow("unknown deviceId"); + }); + + it("should create the indexed dbs", async () => { + const matrixClient = createClient({ + baseUrl: "http://test.server", + userId: "@alice:localhost", + deviceId: "aliceDevice", + }); + + // No databases. + expect(await indexedDB.databases()).toHaveLength(0); + + await matrixClient.initRustCrypto(); + + // should have two dbs now + const databaseNames = (await indexedDB.databases()).map((db) => db.name); + expect(databaseNames).toEqual( + expect.arrayContaining(["matrix-js-sdk::matrix-sdk-crypto", "matrix-js-sdk::matrix-sdk-crypto-meta"]), + ); + }); + + it("should ignore a second call", async () => { + const matrixClient = createClient({ + baseUrl: "http://test.server", + userId: "@alice:localhost", + deviceId: "aliceDevice", + }); + + await matrixClient.initRustCrypto(); + await matrixClient.initRustCrypto(); + }); +}); + +describe("MatrixClient.clearStores", () => { + it("should clear the indexeddbs", async () => { + const matrixClient = createClient({ + baseUrl: "http://test.server", + userId: "@alice:localhost", + deviceId: "aliceDevice", + }); + + await matrixClient.initRustCrypto(); + expect(await indexedDB.databases()).toHaveLength(2); + await matrixClient.stopClient(); + + await matrixClient.clearStores(); + expect(await indexedDB.databases()).toHaveLength(0); + }); +}); diff --git a/spec/unit/ToDeviceMessageQueue.spec.ts b/spec/unit/ToDeviceMessageQueue.spec.ts new file mode 100644 index 000000000..8752331c7 --- /dev/null +++ b/spec/unit/ToDeviceMessageQueue.spec.ts @@ -0,0 +1,106 @@ +import { ConnectionError } from "../../src/http-api/errors"; +import { ClientEvent, MatrixClient, Store } from "../../src/client"; +import { ToDeviceMessageQueue } from "../../src/ToDeviceMessageQueue"; +import { getMockClientWithEventEmitter } from "../test-utils/client"; +import { StubStore } from "../../src/store/stub"; +import { IndexedToDeviceBatch } from "../../src/models/ToDeviceMessage"; +import { SyncState } from "../../src/sync"; + +describe("onResumedSync", () => { + let batch: IndexedToDeviceBatch | null; + let shouldFailSendToDevice: Boolean; + let onSendToDeviceFailure: () => void; + let onSendToDeviceSuccess: () => void; + let resumeSync: (newState: SyncState, oldState: SyncState) => void; + + let store: Store; + let mockClient: MatrixClient; + let queue: ToDeviceMessageQueue; + + beforeEach(() => { + batch = { + id: 0, + txnId: "123", + eventType: "m.dummy", + batch: [], + }; + + shouldFailSendToDevice = true; + onSendToDeviceFailure = () => {}; + onSendToDeviceSuccess = () => {}; + resumeSync = (newState, oldState) => { + shouldFailSendToDevice = false; + mockClient.emit(ClientEvent.Sync, newState, oldState); + }; + + store = new StubStore(); + store.getOldestToDeviceBatch = jest.fn().mockImplementation(() => { + return batch; + }); + store.removeToDeviceBatch = jest.fn().mockImplementation(() => { + batch = null; + }); + + mockClient = getMockClientWithEventEmitter({}); + mockClient.store = store; + mockClient.sendToDevice = jest.fn().mockImplementation(async () => { + if (shouldFailSendToDevice) { + await Promise.reject(new ConnectionError("")).finally(() => { + setTimeout(onSendToDeviceFailure, 0); + }); + } else { + await Promise.resolve({}).finally(() => { + setTimeout(onSendToDeviceSuccess, 0); + }); + } + }); + + queue = new ToDeviceMessageQueue(mockClient); + }); + + it("resends queue after connectivity restored", (done) => { + onSendToDeviceFailure = () => { + expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(1); + expect(store.removeToDeviceBatch).not.toHaveBeenCalled(); + + resumeSync(SyncState.Syncing, SyncState.Catchup); + expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(2); + }; + + onSendToDeviceSuccess = () => { + expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(3); + expect(store.removeToDeviceBatch).toHaveBeenCalled(); + done(); + }; + + queue.start(); + }); + + it("does not resend queue if client sync still catching up", (done) => { + onSendToDeviceFailure = () => { + expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(1); + expect(store.removeToDeviceBatch).not.toHaveBeenCalled(); + + resumeSync(SyncState.Catchup, SyncState.Catchup); + expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(1); + done(); + }; + + queue.start(); + }); + + it("does not resend queue if connectivity restored after queue stopped", (done) => { + onSendToDeviceFailure = () => { + expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(1); + expect(store.removeToDeviceBatch).not.toHaveBeenCalled(); + + queue.stop(); + + resumeSync(SyncState.Syncing, SyncState.Catchup); + expect(store.getOldestToDeviceBatch).toHaveBeenCalledTimes(1); + done(); + }; + + queue.start(); + }); +}); diff --git a/spec/unit/matrix-client.spec.ts b/spec/unit/matrix-client.spec.ts index f5c440067..2b48b69c3 100644 --- a/spec/unit/matrix-client.spec.ts +++ b/spec/unit/matrix-client.spec.ts @@ -59,6 +59,7 @@ import { import { IOlmDevice } from "../../src/crypto/algorithms/megolm"; import { QueryDict } from "../../src/utils"; import { SyncState } from "../../src/sync"; +import * as featureUtils from "../../src/feature"; jest.useFakeTimers(); @@ -435,6 +436,23 @@ describe("MatrixClient", function () { }); }); + describe("getSafeUserId()", () => { + it("returns the logged in user id", () => { + expect(client.getSafeUserId()).toEqual(userId); + }); + + it("throws when there is not logged in user", () => { + const notLoggedInClient = new MatrixClient({ + baseUrl: "https://my.home.server", + idBaseUrl: identityServerUrl, + fetchFn: function () {} as any, // NOP + store: store, + scheduler: scheduler, + }); + expect(() => notLoggedInClient.getSafeUserId()).toThrow("Expected logged in user but found none."); + }); + }); + describe("sendEvent", () => { const roomId = "!room:example.org"; const body = "This is the body"; @@ -1982,4 +2000,68 @@ describe("MatrixClient", function () { expect(client.getUseE2eForGroupCall()).toBe(false); }); }); + + describe("delete account data", () => { + afterEach(() => { + jest.spyOn(featureUtils, "buildFeatureSupportMap").mockRestore(); + }); + it("makes correct request when deletion is supported by server in unstable versions", async () => { + const eventType = "im.vector.test"; + const versionsResponse = { + versions: ["1"], + unstable_features: { + "org.matrix.msc3391": true, + }, + }; + jest.spyOn(client.http, "request").mockResolvedValue(versionsResponse); + const requestSpy = jest.spyOn(client.http, "authedRequest").mockImplementation(() => Promise.resolve()); + const unstablePrefix = "/_matrix/client/unstable/org.matrix.msc3391"; + const path = `/user/${encodeURIComponent(userId)}/account_data/${eventType}`; + + // populate version support + await client.getVersions(); + await client.deleteAccountData(eventType); + + expect(requestSpy).toHaveBeenCalledWith(Method.Delete, path, undefined, undefined, { + prefix: unstablePrefix, + }); + }); + + it("makes correct request when deletion is supported by server based on matrix version", async () => { + const eventType = "im.vector.test"; + // we don't have a stable version for account data deletion yet to test this code path with + // so mock the support map to fake stable support + const stableSupportedDeletionMap = new Map(); + stableSupportedDeletionMap.set(featureUtils.Feature.AccountDataDeletion, featureUtils.ServerSupport.Stable); + jest.spyOn(featureUtils, "buildFeatureSupportMap").mockResolvedValue(new Map()); + const requestSpy = jest.spyOn(client.http, "authedRequest").mockImplementation(() => Promise.resolve()); + const path = `/user/${encodeURIComponent(userId)}/account_data/${eventType}`; + + // populate version support + await client.getVersions(); + await client.deleteAccountData(eventType); + + expect(requestSpy).toHaveBeenCalledWith(Method.Delete, path, undefined, undefined, undefined); + }); + + it("makes correct request when deletion is not supported by server", async () => { + const eventType = "im.vector.test"; + const versionsResponse = { + versions: ["1"], + unstable_features: { + "org.matrix.msc3391": false, + }, + }; + jest.spyOn(client.http, "request").mockResolvedValue(versionsResponse); + const requestSpy = jest.spyOn(client.http, "authedRequest").mockImplementation(() => Promise.resolve()); + const path = `/user/${encodeURIComponent(userId)}/account_data/${eventType}`; + + // populate version support + await client.getVersions(); + await client.deleteAccountData(eventType); + + // account data updated with empty content + expect(requestSpy).toHaveBeenCalledWith(Method.Put, path, undefined, {}); + }); + }); }); diff --git a/spec/unit/stores/memory.spec.ts b/spec/unit/stores/memory.spec.ts new file mode 100644 index 000000000..fac3267db --- /dev/null +++ b/spec/unit/stores/memory.spec.ts @@ -0,0 +1,65 @@ +/* +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 { MatrixEvent, MemoryStore } from "../../../src"; + +describe("MemoryStore", () => { + const event1 = new MatrixEvent({ type: "event1-type", content: { test: 1 } }); + const event2 = new MatrixEvent({ type: "event2-type", content: { test: 1 } }); + const event3 = new MatrixEvent({ type: "event3-type", content: { test: 1 } }); + const event4 = new MatrixEvent({ type: "event4-type", content: { test: 1 } }); + const event4Updated = new MatrixEvent({ type: "event4-type", content: { test: 2 } }); + const event1Empty = new MatrixEvent({ type: "event1-type", content: {} }); + + describe("account data", () => { + it("sets account data events correctly", () => { + const store = new MemoryStore(); + store.storeAccountDataEvents([event1, event2]); + expect(store.getAccountData(event1.getType())).toEqual(event1); + expect(store.getAccountData(event2.getType())).toEqual(event2); + }); + + it("returns undefined when no account data event exists for type", () => { + const store = new MemoryStore(); + expect(store.getAccountData("my-event-type")).toEqual(undefined); + }); + + it("updates account data events correctly", () => { + const store = new MemoryStore(); + // init store with event1, event2 + store.storeAccountDataEvents([event1, event2, event4]); + // remove event1, add event3 + store.storeAccountDataEvents([event1Empty, event3, event4Updated]); + // removed + expect(store.getAccountData(event1.getType())).toEqual(undefined); + // not removed + expect(store.getAccountData(event2.getType())).toEqual(event2); + // added + expect(store.getAccountData(event3.getType())).toEqual(event3); + // updated + expect(store.getAccountData(event4.getType())).toEqual(event4Updated); + }); + + it("removes all account data from state on deleteAllData", async () => { + const store = new MemoryStore(); + store.storeAccountDataEvents([event1, event2]); + await store.deleteAllData(); + + // empty object + expect(store.accountData).toEqual({}); + }); + }); +}); diff --git a/src/ToDeviceMessageQueue.ts b/src/ToDeviceMessageQueue.ts index e78c46ba2..ec5922bb6 100644 --- a/src/ToDeviceMessageQueue.ts +++ b/src/ToDeviceMessageQueue.ts @@ -16,9 +16,11 @@ limitations under the License. import { ToDeviceMessageId } from "./@types/event"; import { logger } from "./logger"; -import { MatrixError, MatrixClient } from "./matrix"; +import { MatrixClient, ClientEvent } from "./client"; +import { MatrixError } from "./http-api"; import { IndexedToDeviceBatch, ToDeviceBatch, ToDeviceBatchWithTxnId, ToDevicePayload } from "./models/ToDeviceMessage"; import { MatrixScheduler } from "./scheduler"; +import { SyncState } from "./sync"; const MAX_BATCH_SIZE = 20; @@ -37,12 +39,14 @@ export class ToDeviceMessageQueue { public start(): void { this.running = true; this.sendQueue(); + this.client.on(ClientEvent.Sync, this.onResumedSync); } public stop(): void { this.running = false; if (this.retryTimeout !== null) clearTimeout(this.retryTimeout); this.retryTimeout = null; + this.client.removeListener(ClientEvent.Sync, this.onResumedSync); } public async queueBatch(batch: ToDeviceBatch): Promise { @@ -132,4 +136,15 @@ export class ToDeviceMessageQueue { await this.client.sendToDevice(batch.eventType, contentMap, batch.txnId); } + + /** + * Listen to sync state changes and automatically resend any pending events + * once syncing is resumed + */ + private onResumedSync = (state: SyncState | null, oldState: SyncState | null): void => { + if (state === SyncState.Syncing && oldState !== SyncState.Syncing) { + logger.info(`Resuming queue after resumed sync`); + this.sendQueue(); + } + }; } diff --git a/src/client.ts b/src/client.ts index 10f59eca5..e36ef7c41 100644 --- a/src/client.ts +++ b/src/client.ts @@ -211,6 +211,7 @@ import { LocalNotificationSettings } from "./@types/local_notifications"; import { UNREAD_THREAD_NOTIFICATIONS } from "./@types/sync"; import { buildFeatureSupportMap, Feature, ServerSupport } from "./feature"; import { CryptoBackend } from "./common-crypto/CryptoBackend"; +import { RUST_SDK_STORE_PREFIX } from "./rust-crypto/constants"; export type Store = IStore; @@ -1657,6 +1658,41 @@ export class MatrixClient extends TypedEventEmitter => { + let indexedDB: IDBFactory; + try { + indexedDB = global.indexedDB; + } catch (e) { + // No indexeddb support + return; + } + for (const dbname of [ + `${RUST_SDK_STORE_PREFIX}::matrix-sdk-crypto`, + `${RUST_SDK_STORE_PREFIX}::matrix-sdk-crypto-meta`, + ]) { + const prom = new Promise((resolve, reject) => { + logger.info(`Removing IndexedDB instance ${dbname}`); + const req = indexedDB.deleteDatabase(dbname); + req.onsuccess = (_): void => { + logger.info(`Removed IndexedDB instance ${dbname}`); + resolve(0); + }; + req.onerror = (e): void => { + logger.error(`Failed to remove IndexedDB instance ${dbname}: ${e}`); + reject(new Error(`Error clearing storage: ${e}`)); + }; + req.onblocked = (e): void => { + logger.info(`cannot yet remove IndexedDB instance ${dbname}`); + //reject(new Error(`Error clearing storage: ${e}`)); + }; + }); + await prom; + } + }; + promises.push(deleteRustSdkStore()); + return Promise.all(promises).then(); // .then to fix types } @@ -1672,6 +1708,20 @@ export class MatrixClient extends TypedEventEmitter { + if (this.cryptoBackend) { + logger.warn("Attempt to re-initialise e2e encryption on MatrixClient"); + return; + } + + const userId = this.getUserId(); + if (userId === null) { + throw new Error( + `Cannot enable encryption on MatrixClient with unknown userId: ` + + `ensure userId is passed in createClient().`, + ); + } + const deviceId = this.getDeviceId(); + if (deviceId === null) { + throw new Error( + `Cannot enable encryption on MatrixClient with unknown deviceId: ` + + `ensure deviceId is passed in createClient().`, + ); + } + + // importing rust-crypto will download the webassembly, so we delay it until we know it will be + // needed. + const RustCrypto = await import("./rust-crypto"); + this.cryptoBackend = await RustCrypto.initRustCrypto(userId, deviceId); + } + /** * Is end-to-end crypto enabled for this client. * @returns True if end-to-end is enabled. @@ -3766,6 +3856,24 @@ export class MatrixClient extends TypedEventEmitter { + const msc3391DeleteAccountDataServerSupport = this.canSupport.get(Feature.AccountDataDeletion); + // if deletion is not supported overwrite with empty content + if (msc3391DeleteAccountDataServerSupport === ServerSupport.Unsupported) { + await this.setAccountData(eventType, {}); + return; + } + const path = utils.encodeUri("/user/$userId/account_data/$type", { + $userId: this.getSafeUserId(), + $type: eventType, + }); + const options = + msc3391DeleteAccountDataServerSupport === ServerSupport.Unstable + ? { prefix: "/_matrix/client/unstable/org.matrix.msc3391" } + : undefined; + return await this.http.authedRequest(Method.Delete, path, undefined, undefined, options); + } + /** * Gets the users that are ignored by this client * @returns The array of users that are ignored (empty if none) diff --git a/src/feature.ts b/src/feature.ts index d555a860b..158e1f7ee 100644 --- a/src/feature.ts +++ b/src/feature.ts @@ -26,6 +26,7 @@ export enum Feature { Thread = "Thread", ThreadUnreadNotifications = "ThreadUnreadNotifications", LoginTokenRequest = "LoginTokenRequest", + AccountDataDeletion = "AccountDataDeletion", } type FeatureSupportCondition = { @@ -45,6 +46,9 @@ const featureSupportResolver: Record = { [Feature.LoginTokenRequest]: { unstablePrefixes: ["org.matrix.msc3882"], }, + [Feature.AccountDataDeletion]: { + unstablePrefixes: ["org.matrix.msc3391"], + }, }; export async function buildFeatureSupportMap(versions: IServerVersions): Promise> { diff --git a/src/rust-crypto/constants.ts b/src/rust-crypto/constants.ts new file mode 100644 index 000000000..9d72060c3 --- /dev/null +++ b/src/rust-crypto/constants.ts @@ -0,0 +1,18 @@ +/* +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. +*/ + +/** The prefix used on indexeddbs created by rust-crypto */ +export const RUST_SDK_STORE_PREFIX = "matrix-js-sdk"; diff --git a/src/rust-crypto/index.ts b/src/rust-crypto/index.ts new file mode 100644 index 000000000..7485836cd --- /dev/null +++ b/src/rust-crypto/index.ts @@ -0,0 +1,41 @@ +/* +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 * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-js"; + +import { RustCrypto } from "./rust-crypto"; +import { logger } from "../logger"; +import { CryptoBackend } from "../common-crypto/CryptoBackend"; +import { RUST_SDK_STORE_PREFIX } from "./constants"; + +export async function initRustCrypto(userId: string, deviceId: string): Promise { + // initialise the rust matrix-sdk-crypto-js, if it hasn't already been done + await RustSdkCryptoJs.initAsync(); + + // enable tracing in the rust-sdk + new RustSdkCryptoJs.Tracing(RustSdkCryptoJs.LoggerLevel.Debug).turnOn(); + + const u = new RustSdkCryptoJs.UserId(userId); + const d = new RustSdkCryptoJs.DeviceId(deviceId); + logger.info("Init OlmMachine"); + + // TODO: use the pickle key for the passphrase + const olmMachine = await RustSdkCryptoJs.OlmMachine.initialize(u, d, RUST_SDK_STORE_PREFIX, "test pass"); + const rustCrypto = new RustCrypto(olmMachine, userId, deviceId); + + logger.info("Completed rust crypto-sdk setup"); + return rustCrypto; +} diff --git a/src/rust-crypto/rust-crypto.ts b/src/rust-crypto/rust-crypto.ts new file mode 100644 index 000000000..07b7ea3b9 --- /dev/null +++ b/src/rust-crypto/rust-crypto.ts @@ -0,0 +1,60 @@ +/* +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 * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-js"; + +import { IEventDecryptionResult } from "../@types/crypto"; +import { MatrixEvent } from "../models/event"; +import { CryptoBackend } from "../common-crypto/CryptoBackend"; + +// import { logger } from "../logger"; + +/** + * An implementation of {@link CryptoBackend} using the Rust matrix-sdk-crypto. + */ +export class RustCrypto implements CryptoBackend { + public globalBlacklistUnverifiedDevices = false; + public globalErrorOnUnknownDevices = false; + + /** whether stop() has been called */ + private stopped = false; + + public constructor(private readonly olmMachine: RustSdkCryptoJs.OlmMachine, _userId: string, _deviceId: string) {} + + public stop(): void { + // stop() may be called multiple times, but attempting to close() the OlmMachine twice + // will cause an error. + if (this.stopped) { + return; + } + this.stopped = true; + + // make sure we close() the OlmMachine; doing so means that all the Rust objects will be + // cleaned up; in particular, the indexeddb connections will be closed, which means they + // can then be deleted. + this.olmMachine.close(); + } + + public async decryptEvent(event: MatrixEvent): Promise { + await this.olmMachine.decryptRoomEvent("event", new RustSdkCryptoJs.RoomId("room")); + throw new Error("not implemented"); + } + + public async userHasCrossSigningKeys(): Promise { + // TODO + return false; + } +} diff --git a/src/store/memory.ts b/src/store/memory.ts index 782d7edef..025a632aa 100644 --- a/src/store/memory.ts +++ b/src/store/memory.ts @@ -286,7 +286,13 @@ export class MemoryStore implements IStore { */ public storeAccountDataEvents(events: MatrixEvent[]): void { events.forEach((event) => { - this.accountData[event.getType()] = event; + // MSC3391: an event with content of {} should be interpreted as deleted + const isDeleted = !Object.keys(event.getContent()).length; + if (isDeleted) { + delete this.accountData[event.getType()]; + } else { + this.accountData[event.getType()] = event; + } }); } diff --git a/yarn.lock b/yarn.lock index c4d494579..232e98e6c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1102,7 +1102,7 @@ esquery "^1.4.0" jsdoc-type-pratt-parser "~3.1.0" -"@eslint-community/eslint-utils@^4.1.0": +"@eslint-community/eslint-utils@^4.1.2": version "4.1.2" resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.1.2.tgz#14ca568ddaa291dd19a4a54498badc18c6cfab78" integrity sha512-7qELuQWWjVDdVsFQ5+beUl+KPczrEDA7S3zM4QUd/bJl7oXgsmpXaEVqrRTnOBqenOV4rWf2kVZk2Ot085zPWA== @@ -1432,6 +1432,11 @@ dependencies: lodash "^4.17.21" +"@matrix-org/matrix-sdk-crypto-js@^0.1.0-alpha.2": + version "0.1.0-alpha.2" + resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-js/-/matrix-sdk-crypto-js-0.1.0-alpha.2.tgz#a09d0fea858e817da971a3c9f904632ef7b49eb6" + integrity sha512-oVkBCh9YP7H9i4gAoQbZzswniczfo/aIptNa4dxRi4Ff9lSvUCFv6Hvzi7C+90c0/PWZLXjIDTIAWZYmwyd2fA== + "@matrix-org/olm@https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz": version "3.2.14" resolved "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz#acd96c00a881d0f462e1f97a56c73742c8dbc984" @@ -1717,9 +1722,9 @@ "@types/istanbul-lib-report" "*" "@types/jest@^29.0.0": - version "29.2.3" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.2.3.tgz#f5fd88e43e5a9e4221ca361e23790d48fcf0a211" - integrity sha512-6XwoEbmatfyoCjWRX7z0fKMmgYKe9+/HrviJ5k0X/tjJWHGAezZOfYaxqQKuzG/TvQyr+ktjm4jgbk0s4/oF2w== + version "29.2.4" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.2.4.tgz#9c155c4b81c9570dbd183eb8604aa0ae80ba5a5b" + integrity sha512-PipFB04k2qTRPePduVLTRiPzQfvMeLwUN3Z21hsAKaB/W9IIzgB2pizCL466ftJlcyZqnHoC9ZHpxLGl3fS86A== dependencies: expect "^29.0.0" pretty-format "^29.0.0" @@ -1743,7 +1748,12 @@ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== -"@types/node@*", "@types/node@18": +"@types/node@*": + version "18.11.15" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.15.tgz#de0e1fbd2b22b962d45971431e2ae696643d3f5d" + integrity sha512-VkhBbVo2+2oozlkdHXLrb3zjsRkpdnaU2bXmX8Wgle3PUi569eLRaHGlgETQHR7lLL1w7GiG3h9SnePhxNDecw== + +"@types/node@18": version "18.11.9" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.9.tgz#02d013de7058cea16d36168ef2fc653464cfbad4" integrity sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg== @@ -1799,20 +1809,20 @@ integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== "@types/yargs@^17.0.8": - version "17.0.13" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.13.tgz#34cced675ca1b1d51fcf4d34c3c6f0fa142a5c76" - integrity sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg== + version "17.0.17" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.17.tgz#5672e5621f8e0fca13f433a8017aae4b7a2a03e7" + integrity sha512-72bWxFKTK6uwWJAVT+3rF6Jo6RTojiJ27FQo8Rf60AL+VZbzoVPnMFhKsUnbjR8A3BTCYQ7Mv3hnl8T0A+CX9g== dependencies: "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^5.45.0": - version "5.45.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.45.0.tgz#ffa505cf961d4844d38cfa19dcec4973a6039e41" - integrity sha512-CXXHNlf0oL+Yg021cxgOdMHNTXD17rHkq7iW6RFHoybdFgQBjU3yIXhhcPpGwr1CjZlo6ET8C6tzX5juQoXeGA== + version "5.46.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.46.1.tgz#098abb4c9354e19f460d57ab18bff1f676a6cff0" + integrity sha512-YpzNv3aayRBwjs4J3oz65eVLXc9xx0PDbIRisHj+dYhvBn02MjYOD96P8YGiWEIFBrojaUjxvkaUpakD82phsA== dependencies: - "@typescript-eslint/scope-manager" "5.45.0" - "@typescript-eslint/type-utils" "5.45.0" - "@typescript-eslint/utils" "5.45.0" + "@typescript-eslint/scope-manager" "5.46.1" + "@typescript-eslint/type-utils" "5.46.1" + "@typescript-eslint/utils" "5.46.1" debug "^4.3.4" ignore "^5.2.0" natural-compare-lite "^1.4.0" @@ -1821,71 +1831,71 @@ tsutils "^3.21.0" "@typescript-eslint/parser@^5.45.0": - version "5.45.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.45.0.tgz#b18a5f6b3cf1c2b3e399e9d2df4be40d6b0ddd0e" - integrity sha512-brvs/WSM4fKUmF5Ot/gEve6qYiCMjm6w4HkHPfS6ZNmxTS0m0iNN4yOChImaCkqc1hRwFGqUyanMXuGal6oyyQ== + version "5.46.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.46.1.tgz#1fc8e7102c1141eb64276c3b89d70da8c0ba5699" + integrity sha512-RelQ5cGypPh4ySAtfIMBzBGyrNerQcmfA1oJvPj5f+H4jI59rl9xxpn4bonC0tQvUKOEN7eGBFWxFLK3Xepneg== dependencies: - "@typescript-eslint/scope-manager" "5.45.0" - "@typescript-eslint/types" "5.45.0" - "@typescript-eslint/typescript-estree" "5.45.0" + "@typescript-eslint/scope-manager" "5.46.1" + "@typescript-eslint/types" "5.46.1" + "@typescript-eslint/typescript-estree" "5.46.1" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.45.0": - version "5.45.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.45.0.tgz#7a4ac1bfa9544bff3f620ab85947945938319a96" - integrity sha512-noDMjr87Arp/PuVrtvN3dXiJstQR1+XlQ4R1EvzG+NMgXi8CuMCXpb8JqNtFHKceVSQ985BZhfRdowJzbv4yKw== +"@typescript-eslint/scope-manager@5.46.1": + version "5.46.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.46.1.tgz#70af8425c79bbc1178b5a63fb51102ddf48e104a" + integrity sha512-iOChVivo4jpwUdrJZyXSMrEIM/PvsbbDOX1y3UCKjSgWn+W89skxWaYXACQfxmIGhPVpRWK/VWPYc+bad6smIA== dependencies: - "@typescript-eslint/types" "5.45.0" - "@typescript-eslint/visitor-keys" "5.45.0" + "@typescript-eslint/types" "5.46.1" + "@typescript-eslint/visitor-keys" "5.46.1" -"@typescript-eslint/type-utils@5.45.0": - version "5.45.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.45.0.tgz#aefbc954c40878fcebeabfb77d20d84a3da3a8b2" - integrity sha512-DY7BXVFSIGRGFZ574hTEyLPRiQIvI/9oGcN8t1A7f6zIs6ftbrU0nhyV26ZW//6f85avkwrLag424n+fkuoJ1Q== +"@typescript-eslint/type-utils@5.46.1": + version "5.46.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.46.1.tgz#195033e4b30b51b870dfcf2828e88d57b04a11cc" + integrity sha512-V/zMyfI+jDmL1ADxfDxjZ0EMbtiVqj8LUGPAGyBkXXStWmCUErMpW873zEHsyguWCuq2iN4BrlWUkmuVj84yng== dependencies: - "@typescript-eslint/typescript-estree" "5.45.0" - "@typescript-eslint/utils" "5.45.0" + "@typescript-eslint/typescript-estree" "5.46.1" + "@typescript-eslint/utils" "5.46.1" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.45.0": - version "5.45.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.45.0.tgz#794760b9037ee4154c09549ef5a96599621109c5" - integrity sha512-QQij+u/vgskA66azc9dCmx+rev79PzX8uDHpsqSjEFtfF2gBUTRCpvYMh2gw2ghkJabNkPlSUCimsyBEQZd1DA== +"@typescript-eslint/types@5.46.1": + version "5.46.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.46.1.tgz#4e9db2107b9a88441c4d5ecacde3bb7a5ebbd47e" + integrity sha512-Z5pvlCaZgU+93ryiYUwGwLl9AQVB/PQ1TsJ9NZ/gHzZjN7g9IAn6RSDkpCV8hqTwAiaj6fmCcKSQeBPlIpW28w== -"@typescript-eslint/typescript-estree@5.45.0": - version "5.45.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.45.0.tgz#f70a0d646d7f38c0dfd6936a5e171a77f1e5291d" - integrity sha512-maRhLGSzqUpFcZgXxg1qc/+H0bT36lHK4APhp0AEUVrpSwXiRAomm/JGjSG+kNUio5kAa3uekCYu/47cnGn5EQ== +"@typescript-eslint/typescript-estree@5.46.1": + version "5.46.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.46.1.tgz#5358088f98a8f9939355e0996f9c8f41c25eced2" + integrity sha512-j9W4t67QiNp90kh5Nbr1w92wzt+toiIsaVPnEblB2Ih2U9fqBTyqV9T3pYWZBRt6QoMh/zVWP59EpuCjc4VRBg== dependencies: - "@typescript-eslint/types" "5.45.0" - "@typescript-eslint/visitor-keys" "5.45.0" + "@typescript-eslint/types" "5.46.1" + "@typescript-eslint/visitor-keys" "5.46.1" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.45.0": - version "5.45.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.45.0.tgz#9cca2996eee1b8615485a6918a5c763629c7acf5" - integrity sha512-OUg2JvsVI1oIee/SwiejTot2OxwU8a7UfTFMOdlhD2y+Hl6memUSL4s98bpUTo8EpVEr0lmwlU7JSu/p2QpSvA== +"@typescript-eslint/utils@5.46.1": + version "5.46.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.46.1.tgz#7da3c934d9fd0eb4002a6bb3429f33298b469b4a" + integrity sha512-RBdBAGv3oEpFojaCYT4Ghn4775pdjvwfDOfQ2P6qzNVgQOVrnSPe5/Pb88kv7xzYQjoio0eKHKB9GJ16ieSxvA== dependencies: "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.45.0" - "@typescript-eslint/types" "5.45.0" - "@typescript-eslint/typescript-estree" "5.45.0" + "@typescript-eslint/scope-manager" "5.46.1" + "@typescript-eslint/types" "5.46.1" + "@typescript-eslint/typescript-estree" "5.46.1" eslint-scope "^5.1.1" eslint-utils "^3.0.0" semver "^7.3.7" -"@typescript-eslint/visitor-keys@5.45.0": - version "5.45.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.45.0.tgz#e0d160e9e7fdb7f8da697a5b78e7a14a22a70528" - integrity sha512-jc6Eccbn2RtQPr1s7th6jJWQHBHI6GBVQkCHoJFQ5UreaKm59Vxw+ynQUPPY2u2Amquc+7tmEoC2G52ApsGNNg== +"@typescript-eslint/visitor-keys@5.46.1": + version "5.46.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.46.1.tgz#126cc6fe3c0f83608b2b125c5d9daced61394242" + integrity sha512-jczZ9noovXwy59KjRTk1OftT78pwygdcmCuBf8yMoWt/8O8l+6x2LSEze0E4TeepXK4MezW3zGSyoDRZK7Y9cg== dependencies: - "@typescript-eslint/types" "5.45.0" + "@typescript-eslint/types" "5.46.1" eslint-visitor-keys "^3.3.0" JSONStream@^1.0.3: @@ -2636,12 +2646,7 @@ chokidar@^3.4.0: optionalDependencies: fsevents "~2.3.2" -ci-info@^3.2.0: - version "3.6.1" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.6.1.tgz#7594f1c95cb7fdfddee7af95a13af7dbc67afdcf" - integrity sha512-up5ggbaDqOqJ4UqLKZ2naVkyqSJQgJi5lwD6b6mM748ysrghDBX0bx/qJTUHzw7zu6Mq4gycviSF5hJnwceD8w== - -ci-info@^3.6.1: +ci-info@^3.2.0, ci-info@^3.6.1: version "3.7.0" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.7.0.tgz#6d01b3696c59915b6ce057e4aa4adfc2fa25f5ef" integrity sha512-2CpRNYmImPx+RXKLq6jko/L07phmS9I02TyqkcNU20GCF/GgaWvc58hPtjxDX8lPpkdwc9sNh72V9k00S7ezog== @@ -3399,12 +3404,12 @@ eslint-plugin-tsdoc@^0.2.17: "@microsoft/tsdoc-config" "0.16.2" eslint-plugin-unicorn@^45.0.0: - version "45.0.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-45.0.1.tgz#2307f4620502fd955c819733ce1276bed705b736" - integrity sha512-tLnIw5oDJJc3ILYtlKtqOxPP64FZLTkZkgeuoN6e7x6zw+rhBjOxyvq2c7577LGxXuIhBYrwisZuKNqOOHp3BA== + version "45.0.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-45.0.2.tgz#d6ba704793a6909fe5dfe013900d2b05b715284c" + integrity sha512-Y0WUDXRyGDMcKLiwgL3zSMpHrXI00xmdyixEGIg90gHnj0PcHY4moNv3Ppje/kDivdAy5vUeUr7z211ImPv2gw== dependencies: "@babel/helper-validator-identifier" "^7.19.1" - "@eslint-community/eslint-utils" "^4.1.0" + "@eslint-community/eslint-utils" "^4.1.2" ci-info "^3.6.1" clean-regexp "^1.0.0" esquery "^1.4.0" @@ -3458,10 +3463,10 @@ eslint-visitor-keys@^3.3.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== -eslint@8.28.0: - version "8.28.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.28.0.tgz#81a680732634677cc890134bcdd9fdfea8e63d6e" - integrity sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ== +eslint@8.29.0: + version "8.29.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.29.0.tgz#d74a88a20fb44d59c51851625bc4ee8d0ec43f87" + integrity sha512-isQ4EEiyUjZFbEKvEGJKKGBwXtvXX+zJbkVKCgTuB9t/+jUBcy8avhkEwWJecI15BkRkOYmvIM5ynbhRjEkoeg== dependencies: "@eslint/eslintrc" "^1.3.3" "@humanwhocodes/config-array" "^0.11.6" @@ -3674,9 +3679,9 @@ fast-safe-stringify@^2.0.7: integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA== fastq@^1.6.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" - integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + version "1.14.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.14.0.tgz#107f69d7295b11e0fccc264e1fc6389f623731ce" + integrity sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg== dependencies: reusify "^1.0.4" @@ -5171,9 +5176,9 @@ makeerror@1.0.12: tmpl "1.0.5" marked@^4.0.19: - version "4.2.2" - resolved "https://registry.yarnpkg.com/marked/-/marked-4.2.2.tgz#1d2075ad6cdfe42e651ac221c32d949a26c0672a" - integrity sha512-JjBTFTAvuTgANXx82a5vzK9JLSMoV6V3LBVn4Uhdso6t7vXrGx7g1Cd2r6NYSsxrYbQGFCMqBDhFHyK5q2UvcQ== + version "4.2.4" + resolved "https://registry.yarnpkg.com/marked/-/marked-4.2.4.tgz#5a4ce6c7a1ae0c952601fce46376ee4cf1797e1c" + integrity sha512-Wcc9ikX7Q5E4BYDPvh1C6QNSxrjC9tBgz+A/vAhp59KXUgachw++uMvMKiSW8oA85nopmPZcEvBoex/YLMsiyA== matrix-events-sdk@0.0.1: version "0.0.1" @@ -5284,9 +5289,9 @@ minimatch@^3.0.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatc brace-expansion "^1.1.7" minimatch@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.0.tgz#1717b464f4971b144f6aabe8f2d0b8e4511e09c7" - integrity sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg== + version "5.1.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.1.tgz#6c9dffcf9927ff2a31e74b5af11adf8b9604b022" + integrity sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g== dependencies: brace-expansion "^2.0.1" @@ -5708,10 +5713,10 @@ prelude-ls@~1.1.2: resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== -prettier@2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.0.tgz#c7df58393c9ba77d6fba3921ae01faf994fb9dc9" - integrity sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA== +prettier@2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.1.tgz#4e1fd11c34e2421bc1da9aea9bd8127cd0a35efc" + integrity sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg== pretty-format@^28.1.3: version "28.1.3" @@ -6635,9 +6640,9 @@ tapable@^2.2.0: integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== terser@^5.5.1: - version "5.16.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.0.tgz#29362c6f5506e71545c73b069ccd199bb28f7f54" - integrity sha512-KjTV81QKStSfwbNiwlBXfcgMcOloyuRdb62/iLFPGBcVNF4EXjhdYBhYHmbJpiBrVxZhDvltE11j+LBQUxEEJg== + version "5.16.1" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.1.tgz#5af3bc3d0f24241c7fb2024199d5c461a1075880" + integrity sha512-xvQfyfA1ayT0qdK47zskQgRZeWLoOQ8JQ6mIgRGVNwZKdQMU+5FkCBjmv4QjcrTzyZquRw2FVtlJSRUmMKQslw== dependencies: "@jridgewell/source-map" "^0.3.2" acorn "^8.5.0" @@ -6879,9 +6884,9 @@ typedoc-plugin-missing-exports@^1.0.0: integrity sha512-7s6znXnuAj1eD9KYPyzVzR1lBF5nwAY8IKccP5sdoO9crG4lpd16RoFpLsh2PccJM+I2NASpr0+/NMka6ThwVA== typedoc@^0.23.20: - version "0.23.21" - resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.23.21.tgz#2a6b0e155f91ffa9689086706ad7e3e4bc11d241" - integrity sha512-VNE9Jv7BgclvyH9moi2mluneSviD43dCE9pY8RWkO88/DrEgJZk9KpUk7WO468c9WWs/+aG6dOnoH7ccjnErhg== + version "0.23.22" + resolved "https://registry.yarnpkg.com/typedoc/-/typedoc-0.23.22.tgz#e25281ca816cd92ecfdaf3ec336d27e7bebb69ac" + integrity sha512-5sJkjK60xp8A7YpcYniu3+Wf0QcgojEnhzHuCN+CkdpQkKRhOspon/9+sGTkGI8kjVkZs3KHrhltpQyVhRMVfw== dependencies: lunr "^2.3.9" marked "^4.0.19" @@ -6894,9 +6899,9 @@ typescript@^3.2.2: integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== typescript@^4.5.3, typescript@^4.5.4: - version "4.9.3" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.3.tgz#3aea307c1746b8c384435d8ac36b8a2e580d85db" - integrity sha512-CIfGzTelbKNEnLpLdGFgdyKhG23CKdKgQPOBc+OUNrkJ2vr+KSzsSV5kq5iWhEQbok+quxgGzrAtGWCyU7tHnA== + version "4.9.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.4.tgz#a2a3d2756c079abda241d75f149df9d561091e78" + integrity sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg== typeson-registry@^1.0.0-alpha.20: version "1.0.0-alpha.39" @@ -7050,16 +7055,16 @@ util@~0.12.0: is-typed-array "^1.1.3" which-typed-array "^1.1.2" -uuid@7: - version "7.0.3" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" - integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== - uuid@8.3.2, uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +uuid@9: + version "9.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5" + integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg== + v8-to-istanbul@^9.0.0, v8-to-istanbul@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz#b6f994b0b5d4ef255e17a0d17dc444a9f5132fa4" @@ -7088,9 +7093,9 @@ void-elements@^2.0.1: integrity sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung== vscode-oniguruma@^1.6.1: - version "1.6.2" - resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.6.2.tgz#aeb9771a2f1dbfc9083c8a7fdd9cccaa3f386607" - integrity sha512-KH8+KKov5eS/9WhofZR8M8dMHWN2gTxjMsG4jd04YhpbPR91fUj7rYQ2/XjeHCJWbg7X++ApRIU9NUwM2vTvLA== + version "1.7.0" + resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz#439bfad8fe71abd7798338d1cd3dc53a8beea94b" + integrity sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA== vscode-textmate@^6.0.0: version "6.0.0"