From e05f9b58150b506b810b1439cfa0cf58479600b3 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Fri, 5 Aug 2022 17:28:02 +0100 Subject: [PATCH 01/28] Add txn_id support to sliding sync This allows clients to know when a request has been applied on the server. This allows us to change `resend(): void` to `resend(): Promise` which resolves/rejects with the transaction ID when it has been applied/not. --- spec/integ/sliding-sync.spec.ts | 219 ++++++++++++++++++++++++++++++++ src/sliding-sync.ts | 74 +++++++++-- 2 files changed, 283 insertions(+), 10 deletions(-) diff --git a/spec/integ/sliding-sync.spec.ts b/spec/integ/sliding-sync.spec.ts index 9cf6ff2e9cd..cc7a1d0dd93 100644 --- a/spec/integ/sliding-sync.spec.ts +++ b/spec/integ/sliding-sync.spec.ts @@ -562,6 +562,225 @@ describe("SlidingSync", () => { }); }); + describe("transaction IDs", () => { + beforeAll(setupClient); + afterAll(teardownClient); + const roomId = "!foo:bar"; + + let slidingSync: SlidingSync; + + // really this applies to them all but it's easier to just test one + it("should resolve modifyRoomSubscriptions after SlidingSync.start() is called", async () => { + const roomSubInfo = { + timeline_limit: 1, + required_state: [ + ["m.room.name", ""], + ], + }; + // add the subscription + slidingSync = new SlidingSync(proxyBaseUrl, [], roomSubInfo, client, 1); + // modification before SlidingSync.start() + const subscribePromise = slidingSync.modifyRoomSubscriptions(new Set([roomId])); + let txnId; + httpBackend.when("POST", syncUrl).check(function(req) { + const body = req.data; + logger.log("txn got ", body); + expect(body.room_subscriptions).toBeTruthy(); + expect(body.room_subscriptions[roomId]).toEqual(roomSubInfo); + expect(body.txn_id).toBeTruthy(); + txnId = body.txn_id; + }).respond(200, function() { + return { + pos: "aaa", + txn_id: txnId, + lists: [], + extensions: {}, + rooms: { + [roomId]: { + name: "foo bar", + required_state: [], + timeline: [], + }, + }, + }; + }); + slidingSync.start(); + await httpBackend.flushAllExpected(); + await subscribePromise; + }); + it("should resolve setList during a connection", async () => { + const newList = { + ranges: [[0, 20]], + }; + const promise = slidingSync.setList(0, newList); + let txnId; + httpBackend.when("POST", syncUrl).check(function(req) { + const body = req.data; + logger.log("txn got ", body); + expect(body.room_subscriptions).toBeFalsy(); + expect(body.lists[0]).toEqual(newList); + expect(body.txn_id).toBeTruthy(); + txnId = body.txn_id; + }).respond(200, function() { + return { + pos: "bbb", + txn_id: txnId, + lists: [{ count: 5 }], + extensions: {}, + }; + }); + await httpBackend.flushAllExpected(); + await promise; + expect(txnId).toBeDefined(); + }); + it("should resolve setListRanges during a connection", async () => { + const promise = slidingSync.setListRanges(0, [[20, 40]]); + let txnId; + httpBackend.when("POST", syncUrl).check(function(req) { + const body = req.data; + logger.log("txn got ", body); + expect(body.room_subscriptions).toBeFalsy(); + expect(body.lists[0]).toEqual({ + ranges: [[20, 40]], + }); + expect(body.txn_id).toBeTruthy(); + txnId = body.txn_id; + }).respond(200, function() { + return { + pos: "ccc", + txn_id: txnId, + lists: [{ count: 5 }], + extensions: {}, + }; + }); + await httpBackend.flushAllExpected(); + await promise; + expect(txnId).toBeDefined(); + }); + it("should resolve modifyRoomSubscriptionInfo during a connection", async () => { + const promise = slidingSync.modifyRoomSubscriptionInfo({ + timeline_limit: 99, + }); + let txnId; + httpBackend.when("POST", syncUrl).check(function(req) { + const body = req.data; + logger.log("txn got ", body); + expect(body.room_subscriptions).toBeTruthy(); + expect(body.room_subscriptions[roomId]).toEqual({ + timeline_limit: 99, + }); + expect(body.txn_id).toBeTruthy(); + txnId = body.txn_id; + }).respond(200, function() { + return { + pos: "ddd", + txn_id: txnId, + extensions: {}, + }; + }); + await httpBackend.flushAllExpected(); + await promise; + expect(txnId).toBeDefined(); + }); + it("should reject earlier pending promises if a later transaction is acknowledged", async () => { + // i.e if we have [A,B,C] and see txn_id=C then A,B should be rejected. + const gotTxnIds = []; + const pushTxn = function(req) { + gotTxnIds.push(req.data.txn_id); + }; + const failPromise = slidingSync.setListRanges(0, [[20, 40]]); + httpBackend.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "e" }); // missing txn_id + await httpBackend.flushAllExpected(); + const failPromise2 = slidingSync.setListRanges(0, [[60, 70]]); + httpBackend.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "f" }); // missing txn_id + await httpBackend.flushAllExpected(); + + // attach rejection handlers now else if we do it later Jest treats that as an unhandled rejection + // which is a fail. + expect(failPromise).rejects.toEqual(gotTxnIds[0]); + expect(failPromise2).rejects.toEqual(gotTxnIds[1]); + + const okPromise = slidingSync.setListRanges(0, [[0, 20]]); + let txnId; + httpBackend.when("POST", syncUrl).check((req) => { + txnId = req.data.txn_id; + }).respond(200, () => { + // include the txn_id, earlier requests should now be reject()ed. + return { + pos: "g", + txn_id: txnId, + }; + }); + await httpBackend.flushAllExpected(); + await okPromise; + + expect(txnId).toBeDefined(); + }); + it("should not reject later pending promises if an earlier transaction is acknowledged", async () => { + // i.e if we have [A,B,C] and see txn_id=B then C should not be rejected but A should. + const gotTxnIds = []; + const pushTxn = function(req) { + gotTxnIds.push(req.data.txn_id); + }; + const A = slidingSync.setListRanges(0, [[20, 40]]); + httpBackend.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "A" }); + await httpBackend.flushAllExpected(); + const B = slidingSync.setListRanges(0, [[60, 70]]); + httpBackend.when("POST", syncUrl).check(pushTxn).respond(200, { pos: "B" }); // missing txn_id + await httpBackend.flushAllExpected(); + + // attach rejection handlers now else if we do it later Jest treats that as an unhandled rejection + // which is a fail. + expect(A).rejects.toEqual(gotTxnIds[0]); + + const C = slidingSync.setListRanges(0, [[0, 20]]); + let pendingC = true; + C.finally(() => { + pendingC = false; + }); + httpBackend.when("POST", syncUrl).check(pushTxn).respond(200, () => { + // include the txn_id for B, so C's promise is outstanding + return { + pos: "C", + txn_id: gotTxnIds[1], + }; + }); + await httpBackend.flushAllExpected(); + // A is rejected, see above + expect(B).resolves.toEqual(gotTxnIds[1]); // B is resolved + expect(pendingC).toBe(true); // C is pending still + }); + it("should do nothing for unknown txn_ids", async () => { + const promise = slidingSync.setListRanges(0, [[20, 40]]); + let pending = true; + promise.finally(() => { + pending = false; + }); + let txnId; + httpBackend.when("POST", syncUrl).check(function(req) { + const body = req.data; + logger.log("txn got ", body); + expect(body.room_subscriptions).toBeFalsy(); + expect(body.lists[0]).toEqual({ + ranges: [[20, 40]], + }); + expect(body.txn_id).toBeTruthy(); + txnId = body.txn_id; + }).respond(200, function() { + return { + pos: "ccc", + txn_id: "bogus transaction id", + lists: [{ count: 5 }], + extensions: {}, + }; + }); + await httpBackend.flushAllExpected(); + expect(txnId).toBeDefined(); + expect(pending).toBe(true); + slidingSync.stop(); + }); + }); + describe("extensions", () => { beforeAll(setupClient); afterAll(teardownClient); diff --git a/src/sliding-sync.ts b/src/sliding-sync.ts index 5254a077dcf..9ce62a07333 100644 --- a/src/sliding-sync.ts +++ b/src/sliding-sync.ts @@ -68,6 +68,7 @@ export interface MSC3575SlidingSyncRequest { unsubscribe_rooms?: string[]; room_subscriptions?: Record; extensions?: object; + txn_id?: string; // query params pos?: string; @@ -126,6 +127,7 @@ type Operation = DeleteOperation | InsertOperation | InvalidateOperation | SyncO */ export interface MSC3575SlidingSyncResponse { pos: string; + txn_id?: string; lists: ListResponse[]; rooms: Record; extensions: object; @@ -334,6 +336,11 @@ export class SlidingSync extends TypedEventEmitter = {}; @@ -404,9 +411,9 @@ export class SlidingSync extends TypedEventEmitter { this.lists[index].updateListRange(ranges); - this.resend(); + return this.resend(); } /** @@ -415,14 +422,14 @@ export class SlidingSync extends TypedEventEmitter { if (this.lists[index]) { this.lists[index].replaceList(list); } else { this.lists[index] = new SlidingList(list); } this.listModifiedCount += 1; - this.resend(); + return this.resend(); } /** @@ -439,9 +446,9 @@ export class SlidingSync extends TypedEventEmitter) { + public modifyRoomSubscriptions(s: Set): Promise { this.desiredRoomSubscriptions = s; - this.resend(); + return this.resend(); } /** @@ -449,10 +456,10 @@ export class SlidingSync extends TypedEventEmitter { this.roomSubscriptionInfo = rs; this.confirmedRoomSubscriptions = new Set(); - this.resend(); + return this.resend(); } /** @@ -615,11 +622,52 @@ export class SlidingSync extends TypedEventEmitter { this.needsResend = true; + this.txnId = ""+Math.random(); + const p: Promise = new Promise((resolve, reject) => { + this.txnIdDefers.push({ + txnId: this.txnId, + resolve: resolve, + reject: reject, + }); + }); this.pendingReq?.abort(); + return p; + } + + private resolveTransactionDefers(txnId?: string) { + if (!txnId) { + return; + } + // find the matching index + let txnIndex = -1; + for (let i = 0; i < this.txnIdDefers.length; i++) { + if (this.txnIdDefers[i].txnId === txnId) { + txnIndex = i; + break; + } + } + if (txnIndex === -1) { + // this shouldn't happen; we shouldn't be seeing txn_ids for things we don't know about, + // whine about it. + logger.warn(`resolveTransactionDefers: seen ${txnId} but it isn't a pending txn, ignoring.`); + return; + } + // This list is sorted in time, so if the input txnId ACKs in the middle of this array, + // then everything before it that hasn't been ACKed yet never will and we should reject them. + for (let i = 0; i < txnIndex; i++) { + if (i < txnIndex) { + this.txnIdDefers[i].reject(this.txnIdDefers[i].txnId); + } + } + this.txnIdDefers[txnIndex].resolve(txnId); + // clear out settled promises, incuding the one we resolved. + this.txnIdDefers = this.txnIdDefers.slice(txnIndex+1); } /** @@ -666,6 +714,10 @@ export class SlidingSync extends TypedEventEmitter Date: Mon, 8 Aug 2022 14:26:24 +0100 Subject: [PATCH 02/28] Review comments --- src/sliding-sync.ts | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/src/sliding-sync.ts b/src/sliding-sync.ts index 9ce62a07333..9d5cefe02b3 100644 --- a/src/sliding-sync.ts +++ b/src/sliding-sync.ts @@ -19,7 +19,7 @@ import { IAbortablePromise } from "./@types/partials"; import { MatrixClient } from "./client"; import { IRoomEvent, IStateEvent } from "./sync-accumulator"; import { TypedEventEmitter } from "./models//typed-event-emitter"; -import { sleep } from "./utils"; +import { sleep, IDeferred, defer } from "./utils"; // /sync requests allow you to set a timeout= but the request may continue // beyond that and wedge forever, so we need to track how long we are willing @@ -340,7 +340,7 @@ export class SlidingSync extends TypedEventEmitter & { txnId: string})[] = []; // map of extension name to req/resp handler private extensions: Record = {}; @@ -410,6 +410,9 @@ export class SlidingSync extends TypedEventEmitter { this.lists[index].updateListRange(ranges); @@ -421,6 +424,9 @@ export class SlidingSync extends TypedEventEmitter { if (this.lists[index]) { @@ -445,6 +451,9 @@ export class SlidingSync extends TypedEventEmitter): Promise { this.desiredRoomSubscriptions = s; @@ -455,6 +464,9 @@ export class SlidingSync extends TypedEventEmitter { this.roomSubscriptionInfo = rs; @@ -628,16 +640,14 @@ export class SlidingSync extends TypedEventEmitter { this.needsResend = true; - this.txnId = ""+Math.random(); - const p: Promise = new Promise((resolve, reject) => { - this.txnIdDefers.push({ - txnId: this.txnId, - resolve: resolve, - reject: reject, - }); + this.txnId = this.client.makeTxnId(); + const d = defer(); + this.txnIdDefers.push({ + ...d, + txnId: this.txnId, }); this.pendingReq?.abort(); - return p; + return d.promise; } private resolveTransactionDefers(txnId?: string) { From 24d4181a084bac3096c46ef0f30f181f950fa83e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 9 Aug 2022 15:42:40 -0400 Subject: [PATCH 03/28] Update typescript-eslint monorepo to v5.33.0 (#2579) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 90 +++++++++++++++++++++++++++---------------------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/yarn.lock b/yarn.lock index fc673f36560..aa5dcc96457 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1632,13 +1632,13 @@ "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^5.6.0": - version "5.31.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.31.0.tgz#cae1967b1e569e6171bbc6bec2afa4e0c8efccfe" - integrity sha512-VKW4JPHzG5yhYQrQ1AzXgVgX8ZAJEvCz0QI6mLRX4tf7rnFfh5D8SKm0Pq6w5PyNfAWJk6sv313+nEt3ohWMBQ== + version "5.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.33.0.tgz#059798888720ec52ffa96c5f868e31a8f70fa3ec" + integrity sha512-jHvZNSW2WZ31OPJ3enhLrEKvAZNyAFWZ6rx9tUwaessTc4sx9KmgMNhVcqVAl1ETnT5rU5fpXTLmY9YvC1DCNg== dependencies: - "@typescript-eslint/scope-manager" "5.31.0" - "@typescript-eslint/type-utils" "5.31.0" - "@typescript-eslint/utils" "5.31.0" + "@typescript-eslint/scope-manager" "5.33.0" + "@typescript-eslint/type-utils" "5.33.0" + "@typescript-eslint/utils" "5.33.0" debug "^4.3.4" functional-red-black-tree "^1.0.1" ignore "^5.2.0" @@ -1647,68 +1647,68 @@ tsutils "^3.21.0" "@typescript-eslint/parser@^5.6.0": - version "5.31.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.31.0.tgz#7f42d7dcc68a0a6d80a0f3d9a65063aee7bb8d2c" - integrity sha512-UStjQiZ9OFTFReTrN+iGrC6O/ko9LVDhreEK5S3edmXgR396JGq7CoX2TWIptqt/ESzU2iRKXAHfSF2WJFcWHw== + version "5.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.33.0.tgz#26ec3235b74f0667414613727cb98f9b69dc5383" + integrity sha512-cgM5cJrWmrDV2KpvlcSkelTBASAs1mgqq+IUGKJvFxWrapHpaRy5EXPQz9YaKF3nZ8KY18ILTiVpUtbIac86/w== dependencies: - "@typescript-eslint/scope-manager" "5.31.0" - "@typescript-eslint/types" "5.31.0" - "@typescript-eslint/typescript-estree" "5.31.0" + "@typescript-eslint/scope-manager" "5.33.0" + "@typescript-eslint/types" "5.33.0" + "@typescript-eslint/typescript-estree" "5.33.0" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.31.0": - version "5.31.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.31.0.tgz#f47a794ba84d9b818ab7f8f44fff55a61016c606" - integrity sha512-8jfEzBYDBG88rcXFxajdVavGxb5/XKXyvWgvD8Qix3EEJLCFIdVloJw+r9ww0wbyNLOTYyBsR+4ALNGdlalLLg== +"@typescript-eslint/scope-manager@5.33.0": + version "5.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.33.0.tgz#509d7fa540a2c58f66bdcfcf278a3fa79002e18d" + integrity sha512-/Jta8yMNpXYpRDl8EwF/M8It2A9sFJTubDo0ATZefGXmOqlaBffEw0ZbkbQ7TNDK6q55NPHFshGBPAZvZkE8Pw== dependencies: - "@typescript-eslint/types" "5.31.0" - "@typescript-eslint/visitor-keys" "5.31.0" + "@typescript-eslint/types" "5.33.0" + "@typescript-eslint/visitor-keys" "5.33.0" -"@typescript-eslint/type-utils@5.31.0": - version "5.31.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.31.0.tgz#70a0b7201360b5adbddb0c36080495aa08f6f3d9" - integrity sha512-7ZYqFbvEvYXFn9ax02GsPcEOmuWNg+14HIf4q+oUuLnMbpJ6eHAivCg7tZMVwzrIuzX3QCeAOqKoyMZCv5xe+w== +"@typescript-eslint/type-utils@5.33.0": + version "5.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.33.0.tgz#92ad1fba973c078d23767ce2d8d5a601baaa9338" + integrity sha512-2zB8uEn7hEH2pBeyk3NpzX1p3lF9dKrEbnXq1F7YkpZ6hlyqb2yZujqgRGqXgRBTHWIUG3NGx/WeZk224UKlIA== dependencies: - "@typescript-eslint/utils" "5.31.0" + "@typescript-eslint/utils" "5.33.0" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.31.0": - version "5.31.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.31.0.tgz#7aa389122b64b18e473c1672fb3b8310e5f07a9a" - integrity sha512-/f/rMaEseux+I4wmR6mfpM2wvtNZb1p9hAV77hWfuKc3pmaANp5dLAZSiE3/8oXTYTt3uV9KW5yZKJsMievp6g== +"@typescript-eslint/types@5.33.0": + version "5.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.33.0.tgz#d41c584831805554b063791338b0220b613a275b" + integrity sha512-nIMt96JngB4MYFYXpZ/3ZNU4GWPNdBbcB5w2rDOCpXOVUkhtNlG2mmm8uXhubhidRZdwMaMBap7Uk8SZMU/ppw== -"@typescript-eslint/typescript-estree@5.31.0": - version "5.31.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.31.0.tgz#eb92970c9d6e3946690d50c346fb9b1d745ee882" - integrity sha512-3S625TMcARX71wBc2qubHaoUwMEn+l9TCsaIzYI/ET31Xm2c9YQ+zhGgpydjorwQO9pLfR/6peTzS/0G3J/hDw== +"@typescript-eslint/typescript-estree@5.33.0": + version "5.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.33.0.tgz#02d9c9ade6f4897c09e3508c27de53ad6bfa54cf" + integrity sha512-tqq3MRLlggkJKJUrzM6wltk8NckKyyorCSGMq4eVkyL5sDYzJJcMgZATqmF8fLdsWrW7OjjIZ1m9v81vKcaqwQ== dependencies: - "@typescript-eslint/types" "5.31.0" - "@typescript-eslint/visitor-keys" "5.31.0" + "@typescript-eslint/types" "5.33.0" + "@typescript-eslint/visitor-keys" "5.33.0" 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.31.0": - version "5.31.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.31.0.tgz#e146fa00dca948bfe547d665b2138a2dc1b79acd" - integrity sha512-kcVPdQS6VIpVTQ7QnGNKMFtdJdvnStkqS5LeALr4rcwx11G6OWb2HB17NMPnlRHvaZP38hL9iK8DdE9Fne7NYg== +"@typescript-eslint/utils@5.33.0": + version "5.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.33.0.tgz#46797461ce3146e21c095d79518cc0f8ec574038" + integrity sha512-JxOAnXt9oZjXLIiXb5ZIcZXiwVHCkqZgof0O8KPgz7C7y0HS42gi75PdPlqh1Tf109M0fyUw45Ao6JLo7S5AHw== dependencies: "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.31.0" - "@typescript-eslint/types" "5.31.0" - "@typescript-eslint/typescript-estree" "5.31.0" + "@typescript-eslint/scope-manager" "5.33.0" + "@typescript-eslint/types" "5.33.0" + "@typescript-eslint/typescript-estree" "5.33.0" eslint-scope "^5.1.1" eslint-utils "^3.0.0" -"@typescript-eslint/visitor-keys@5.31.0": - version "5.31.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.31.0.tgz#b0eca264df01ce85dceb76aebff3784629258f54" - integrity sha512-ZK0jVxSjS4gnPirpVjXHz7mgdOsZUHzNYSfTw2yPa3agfbt9YfqaBiBZFSSxeBWnpWkzCxTfUpnzA3Vily/CSg== +"@typescript-eslint/visitor-keys@5.33.0": + version "5.33.0" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.33.0.tgz#fbcbb074e460c11046e067bc3384b5d66b555484" + integrity sha512-/XsqCzD4t+Y9p5wd9HZiptuGKBlaZO5showwqODii5C0nZawxWLF+Q6k5wYHBrQv96h6GYKyqqMHCSTqta8Kiw== dependencies: - "@typescript-eslint/types" "5.31.0" + "@typescript-eslint/types" "5.33.0" eslint-visitor-keys "^3.3.0" JSONStream@^1.0.3: From 1645867ea6220763f86456f35093365017534da9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 10 Aug 2022 07:27:31 +0100 Subject: [PATCH 04/28] Update babel monorepo to v7.18.10 (#2578) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 242 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 156 insertions(+), 86 deletions(-) diff --git a/yarn.lock b/yarn.lock index aa5dcc96457..f841a40b6a3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -35,15 +35,15 @@ "@jridgewell/trace-mapping" "^0.3.9" "@babel/cli@^7.12.10": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.18.9.tgz#1fbc8424e5f74ae08bc61ec71609af29287d82d2" - integrity sha512-e7TOtHVrAXBJGNgoROVxqx0mathd01oJGXIDekRfxdrISnRqfM795APwkDtse9GdyPYivjg3iXiko3sF3W7f5Q== + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.18.10.tgz#4211adfc45ffa7d4f3cee6b60bb92e9fe68fe56a" + integrity sha512-dLvWH+ZDFAkd2jPBSghrsFBuXrREvFwjpDycXbmUoeochqKYe4zNSLEJYErpLg8dvxvZYe79/MkN461XCwpnGw== dependencies: "@jridgewell/trace-mapping" "^0.3.8" commander "^4.0.1" convert-source-map "^1.1.0" fs-readdir-recursive "^1.1.0" - glob "^7.0.0" + glob "^7.2.0" make-dir "^2.1.0" slash "^2.0.0" optionalDependencies: @@ -63,20 +63,20 @@ integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ== "@babel/core@^7.11.6", "@babel/core@^7.12.10", "@babel/core@^7.12.3", "@babel/core@^7.7.5": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.9.tgz#805461f967c77ff46c74ca0460ccf4fe933ddd59" - integrity sha512-1LIb1eL8APMy91/IMW+31ckrfBM4yCoLaVzoDhZUKSM4cu1L1nIidyxkCgzPAgrC5WEz36IPEr/eSeSF9pIn+g== + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.10.tgz#39ad504991d77f1f3da91be0b8b949a5bc466fb8" + integrity sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw== dependencies: "@ampproject/remapping" "^2.1.0" "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.18.9" + "@babel/generator" "^7.18.10" "@babel/helper-compilation-targets" "^7.18.9" "@babel/helper-module-transforms" "^7.18.9" "@babel/helpers" "^7.18.9" - "@babel/parser" "^7.18.9" - "@babel/template" "^7.18.6" - "@babel/traverse" "^7.18.9" - "@babel/types" "^7.18.9" + "@babel/parser" "^7.18.10" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.18.10" + "@babel/types" "^7.18.10" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -93,13 +93,13 @@ semver "^6.3.0" "@babel/eslint-plugin@^7.12.10": - version "7.17.7" - resolved "https://registry.yarnpkg.com/@babel/eslint-plugin/-/eslint-plugin-7.17.7.tgz#4ee1d5b29b79130f3bb5a933358376bcbee172b8" - integrity sha512-JATUoJJXSgwI0T8juxWYtK1JSgoLpIGUsCHIv+NMXcUDA2vIe6nvAHR9vnuJgs/P1hOFw7vPwibixzfqBBLIVw== + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/eslint-plugin/-/eslint-plugin-7.18.10.tgz#11f454b5d1aa64c42fcfd64abe93071c15ebea3c" + integrity sha512-iV1OZj/7eg4wZIcsVEkXS3MUWdhmpLsu2h+9Zr2ppywKWdCRs6VfjxbRzmHHYeurTizrrnaJ9ZkbO8KOv4lauQ== dependencies: eslint-rule-composer "^0.3.0" -"@babel/generator@^7.12.11", "@babel/generator@^7.18.9", "@babel/generator@^7.7.2": +"@babel/generator@^7.12.11", "@babel/generator@^7.7.2": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.9.tgz#68337e9ea8044d6ddc690fb29acae39359cca0a5" integrity sha512-wt5Naw6lJrL1/SGkipMiFxJjtyczUWTP38deiP1PO60HsBjDeKk08CGC3S8iVuvf0FmTdgKwU1KIXzSKL1G0Ug== @@ -108,6 +108,15 @@ "@jridgewell/gen-mapping" "^0.3.2" jsesc "^2.5.1" +"@babel/generator@^7.18.10", "@babel/generator@^7.18.9": + version "7.18.12" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.12.tgz#fa58daa303757bd6f5e4bbca91b342040463d9f4" + integrity sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg== + dependencies: + "@babel/types" "^7.18.10" + "@jridgewell/gen-mapping" "^0.3.2" + jsesc "^2.5.1" + "@babel/helper-annotate-as-pure@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz#eaa49f6f80d5a33f9a5dd2276e6d6e451be0a6bb" @@ -154,7 +163,7 @@ "@babel/helper-annotate-as-pure" "^7.18.6" regexpu-core "^5.1.0" -"@babel/helper-define-polyfill-provider@^0.3.1", "@babel/helper-define-polyfill-provider@^0.3.2": +"@babel/helper-define-polyfill-provider@^0.3.2": version "0.3.2" resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz#bd10d0aca18e8ce012755395b05a79f45eca5073" integrity sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg== @@ -166,7 +175,7 @@ resolve "^1.14.2" semver "^6.1.2" -"@babel/helper-environment-visitor@^7.18.6", "@babel/helper-environment-visitor@^7.18.9": +"@babel/helper-environment-visitor@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== @@ -233,7 +242,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f" integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w== -"@babel/helper-remap-async-to-generator@^7.18.6": +"@babel/helper-remap-async-to-generator@^7.18.6", "@babel/helper-remap-async-to-generator@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz#997458a0e3357080e54e1d79ec347f8a8cd28519" integrity sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA== @@ -275,6 +284,11 @@ dependencies: "@babel/types" "^7.18.6" +"@babel/helper-string-parser@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56" + integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw== + "@babel/helper-validator-identifier@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" @@ -286,14 +300,14 @@ integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== "@babel/helper-wrap-function@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.18.9.tgz#ae1feddc6ebbaa2fd79346b77821c3bd73a39646" - integrity sha512-cG2ru3TRAL6a60tfQflpEfs4ldiPwF6YW3zfJiRgmoFVIaC1vGnBBgatfec+ZUziPHkHSaXAuEck3Cdkf3eRpQ== + version "7.18.11" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.18.11.tgz#bff23ace436e3f6aefb61f85ffae2291c80ed1fb" + integrity sha512-oBUlbv+rjZLh2Ks9SKi4aL7eKaAXBWleHzU89mP0G6BMUlRxSckk9tSIkgDGydhgFxHuGSlBQZfnaD47oBEB7w== dependencies: "@babel/helper-function-name" "^7.18.9" - "@babel/template" "^7.18.6" - "@babel/traverse" "^7.18.9" - "@babel/types" "^7.18.9" + "@babel/template" "^7.18.10" + "@babel/traverse" "^7.18.11" + "@babel/types" "^7.18.10" "@babel/helpers@^7.18.9": version "7.18.9" @@ -313,11 +327,16 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.18.6", "@babel/parser@^7.18.9", "@babel/parser@^7.2.3", "@babel/parser@^7.9.4": +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.2.3", "@babel/parser@^7.9.4": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.9.tgz#f2dde0c682ccc264a9a8595efd030a5cc8fd2539" integrity sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg== +"@babel/parser@^7.18.10", "@babel/parser@^7.18.11", "@babel/parser@^7.18.6", "@babel/parser@^7.18.9": + version "7.18.11" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.11.tgz#68bb07ab3d380affa9a3f96728df07969645d2d9" + integrity sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ== + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz#da5b8f9a580acdfbe53494dba45ea389fb09a4d2" @@ -334,14 +353,14 @@ "@babel/helper-skip-transparent-expression-wrappers" "^7.18.9" "@babel/plugin-proposal-optional-chaining" "^7.18.9" -"@babel/plugin-proposal-async-generator-functions@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.6.tgz#aedac81e6fc12bb643374656dd5f2605bf743d17" - integrity sha512-WAz4R9bvozx4qwf74M+sfqPMKfSqwM0phxPTR6iJIi8robgzXwkEgmeJG1gEKhm6sDqT/U9aV3lfcqybIpev8w== +"@babel/plugin-proposal-async-generator-functions@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz#85ea478c98b0095c3e4102bff3b67d306ed24952" + integrity sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew== dependencies: - "@babel/helper-environment-visitor" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/helper-remap-async-to-generator" "^7.18.6" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" + "@babel/helper-remap-async-to-generator" "^7.18.9" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-proposal-class-properties@^7.12.1", "@babel/plugin-proposal-class-properties@^7.18.6": @@ -791,15 +810,15 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-transform-runtime@^7.12.10": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.9.tgz#d9e4b1b25719307bfafbf43065ed7fb3a83adb8f" - integrity sha512-wS8uJwBt7/b/mzE13ktsJdmS4JP/j7PQSaADtnb4I2wL0zK51MQ0pmF8/Jy0wUIS96fr+fXT6S/ifiPXnvrlSg== + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.10.tgz#37d14d1fa810a368fd635d4d1476c0154144a96f" + integrity sha512-q5mMeYAdfEbpBAgzl7tBre/la3LeCxmDO1+wMXRdPWbcoMjR3GiXlCLk7JBZVVye0bqTGNMbt0yYVXX1B1jEWQ== dependencies: "@babel/helper-module-imports" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.9" - babel-plugin-polyfill-corejs2 "^0.3.1" - babel-plugin-polyfill-corejs3 "^0.5.2" - babel-plugin-polyfill-regenerator "^0.3.1" + babel-plugin-polyfill-corejs2 "^0.3.2" + babel-plugin-polyfill-corejs3 "^0.5.3" + babel-plugin-polyfill-regenerator "^0.4.0" semver "^6.3.0" "@babel/plugin-transform-shorthand-properties@^7.18.6": @@ -847,12 +866,12 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/plugin-syntax-typescript" "^7.18.6" -"@babel/plugin-transform-unicode-escapes@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.6.tgz#0d01fb7fb2243ae1c033f65f6e3b4be78db75f27" - integrity sha512-XNRwQUXYMP7VLuy54cr/KS/WeL3AZeORhrmeZ7iewgu+X2eBqmpaLI/hzqr9ZxCeUoq0ASK4GUzSM0BDhZkLFw== +"@babel/plugin-transform-unicode-escapes@^7.18.10": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz#1ecfb0eda83d09bbcb77c09970c2dd55832aa246" + integrity sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ== dependencies: - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-unicode-regex@^7.18.6": version "7.18.6" @@ -863,9 +882,9 @@ "@babel/helper-plugin-utils" "^7.18.6" "@babel/preset-env@^7.12.11": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.18.9.tgz#9b3425140d724fbe590322017466580844c7eaff" - integrity sha512-75pt/q95cMIHWssYtyfjVlvI+QEZQThQbKvR9xH+F/Agtw/s4Wfc2V9Bwd/P39VtixB7oWxGdH4GteTTwYJWMg== + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.18.10.tgz#83b8dfe70d7eea1aae5a10635ab0a5fe60dfc0f4" + integrity sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA== dependencies: "@babel/compat-data" "^7.18.8" "@babel/helper-compilation-targets" "^7.18.9" @@ -873,7 +892,7 @@ "@babel/helper-validator-option" "^7.18.6" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.18.6" "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.18.9" - "@babel/plugin-proposal-async-generator-functions" "^7.18.6" + "@babel/plugin-proposal-async-generator-functions" "^7.18.10" "@babel/plugin-proposal-class-properties" "^7.18.6" "@babel/plugin-proposal-class-static-block" "^7.18.6" "@babel/plugin-proposal-dynamic-import" "^7.18.6" @@ -933,13 +952,13 @@ "@babel/plugin-transform-sticky-regex" "^7.18.6" "@babel/plugin-transform-template-literals" "^7.18.9" "@babel/plugin-transform-typeof-symbol" "^7.18.9" - "@babel/plugin-transform-unicode-escapes" "^7.18.6" + "@babel/plugin-transform-unicode-escapes" "^7.18.10" "@babel/plugin-transform-unicode-regex" "^7.18.6" "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.18.9" - babel-plugin-polyfill-corejs2 "^0.3.1" - babel-plugin-polyfill-corejs3 "^0.5.2" - babel-plugin-polyfill-regenerator "^0.3.1" + "@babel/types" "^7.18.10" + babel-plugin-polyfill-corejs2 "^0.3.2" + babel-plugin-polyfill-corejs3 "^0.5.3" + babel-plugin-polyfill-regenerator "^0.4.0" core-js-compat "^3.22.1" semver "^6.3.0" @@ -981,7 +1000,16 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/template@^7.18.6", "@babel/template@^7.3.3": +"@babel/template@^7.18.10", "@babel/template@^7.18.6": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" + integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/parser" "^7.18.10" + "@babel/types" "^7.18.10" + +"@babel/template@^7.3.3": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.6.tgz#1283f4993e00b929d6e2d3c72fdc9168a2977a31" integrity sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw== @@ -990,7 +1018,7 @@ "@babel/parser" "^7.18.6" "@babel/types" "^7.18.6" -"@babel/traverse@^7.1.6", "@babel/traverse@^7.18.9", "@babel/traverse@^7.7.2": +"@babel/traverse@^7.1.6", "@babel/traverse@^7.7.2": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.9.tgz#deeff3e8f1bad9786874cb2feda7a2d77a904f98" integrity sha512-LcPAnujXGwBgv3/WHv01pHtb2tihcyW1XuL9wd7jqh1Z8AQkTd+QVjMrMijrln0T7ED3UXLIy36P9Ao7W75rYg== @@ -1006,7 +1034,23 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": +"@babel/traverse@^7.18.10", "@babel/traverse@^7.18.11", "@babel/traverse@^7.18.9": + version "7.18.11" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.11.tgz#3d51f2afbd83ecf9912bcbb5c4d94e3d2ddaa16f" + integrity sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ== + dependencies: + "@babel/code-frame" "^7.18.6" + "@babel/generator" "^7.18.10" + "@babel/helper-environment-visitor" "^7.18.9" + "@babel/helper-function-name" "^7.18.9" + "@babel/helper-hoist-variables" "^7.18.6" + "@babel/helper-split-export-declaration" "^7.18.6" + "@babel/parser" "^7.18.11" + "@babel/types" "^7.18.10" + debug "^4.1.0" + globals "^11.1.0" + +"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.9.tgz#7148d64ba133d8d73a41b3172ac4b83a1452205f" integrity sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg== @@ -1014,6 +1058,15 @@ "@babel/helper-validator-identifier" "^7.18.6" to-fast-properties "^2.0.0" +"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.4.4": + version "7.18.10" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.10.tgz#4908e81b6b339ca7c6b7a555a5fc29446f26dde6" + integrity sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ== + dependencies: + "@babel/helper-string-parser" "^7.18.10" + "@babel/helper-validator-identifier" "^7.18.6" + to-fast-properties "^2.0.0" + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" @@ -1998,7 +2051,7 @@ babel-plugin-jest-hoist@^28.1.3: "@types/babel__core" "^7.1.14" "@types/babel__traverse" "^7.0.6" -babel-plugin-polyfill-corejs2@^0.3.1: +babel-plugin-polyfill-corejs2@^0.3.2: version "0.3.2" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz#e4c31d4c89b56f3cf85b92558954c66b54bd972d" integrity sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q== @@ -2007,7 +2060,7 @@ babel-plugin-polyfill-corejs2@^0.3.1: "@babel/helper-define-polyfill-provider" "^0.3.2" semver "^6.1.1" -babel-plugin-polyfill-corejs3@^0.5.2: +babel-plugin-polyfill-corejs3@^0.5.3: version "0.5.3" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz#d7e09c9a899079d71a8b670c6181af56ec19c5c7" integrity sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw== @@ -2015,12 +2068,12 @@ babel-plugin-polyfill-corejs3@^0.5.2: "@babel/helper-define-polyfill-provider" "^0.3.2" core-js-compat "^3.21.0" -babel-plugin-polyfill-regenerator@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.3.1.tgz#2c0678ea47c75c8cc2fbb1852278d8fb68233990" - integrity sha512-Y2B06tvgHYt1x0yz17jGkGeeMr5FeKUu+ASJ+N6nB5lQ8Dapfg42i0OVrf8PNGJ3zKL4A23snMi1IRwrqqND7A== +babel-plugin-polyfill-regenerator@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.0.tgz#8f51809b6d5883e07e71548d75966ff7635527fe" + integrity sha512-RW1cnryiADFeHmfLS+WW/G431p1PsW5qdRdz0SDRi7TKcUgc7Oh/uXkT7MZ/+tGsT1BkczEAmD5XjUyJ5SWDTw== dependencies: - "@babel/helper-define-polyfill-provider" "^0.3.1" + "@babel/helper-define-polyfill-provider" "^0.3.2" babel-preset-current-node-syntax@^1.0.0: version "1.0.1" @@ -2313,15 +2366,15 @@ browserify@^17.0.0: vm-browserify "^1.0.0" xtend "^4.0.0" -browserslist@^4.20.2, browserslist@^4.21.2: - version "4.21.2" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.2.tgz#59a400757465535954946a400b841ed37e2b4ecf" - integrity sha512-MonuOgAtUB46uP5CezYbRaYKBNt2LxP0yX+Pmj4LkcDFGkn9Cbpi83d9sCjwQDErXsIJSzY5oKGDbgOlF/LPAA== +browserslist@^4.20.2, browserslist@^4.21.3: + version "4.21.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.3.tgz#5df277694eb3c48bc5c4b05af3e8b7e09c5a6d1a" + integrity sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ== dependencies: - caniuse-lite "^1.0.30001366" - electron-to-chromium "^1.4.188" + caniuse-lite "^1.0.30001370" + electron-to-chromium "^1.4.202" node-releases "^2.0.6" - update-browserslist-db "^1.0.4" + update-browserslist-db "^1.0.5" bs58@^5.0.0: version "5.0.0" @@ -2411,10 +2464,10 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001366: - version "1.0.30001370" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001370.tgz#0a30d4f20d38b9e108cc5ae7cc62df9fe66cd5ba" - integrity sha512-3PDmaP56wz/qz7G508xzjx8C+MC2qEm4SYhSEzC9IBROo+dGXFWRuaXkWti0A9tuI00g+toiriVqxtWMgl350g== +caniuse-lite@^1.0.30001370: + version "1.0.30001374" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001374.tgz#3dab138e3f5485ba2e74bd13eca7fe1037ce6f57" + integrity sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw== caseless@~0.12.0: version "0.12.0" @@ -2662,11 +2715,11 @@ convert-source-map@~1.1.0: integrity sha512-Y8L5rp6jo+g9VEPgvqNfEopjTR4OTYct8lXlS8iVQdmnjDvbdbzYe9rjtFCB9egC86JoNCU61WRY+ScjkZpnIg== core-js-compat@^3.21.0, core-js-compat@^3.22.1: - version "3.24.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.24.0.tgz#885958fac38bf3f4464a90f2663b4620f6aee6e3" - integrity sha512-F+2E63X3ff/nj8uIrf8Rf24UDGIz7p838+xjEp+Bx3y8OWXj+VTPPZNCtdqovPaS9o7Tka5mCH01Zn5vOd6UQg== + version "3.24.1" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.24.1.tgz#d1af84a17e18dfdd401ee39da9996f9a7ba887de" + integrity sha512-XhdNAGeRnTpp8xbD+sR/HFDK9CbeeeqXT6TuofXh3urqEevzkWmLRgrVoykodsw8okqo2pu1BOmuCKrHx63zdw== dependencies: - browserslist "^4.21.2" + browserslist "^4.21.3" semver "7.0.0" core-js@^2.4.0: @@ -2939,10 +2992,10 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" -electron-to-chromium@^1.4.188: - version "1.4.202" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.202.tgz#0c2ed733f42b02ec49a955c5badfcc65888c390b" - integrity sha512-JYsK2ex9lmQD27kj19fhXYxzFJ/phLAkLKHv49A5UY6kMRV2xED3qMMLg/voW/+0AR6wMiI+VxlmK9NDtdxlPA== +electron-to-chromium@^1.4.202: + version "1.4.212" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.212.tgz#20cd48e88288fd2428138c108804edb1961bf559" + integrity sha512-LjQUg1SpLj2GfyaPDVBUHdhmlDU1vDB4f0mJWSGkISoXQrn5/lH3ECPCuo2Bkvf6Y30wO+b69te+rZK/llZmjg== elliptic@^6.5.3: version "6.5.4" @@ -3599,7 +3652,7 @@ glob-parent@^6.0.1: dependencies: is-glob "^4.0.3" -glob@^7.0.0, glob@^7.1.0, glob@^7.1.3, glob@^7.1.4: +glob@^7.1.0, glob@^7.1.3, glob@^7.1.4, glob@^7.2.0: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -3885,13 +3938,20 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.4: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== -is-core-module@^2.8.1, is-core-module@^2.9.0: +is-core-module@^2.8.1: version "2.9.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== dependencies: has "^1.0.3" +is-core-module@^2.9.0: + version "2.10.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed" + integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg== + dependencies: + has "^1.0.3" + is-date-object@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" @@ -5031,7 +5091,17 @@ object-keys@^1.1.1: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object.assign@^4.1.0, object.assign@^4.1.2: +object.assign@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.3.tgz#d36b7700ddf0019abb6b1df1bb13f6445f79051f" + integrity sha512-ZFJnX3zltyjcYJL0RoCJuzb+11zWGyaDbjgxZbdV7rFEcHQuYxrZqhow67aA7xpes6LhojyFDaBKAFfogQrikA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +object.assign@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== @@ -6478,7 +6548,7 @@ 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== -update-browserslist-db@^1.0.4: +update-browserslist-db@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz#be06a5eedd62f107b7c19eb5bcefb194411abf38" integrity sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q== From 9ee94c9902bf0db56625dbb0059b3f6b17912bb0 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 10 Aug 2022 09:04:38 +0100 Subject: [PATCH 05/28] Update jsdoc.yml (#2577) --- .github/workflows/jsdoc.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/jsdoc.yml b/.github/workflows/jsdoc.yml index e36db60dd27..3763c7841d3 100644 --- a/.github/workflows/jsdoc.yml +++ b/.github/workflows/jsdoc.yml @@ -40,7 +40,7 @@ jobs: cp -a "$RUNNER_TEMP/$VERSION" . # Add the new directory to the index if it isn't there already - if ! grep -q "Version $VERSION" index.html; then + if ! grep -q ">Version $VERSION" index.html; then perl -i -pe 'BEGIN {$rel=shift} $_ =~ /^<\/ul>/ && print "
  • Version ${rel}
  • \n"' "$VERSION" index.html fi From e29e0d15a5711adfb6783b95274572d4feb6d504 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 10 Aug 2022 09:21:39 +0100 Subject: [PATCH 06/28] Set up basic Backporting action (#2580) * Create backport.yml * Update backport.yml --- .github/workflows/backport.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/backport.yml diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml new file mode 100644 index 00000000000..f9aa90f02e2 --- /dev/null +++ b/.github/workflows/backport.yml @@ -0,0 +1,28 @@ +name: Backport +on: + pull_request_target: + types: + - closed + - labeled + branches: + - develop + +jobs: + backport: + name: Backport + runs-on: ubuntu-latest + # Only react to merged PRs for security reasons. + # See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target. + if: > + github.event.pull_request.merged + && ( + github.event.action == 'closed' + || ( + github.event.action == 'labeled' + && contains(github.event.label.name, 'backport') + ) + ) + steps: + - uses: tibdex/backport@v2 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} From 1ba2730e2597cf97dc6e17048bf2bb13a28620ec Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 10 Aug 2022 11:33:51 +0100 Subject: [PATCH 07/28] Update backport.yml (#2582) --- .github/workflows/backport.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index f9aa90f02e2..e18f4631529 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -25,4 +25,6 @@ jobs: steps: - uses: tibdex/backport@v2 with: - github_token: ${{ secrets.GITHUB_TOKEN }} + labels_template: "[<%= labels %>]" + # We can't use GITHUB_TOKEN here or CI won't run on the new PR + github_token: ${{ secrets.ELEMENT_BOT_TOKEN }} From 055af933cc5d5ca574c5a38904ae6bb7a6497a79 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 10 Aug 2022 11:41:44 +0100 Subject: [PATCH 08/28] Update backport.yml --- .github/workflows/backport.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml index e18f4631529..21ebc70c27c 100644 --- a/.github/workflows/backport.yml +++ b/.github/workflows/backport.yml @@ -25,6 +25,6 @@ jobs: steps: - uses: tibdex/backport@v2 with: - labels_template: "[<%= labels %>]" + labels_template: "<%= JSON.stringify(labels) %>" # We can't use GITHUB_TOKEN here or CI won't run on the new PR github_token: ${{ secrets.ELEMENT_BOT_TOKEN }} From fa9f078a75794de5f46bf081409dd55a30cd4dc7 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 10 Aug 2022 11:53:13 +0100 Subject: [PATCH 09/28] Review comments --- spec/integ/sliding-sync.spec.ts | 10 +++++----- src/sliding-sync.ts | 8 +++++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/spec/integ/sliding-sync.spec.ts b/spec/integ/sliding-sync.spec.ts index cc7a1d0dd93..8c4a7ad1254 100644 --- a/spec/integ/sliding-sync.spec.ts +++ b/spec/integ/sliding-sync.spec.ts @@ -584,7 +584,7 @@ describe("SlidingSync", () => { let txnId; httpBackend.when("POST", syncUrl).check(function(req) { const body = req.data; - logger.log("txn got ", body); + logger.debug("got ", body); expect(body.room_subscriptions).toBeTruthy(); expect(body.room_subscriptions[roomId]).toEqual(roomSubInfo); expect(body.txn_id).toBeTruthy(); @@ -616,7 +616,7 @@ describe("SlidingSync", () => { let txnId; httpBackend.when("POST", syncUrl).check(function(req) { const body = req.data; - logger.log("txn got ", body); + logger.debug("got ", body); expect(body.room_subscriptions).toBeFalsy(); expect(body.lists[0]).toEqual(newList); expect(body.txn_id).toBeTruthy(); @@ -638,7 +638,7 @@ describe("SlidingSync", () => { let txnId; httpBackend.when("POST", syncUrl).check(function(req) { const body = req.data; - logger.log("txn got ", body); + logger.debug("got ", body); expect(body.room_subscriptions).toBeFalsy(); expect(body.lists[0]).toEqual({ ranges: [[20, 40]], @@ -664,7 +664,7 @@ describe("SlidingSync", () => { let txnId; httpBackend.when("POST", syncUrl).check(function(req) { const body = req.data; - logger.log("txn got ", body); + logger.debug("got ", body); expect(body.room_subscriptions).toBeTruthy(); expect(body.room_subscriptions[roomId]).toEqual({ timeline_limit: 99, @@ -759,7 +759,7 @@ describe("SlidingSync", () => { let txnId; httpBackend.when("POST", syncUrl).check(function(req) { const body = req.data; - logger.log("txn got ", body); + logger.debug("got ", body); expect(body.room_subscriptions).toBeFalsy(); expect(body.lists[0]).toEqual({ ranges: [[20, 40]], diff --git a/src/sliding-sync.ts b/src/sliding-sync.ts index 9d5cefe02b3..28026b3a999 100644 --- a/src/sliding-sync.ts +++ b/src/sliding-sync.ts @@ -639,6 +639,10 @@ export class SlidingSync extends TypedEventEmitter { + if (this.needsResend && this.txnIdDefers.length > 0) { + // we already have a resend queued, so just return the same promise + return this.txnIdDefers[this.txnIdDefers.length-1].promise; + } this.needsResend = true; this.txnId = this.client.makeTxnId(); const d = defer(); @@ -671,9 +675,7 @@ export class SlidingSync extends TypedEventEmitter Date: Wed, 10 Aug 2022 12:32:28 +0100 Subject: [PATCH 10/28] sliding sync bugfix: ensure history is treated as history and not live events with tests --- spec/integ/sliding-sync-sdk.spec.ts | 31 +++++++++++++++++++++ src/sliding-sync-sdk.ts | 42 ++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/spec/integ/sliding-sync-sdk.spec.ts b/spec/integ/sliding-sync-sdk.spec.ts index 12ff0ae2e08..34d84bddaed 100644 --- a/spec/integ/sliding-sync-sdk.spec.ts +++ b/spec/integ/sliding-sync-sdk.spec.ts @@ -28,6 +28,7 @@ import { import { SlidingSyncSdk } from "../../src/sliding-sync-sdk"; import { SyncState } from "../../src/sync"; import { IStoredClientOpts } from "../../src/client"; +import { logger } from "../../src/logger"; describe("SlidingSyncSdk", () => { let client: MatrixClient = null; @@ -372,6 +373,36 @@ describe("SlidingSyncSdk", () => { gotRoom.getUnreadNotificationCount(NotificationCountType.Total), ).toEqual(1); }); + + // Regression test for a bug which caused the timeline entries to be out-of-order + // when the same room appears twice with different timeline limits. E.g appears in + // the list with timeline_limit:1 then appears again as a room subscription with + // timeline_limit:50 + it("can return history with a larger timeline_limit", async () => { + const timeline = data[roomA].timeline; + const oldTimeline = [ + mkOwnEvent(EventType.RoomMessage, { body: "old event A" }), + mkOwnEvent(EventType.RoomMessage, { body: "old event B" }), + mkOwnEvent(EventType.RoomMessage, { body: "old event C" }), + ...timeline, + ]; + mockSlidingSync.emit(SlidingSyncEvent.RoomData, roomA, { + timeline: oldTimeline, + required_state: [], + name: data[roomA].name, + initial: true, // e.g requested via room subscription + }); + const gotRoom = client.getRoom(roomA); + expect(gotRoom).toBeDefined(); + + logger.log("want:", oldTimeline.map((e) => (e.type + " : " + (e.content || {}).body))); + logger.log("got:", gotRoom.getLiveTimeline().getEvents().map( + (e) => (e.getType() + " : " + e.getContent().body)), + ); + + // we expect the timeline now to be oldTimeline (so the old events are in fact old) + assertTimelineEvents(gotRoom.getLiveTimeline().getEvents(), oldTimeline); + }); }); }); }); diff --git a/src/sliding-sync-sdk.ts b/src/sliding-sync-sdk.ts index 39fafec9056..a32a86e1b38 100644 --- a/src/sliding-sync-sdk.ts +++ b/src/sliding-sync-sdk.ts @@ -406,9 +406,49 @@ export class SlidingSyncSdk { // this helps large account to speed up faster // room::decryptCriticalEvent is in charge of decrypting all the events // required for a client to function properly - const timelineEvents = mapEvents(this.client, room.roomId, roomData.timeline, false); + let timelineEvents = mapEvents(this.client, room.roomId, roomData.timeline, false); const ephemeralEvents = []; // TODO this.mapSyncEventsFormat(joinObj.ephemeral); + // TODO: handle threaded / beacon events + + if (roomData.initial) { + // we should not know about any of these timeline entries if this is a genuinely new room. + // If we do, then we've effectively done scrollback (e.g requesting timeline_limit: 1 for + // this room, then timeline_limit: 50). + const knownEvents: Record = {}; + room.getLiveTimeline().getEvents().forEach((e) => { + knownEvents[e.getId()] = true; + }); + // all unknown events BEFORE a known event must be scrollback e.g: + // D E <-- what we know + // A B C D E F <-- what we just received + // means: + // A B C <-- scrollback + // D E <-- dupes + // F <-- new event + // We bucket events based on if we have seen a known event yet. + const oldEvents: MatrixEvent[] = []; + const newEvents: MatrixEvent[] = []; + let seenKnownEvent = false; + for (let i = timelineEvents.length-1; i >= 0; i--) { + const recvEvent = timelineEvents[i]; + if (knownEvents[recvEvent.getId()]) { + seenKnownEvent = true; + continue; // don't include this event, it's a dupe + } + if (seenKnownEvent) { + // old -> new + oldEvents.push(recvEvent); + } else { + // old -> new + newEvents.unshift(recvEvent); + } + } + timelineEvents = newEvents; + // old events are scrollback, insert them now + room.addEventsToTimeline(oldEvents, true, room.getLiveTimeline()); + } + const encrypted = this.client.isRoomEncrypted(room.roomId); // we do this first so it's correct when any of the events fire if (roomData.notification_count != null) { From edcef9364ce6a003f1973ebeab44bb288bc80c59 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 10 Aug 2022 12:43:47 +0100 Subject: [PATCH 11/28] Only add events if there are some; set the pagination token for faster scrollback --- src/sliding-sync-sdk.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sliding-sync-sdk.ts b/src/sliding-sync-sdk.ts index a32a86e1b38..0b8e860e918 100644 --- a/src/sliding-sync-sdk.ts +++ b/src/sliding-sync-sdk.ts @@ -445,8 +445,10 @@ export class SlidingSyncSdk { } } timelineEvents = newEvents; - // old events are scrollback, insert them now - room.addEventsToTimeline(oldEvents, true, room.getLiveTimeline()); + if (oldEvents.length > 0) { + // old events are scrollback, insert them now + room.addEventsToTimeline(oldEvents, true, room.getLiveTimeline(), roomData.prev_batch); + } } const encrypted = this.client.isRoomEncrypted(room.roomId); From 2728d7477138a4ef1a559b0026e41c387d71fd6e Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Wed, 10 Aug 2022 19:53:36 +0100 Subject: [PATCH 12/28] Review comments --- src/sliding-sync-sdk.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sliding-sync-sdk.ts b/src/sliding-sync-sdk.ts index 0b8e860e918..74a03b99b45 100644 --- a/src/sliding-sync-sdk.ts +++ b/src/sliding-sync-sdk.ts @@ -415,9 +415,9 @@ export class SlidingSyncSdk { // we should not know about any of these timeline entries if this is a genuinely new room. // If we do, then we've effectively done scrollback (e.g requesting timeline_limit: 1 for // this room, then timeline_limit: 50). - const knownEvents: Record = {}; + const knownEvents = new Set(); room.getLiveTimeline().getEvents().forEach((e) => { - knownEvents[e.getId()] = true; + knownEvents.add(e.getId()); }); // all unknown events BEFORE a known event must be scrollback e.g: // D E <-- what we know @@ -432,7 +432,7 @@ export class SlidingSyncSdk { let seenKnownEvent = false; for (let i = timelineEvents.length-1; i >= 0; i--) { const recvEvent = timelineEvents[i]; - if (knownEvents[recvEvent.getId()]) { + if (knownEvents.has(recvEvent.getId())) { seenKnownEvent = true; continue; // don't include this event, it's a dupe } From 478270b225ec6c816620ee16da394e82096e75dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 11 Aug 2022 14:30:51 +0200 Subject: [PATCH 13/28] Fix finding event read up to if stable private read receipts is missing (#2585) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner Signed-off-by: Šimon Brandner --- spec/unit/room.spec.ts | 36 +++++++++++++++++++++++++++--------- src/models/room.ts | 2 +- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/spec/unit/room.spec.ts b/spec/unit/room.spec.ts index 410959ad774..309d43f030d 100644 --- a/spec/unit/room.spec.ts +++ b/spec/unit/room.spec.ts @@ -2458,25 +2458,43 @@ describe("Room", function() { } }); - it("should compare correctly by timestamp", () => { - for (let i = 1; i <= 3; i++) { + describe("correctly compares by timestamp", () => { + it("should correctly compare, if we have all receipts", () => { + for (let i = 1; i <= 3; i++) { + room.getUnfilteredTimelineSet = () => ({ + compareEventOrdering: (_1, _2) => null, + } as EventTimelineSet); + room.getReadReceiptForUserId = (userId, ignore, receiptType) => { + if (receiptType === ReceiptType.ReadPrivate) { + return { eventId: "eventId1", data: { ts: i === 1 ? 1 : 0 } } as IWrappedReceipt; + } + if (receiptType === ReceiptType.UnstableReadPrivate) { + return { eventId: "eventId2", data: { ts: i === 2 ? 1 : 0 } } as IWrappedReceipt; + } + if (receiptType === ReceiptType.Read) { + return { eventId: "eventId3", data: { ts: i === 3 ? 1 : 0 } } as IWrappedReceipt; + } + }; + + expect(room.getEventReadUpTo(userA)).toEqual(`eventId${i}`); + } + }); + + it("should correctly compare, if private read receipt is missing", () => { room.getUnfilteredTimelineSet = () => ({ compareEventOrdering: (_1, _2) => null, } as EventTimelineSet); room.getReadReceiptForUserId = (userId, ignore, receiptType) => { - if (receiptType === ReceiptType.ReadPrivate) { - return { eventId: "eventId1", data: { ts: i === 1 ? 1 : 0 } } as IWrappedReceipt; - } if (receiptType === ReceiptType.UnstableReadPrivate) { - return { eventId: "eventId2", data: { ts: i === 2 ? 1 : 0 } } as IWrappedReceipt; + return { eventId: "eventId1", data: { ts: 0 } } as IWrappedReceipt; } if (receiptType === ReceiptType.Read) { - return { eventId: "eventId3", data: { ts: i === 3 ? 1 : 0 } } as IWrappedReceipt; + return { eventId: "eventId2", data: { ts: 1 } } as IWrappedReceipt; } }; - expect(room.getEventReadUpTo(userA)).toEqual(`eventId${i}`); - } + expect(room.getEventReadUpTo(userA)).toEqual(`eventId2`); + }); }); describe("fallback precedence", () => { diff --git a/src/models/room.ts b/src/models/room.ts index 782acdbcb9d..5de6fa7e128 100644 --- a/src/models/room.ts +++ b/src/models/room.ts @@ -2592,7 +2592,7 @@ export class Room extends TypedEventEmitter let latest = privateReadReceipt; [unstablePrivateReadReceipt, publicReadReceipt].forEach((receipt) => { - if (receipt?.data?.ts > latest?.data?.ts) { + if (receipt?.data?.ts > latest?.data?.ts || !latest) { latest = receipt; } }); From 3f6f5b69c7a10145dc562f3b470385b195ef71c9 Mon Sep 17 00:00:00 2001 From: 3nprob <74199244+3nprob@users.noreply.github.com> Date: Thu, 11 Aug 2022 14:29:53 +0000 Subject: [PATCH 14/28] Improve test coverage and modernize style for interactive-auth (#2574) * style: address no-mixed-operators errors,minor style improvements * test: Fix async interactive-auth tests, add test case * tests: Fix incorrectly stringified mock response * pushprocessor: style update * use async primitives in interactive-auth-spec * lint * fixup: remove duplicate test * add test case for no-flow-with-session for interactive-auth * interactive-auth: handle non-existing error.data * async test fix * test: add dummyauth test * add testing for errcode * Revert "pushprocessor: style update" This reverts commit 3ed0fdfb73ae55b725aa7c74d9cab35fb96c9178. * add testcase for missing error data * test: move sessionId assignment * Add tests to improve coverage for interactive-auth * pushprocessor: style update --- spec/unit/autodiscovery.spec.ts | 35 ++-- spec/unit/interactive-auth.spec.ts | 314 ++++++++++++++++++++++++++--- src/autodiscovery.ts | 52 ++--- src/interactive-auth.ts | 28 ++- src/pushprocessor.ts | 10 +- src/utils.ts | 30 +-- src/webrtc/call.ts | 4 +- 7 files changed, 372 insertions(+), 101 deletions(-) diff --git a/spec/unit/autodiscovery.spec.ts b/spec/unit/autodiscovery.spec.ts index 71b48ef41e0..939f477977a 100644 --- a/spec/unit/autodiscovery.spec.ts +++ b/spec/unit/autodiscovery.spec.ts @@ -153,27 +153,26 @@ describe("AutoDiscovery", function() { ]); }); - it("should return FAIL_PROMPT when .well-known returns not-JSON", function() { + it("should return FAIL_PROMPT when .well-known returns not-JSON", async () => { const httpBackend = getHttpBackend(); - httpBackend.when("GET", "/.well-known/matrix/client").respond(200, "abc"); + httpBackend.when("GET", "/.well-known/matrix/client").respond(200, "abc", true); + const expected = { + "m.homeserver": { + state: "FAIL_PROMPT", + error: AutoDiscovery.ERROR_INVALID, + base_url: null, + }, + "m.identity_server": { + state: "PROMPT", + error: null, + base_url: null, + }, + }; return Promise.all([ httpBackend.flushAllExpected(), - AutoDiscovery.findClientConfig("example.org").then((conf) => { - const expected = { - "m.homeserver": { - state: "FAIL_PROMPT", - error: AutoDiscovery.ERROR_INVALID, - base_url: null, - }, - "m.identity_server": { - state: "PROMPT", - error: null, - base_url: null, - }, - }; - - expect(conf).toEqual(expected); - }), + AutoDiscovery.findClientConfig("example.org").then( + expect(expected).toEqual, + ), ]); }); diff --git a/spec/unit/interactive-auth.spec.ts b/spec/unit/interactive-auth.spec.ts index b21a9296a0e..8005f11791f 100644 --- a/spec/unit/interactive-auth.spec.ts +++ b/spec/unit/interactive-auth.spec.ts @@ -32,8 +32,8 @@ class FakeClient { const getFakeClient = (): MatrixClient => new FakeClient() as unknown as MatrixClient; -describe("InteractiveAuth", function() { - it("should start an auth stage and complete it", function() { +describe("InteractiveAuth", () => { + it("should start an auth stage and complete it", async () => { const doRequest = jest.fn(); const stateUpdated = jest.fn(); @@ -59,7 +59,7 @@ describe("InteractiveAuth", function() { }); // first we expect a call here - stateUpdated.mockImplementation(function(stage) { + stateUpdated.mockImplementation((stage) => { logger.log('aaaa'); expect(stage).toEqual(AuthType.Password); ia.submitAuthDict({ @@ -69,23 +69,130 @@ describe("InteractiveAuth", function() { // .. which should trigger a call here const requestRes = { "a": "b" }; - doRequest.mockImplementation(function(authData) { + doRequest.mockImplementation(async (authData) => { logger.log('cccc'); expect(authData).toEqual({ session: "sessionId", type: AuthType.Password, }); - return Promise.resolve(requestRes); + return requestRes; }); - return ia.attemptAuth().then(function(res) { - expect(res).toBe(requestRes); - expect(doRequest).toBeCalledTimes(1); - expect(stateUpdated).toBeCalledTimes(1); + const res = await ia.attemptAuth(); + expect(res).toBe(requestRes); + expect(doRequest).toBeCalledTimes(1); + expect(stateUpdated).toBeCalledTimes(1); + }); + + it("should handle auth errcode presence ", async () => { + const doRequest = jest.fn(); + const stateUpdated = jest.fn(); + + const ia = new InteractiveAuth({ + matrixClient: getFakeClient(), + doRequest: doRequest, + stateUpdated: stateUpdated, + requestEmailToken: jest.fn(), + authData: { + session: "sessionId", + flows: [ + { stages: [AuthType.Password] }, + ], + errcode: "MockError0", + params: { + [AuthType.Password]: { param: "aa" }, + }, + }, + }); + + expect(ia.getSessionId()).toEqual("sessionId"); + expect(ia.getStageParams(AuthType.Password)).toEqual({ + param: "aa", + }); + + // first we expect a call here + stateUpdated.mockImplementation((stage) => { + logger.log('aaaa'); + expect(stage).toEqual(AuthType.Password); + ia.submitAuthDict({ + type: AuthType.Password, + }); + }); + + // .. which should trigger a call here + const requestRes = { "a": "b" }; + doRequest.mockImplementation(async (authData) => { + logger.log('cccc'); + expect(authData).toEqual({ + session: "sessionId", + type: AuthType.Password, + }); + return requestRes; + }); + + const res = await ia.attemptAuth(); + expect(res).toBe(requestRes); + expect(doRequest).toBeCalledTimes(1); + expect(stateUpdated).toBeCalledTimes(1); + }); + + it("should handle set emailSid for email flow", async () => { + const doRequest = jest.fn(); + const stateUpdated = jest.fn(); + const requestEmailToken = jest.fn(); + + const ia = new InteractiveAuth({ + doRequest, + stateUpdated, + requestEmailToken, + matrixClient: getFakeClient(), + emailSid: 'myEmailSid', + authData: { + session: "sessionId", + flows: [ + { stages: [AuthType.Email, AuthType.Password] }, + ], + params: { + [AuthType.Email]: { param: "aa" }, + [AuthType.Password]: { param: "bb" }, + }, + }, + }); + + expect(ia.getSessionId()).toEqual("sessionId"); + expect(ia.getStageParams(AuthType.Email)).toEqual({ + param: "aa", + }); + + // first we expect a call here + stateUpdated.mockImplementation((stage) => { + logger.log('husky'); + expect(stage).toEqual(AuthType.Email); + ia.submitAuthDict({ + type: AuthType.Email, + }); + }); + + // .. which should trigger a call here + const requestRes = { "a": "b" }; + doRequest.mockImplementation(async (authData) => { + logger.log('barfoo'); + expect(authData).toEqual({ + session: "sessionId", + type: AuthType.Email, + }); + return requestRes; }); + + const res = await ia.attemptAuth(); + expect(res).toBe(requestRes); + expect(doRequest).toBeCalledTimes(1); + expect(stateUpdated).toBeCalledTimes(1); + expect(requestEmailToken).toBeCalledTimes(0); + expect(ia.getEmailSid()).toBe("myEmailSid"); }); - it("should make a request if no authdata is provided", function() { + it("should make a request if no authdata is provided", async () => { const doRequest = jest.fn(); const stateUpdated = jest.fn(); const requestEmailToken = jest.fn(); @@ -101,7 +208,7 @@ describe("InteractiveAuth", function() { expect(ia.getStageParams(AuthType.Password)).toBe(undefined); // first we expect a call to doRequest - doRequest.mockImplementation(function(authData) { + doRequest.mockImplementation((authData) => { logger.log("request1", authData); expect(authData).toEqual(null); // first request should be null const err = new MatrixError({ @@ -119,7 +226,7 @@ describe("InteractiveAuth", function() { // .. which should be followed by a call to stateUpdated const requestRes = { "a": "b" }; - stateUpdated.mockImplementation(function(stage) { + stateUpdated.mockImplementation((stage) => { expect(stage).toEqual(AuthType.Password); expect(ia.getSessionId()).toEqual("sessionId"); expect(ia.getStageParams(AuthType.Password)).toEqual({ @@ -127,13 +234,13 @@ describe("InteractiveAuth", function() { }); // submitAuthDict should trigger another call to doRequest - doRequest.mockImplementation(function(authData) { + doRequest.mockImplementation(async (authData) => { logger.log("request2", authData); expect(authData).toEqual({ session: "sessionId", type: AuthType.Password, }); - return Promise.resolve(requestRes); + return requestRes; }); ia.submitAuthDict({ @@ -141,14 +248,76 @@ describe("InteractiveAuth", function() { }); }); - return ia.attemptAuth().then(function(res) { - expect(res).toBe(requestRes); - expect(doRequest).toBeCalledTimes(2); - expect(stateUpdated).toBeCalledTimes(1); + const res = await ia.attemptAuth(); + expect(res).toBe(requestRes); + expect(doRequest).toBeCalledTimes(2); + expect(stateUpdated).toBeCalledTimes(1); + }); + + it("should make a request if authdata is null", async () => { + const doRequest = jest.fn(); + const stateUpdated = jest.fn(); + const requestEmailToken = jest.fn(); + + const ia = new InteractiveAuth({ + authData: null, + matrixClient: getFakeClient(), + stateUpdated, + doRequest, + requestEmailToken, + }); + + expect(ia.getSessionId()).toBe(undefined); + expect(ia.getStageParams(AuthType.Password)).toBe(undefined); + + // first we expect a call to doRequest + doRequest.mockImplementation((authData) => { + logger.log("request1", authData); + expect(authData).toEqual(null); // first request should be null + const err = new MatrixError({ + session: "sessionId", + flows: [ + { stages: [AuthType.Password] }, + ], + params: { + [AuthType.Password]: { param: "aa" }, + }, + }); + err.httpStatus = 401; + throw err; + }); + + // .. which should be followed by a call to stateUpdated + const requestRes = { "a": "b" }; + stateUpdated.mockImplementation((stage) => { + expect(stage).toEqual(AuthType.Password); + expect(ia.getSessionId()).toEqual("sessionId"); + expect(ia.getStageParams(AuthType.Password)).toEqual({ + param: "aa", + }); + + // submitAuthDict should trigger another call to doRequest + doRequest.mockImplementation(async (authData) => { + logger.log("request2", authData); + expect(authData).toEqual({ + session: "sessionId", + type: AuthType.Password, + }); + return requestRes; + }); + + ia.submitAuthDict({ + type: AuthType.Password, + }); }); + + const res = await ia.attemptAuth(); + expect(res).toBe(requestRes); + expect(doRequest).toBeCalledTimes(2); + expect(stateUpdated).toBeCalledTimes(1); }); - it("should start an auth stage and reject if no auth flow", function() { + it("should start an auth stage and reject if no auth flow", async () => { const doRequest = jest.fn(); const stateUpdated = jest.fn(); const requestEmailToken = jest.fn(); @@ -160,7 +329,7 @@ describe("InteractiveAuth", function() { requestEmailToken, }); - doRequest.mockImplementation(function(authData) { + doRequest.mockImplementation((authData) => { logger.log("request1", authData); expect(authData).toEqual(null); // first request should be null const err = new MatrixError({ @@ -174,9 +343,108 @@ describe("InteractiveAuth", function() { throw err; }); - return ia.attemptAuth().catch(function(error) { - expect(error.message).toBe('No appropriate authentication flow found'); + await expect(ia.attemptAuth.bind(ia)).rejects.toThrow( + new Error('No appropriate authentication flow found'), + ); + }); + + it("should start an auth stage and reject if no auth flow but has session", async () => { + const doRequest = jest.fn(); + const stateUpdated = jest.fn(); + const requestEmailToken = jest.fn(); + + const ia = new InteractiveAuth({ + matrixClient: getFakeClient(), + doRequest, + stateUpdated, + requestEmailToken, + authData: { + }, + sessionId: "sessionId", + }); + + doRequest.mockImplementation((authData) => { + logger.log("request1", authData); + expect(authData).toEqual({ "session": "sessionId" }); // has existing sessionId + const err = new MatrixError({ + session: "sessionId", + flows: [], + params: { + [AuthType.Password]: { param: "aa" }, + }, + error: "Mock Error 1", + errcode: "MOCKERR1", + }); + err.httpStatus = 401; + throw err; + }); + + await expect(ia.attemptAuth.bind(ia)).rejects.toThrow( + new Error('No appropriate authentication flow found'), + ); + }); + + it("should handle unexpected error types without data propery set", async () => { + const doRequest = jest.fn(); + const stateUpdated = jest.fn(); + const requestEmailToken = jest.fn(); + + const ia = new InteractiveAuth({ + matrixClient: getFakeClient(), + doRequest, + stateUpdated, + requestEmailToken, + authData: { + session: "sessionId", + }, + }); + + doRequest.mockImplementation((authData) => { + logger.log("request1", authData); + expect(authData).toEqual({ "session": "sessionId" }); // has existing sessionId + const err = new Error('myerror'); + (err as any).httpStatus = 401; + throw err; + }); + + await expect(ia.attemptAuth.bind(ia)).rejects.toThrow( + new Error("myerror"), + ); + }); + + it("should allow dummy auth", async () => { + const doRequest = jest.fn(); + const stateUpdated = jest.fn(); + const requestEmailToken = jest.fn(); + + const ia = new InteractiveAuth({ + matrixClient: getFakeClient(), + doRequest, + stateUpdated, + requestEmailToken, + authData: { + session: 'sessionId', + flows: [ + { stages: [AuthType.Dummy] }, + ], + params: {}, + }, + }); + + const requestRes = { "a": "b" }; + doRequest.mockImplementation((authData) => { + logger.log("request1", authData); + expect(authData).toEqual({ + session: "sessionId", + type: AuthType.Dummy, + }); + return requestRes; }); + + const res = await ia.attemptAuth(); + expect(res).toBe(requestRes); + expect(doRequest).toBeCalledTimes(1); + expect(stateUpdated).toBeCalledTimes(0); }); describe("requestEmailToken", () => { @@ -247,7 +515,7 @@ describe("InteractiveAuth", function() { doRequest, stateUpdated, requestEmailToken, }); - expect(async () => await ia.requestEmailToken()).rejects.toThrowError("unspecific network error"); + await expect(ia.requestEmailToken.bind(ia)).rejects.toThrowError("unspecific network error"); }); it("only starts one request at a time", async () => { diff --git a/src/autodiscovery.ts b/src/autodiscovery.ts index 58c4cb15736..71cbd2105f9 100644 --- a/src/autodiscovery.ts +++ b/src/autodiscovery.ts @@ -17,6 +17,8 @@ limitations under the License. /** @module auto-discovery */ +import { ServerResponse } from "http"; + import { IClientWellKnown, IWellKnownConfig } from "./client"; import { logger } from './logger'; @@ -409,39 +411,41 @@ export class AutoDiscovery { * @return {Promise} Resolves to the returned state. * @private */ - private static fetchWellKnownObject(url: string): Promise { - return new Promise(function(resolve) { + private static fetchWellKnownObject(uri: string): Promise { + return new Promise((resolve) => { // eslint-disable-next-line const request = require("./matrix").getRequest(); if (!request) throw new Error("No request library available"); request( - { method: "GET", uri: url, timeout: 5000 }, - (err, response, body) => { - if (err || response && - (response.statusCode < 200 || response.statusCode >= 300) - ) { - let action = AutoDiscoveryAction.FAIL_PROMPT; - let reason = (err ? err.message : null) || "General failure"; - if (response && response.statusCode === 404) { - action = AutoDiscoveryAction.IGNORE; - reason = AutoDiscovery.ERROR_MISSING_WELLKNOWN; - } - resolve({ raw: {}, action: action, reason: reason, error: err }); - return; + { method: "GET", uri, timeout: 5000 }, + (error: Error, response: ServerResponse, body: string) => { + if (error || response?.statusCode < 200 || response?.statusCode >= 300) { + const result = { error, raw: {} }; + return resolve(response?.statusCode === 404 + ? { + ...result, + action: AutoDiscoveryAction.IGNORE, + reason: AutoDiscovery.ERROR_MISSING_WELLKNOWN, + } : { + ...result, + action: AutoDiscoveryAction.FAIL_PROMPT, + reason: error?.message || "General failure", + }); } try { - resolve({ raw: JSON.parse(body), action: AutoDiscoveryAction.SUCCESS }); - } catch (e) { - let reason = AutoDiscovery.ERROR_INVALID; - if (e.name === "SyntaxError") { - reason = AutoDiscovery.ERROR_INVALID_JSON; - } - resolve({ + return resolve({ + raw: JSON.parse(body), + action: AutoDiscoveryAction.SUCCESS, + }); + } catch (err) { + return resolve({ + error: err, raw: {}, action: AutoDiscoveryAction.FAIL_PROMPT, - reason: reason, - error: e, + reason: err?.name === "SyntaxError" + ? AutoDiscovery.ERROR_INVALID_JSON + : AutoDiscovery.ERROR_INVALID, }); } }, diff --git a/src/interactive-auth.ts b/src/interactive-auth.ts index d5e10427e51..dab48a94477 100644 --- a/src/interactive-auth.ts +++ b/src/interactive-auth.ts @@ -250,12 +250,9 @@ export class InteractiveAuth { if (!this.data?.flows) { this.busyChangedCallback?.(true); // use the existing sessionId, if one is present. - let auth = null; - if (this.data.session) { - auth = { - session: this.data.session, - }; - } + const auth = this.data.session + ? { session: this.data.session } + : null; this.doRequest(auth).finally(() => { this.busyChangedCallback?.(false); }); @@ -312,7 +309,7 @@ export class InteractiveAuth { * @return {string} session id */ public getSessionId(): string { - return this.data ? this.data.session : undefined; + return this.data?.session; } /** @@ -477,6 +474,9 @@ export class InteractiveAuth { logger.log("Background poll request failed doing UI auth: ignoring", error); } } + if (!error.data) { + error.data = {}; + } // if the error didn't come with flows, completed flows or session ID, // copy over the ones we have. Synapse sometimes sends responses without // any UI auth data (eg. when polling for email validation, if the email @@ -539,19 +539,17 @@ export class InteractiveAuth { return; } - if (this.data && this.data.errcode || this.data.error) { + if (this.data?.errcode || this.data?.error) { this.stateUpdatedCallback(nextStage, { - errcode: this.data.errcode || "", - error: this.data.error || "", + errcode: this.data?.errcode || "", + error: this.data?.error || "", }); return; } - const stageStatus: IStageStatus = {}; - if (nextStage == EMAIL_STAGE_TYPE) { - stageStatus.emailSid = this.emailSid; - } - this.stateUpdatedCallback(nextStage, stageStatus); + this.stateUpdatedCallback(nextStage, nextStage === EMAIL_STAGE_TYPE + ? { emailSid: this.emailSid } + : {}); } /** diff --git a/src/pushprocessor.ts b/src/pushprocessor.ts index 950f395b7ed..4e736c3a1f4 100644 --- a/src/pushprocessor.ts +++ b/src/pushprocessor.ts @@ -362,13 +362,9 @@ export class PushProcessor { return false; } - let regex; - - if (cond.key == 'content.body') { - regex = this.createCachedRegex('(^|\\W)', cond.pattern, '(\\W|$)'); - } else { - regex = this.createCachedRegex('^', cond.pattern, '$'); - } + const regex = cond.key === 'content.body' + ? this.createCachedRegex('(^|\\W)', cond.pattern, '(\\W|$)') + : this.createCachedRegex('^', cond.pattern, '$'); return !!val.match(regex); } diff --git a/src/utils.ts b/src/utils.ts index 8ebafc1ae30..63e3475b8fb 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -328,22 +328,28 @@ export function escapeRegExp(string: string): string { } export function globToRegexp(glob: string, extended?: any): string { - extended = typeof(extended) === 'boolean' ? extended : true; // From // https://github.com/matrix-org/synapse/blob/abbee6b29be80a77e05730707602f3bbfc3f38cb/synapse/push/__init__.py#L132 // Because micromatch is about 130KB with dependencies, // and minimatch is not much better. - let pat = escapeRegExp(glob); - pat = pat.replace(/\\\*/g, '.*'); - pat = pat.replace(/\?/g, '.'); - if (extended) { - pat = pat.replace(/\\\[(!|)(.*)\\]/g, function(match, p1, p2, offset, string) { - const first = p1 && '^' || ''; - const second = p2.replace(/\\-/, '-'); - return '[' + first + second + ']'; - }); - } - return pat; + const replacements: ([RegExp, string | ((substring: string, ...args: any[]) => string) ])[] = [ + [/\\\*/g, '.*'], + [/\?/g, '.'], + extended !== false && [ + /\\\[(!|)(.*)\\]/g, + (_match: string, neg: string, pat: string) => [ + '[', + neg ? '^' : '', + pat.replace(/\\-/, '-'), + ']', + ].join(''), + ], + ]; + return replacements.reduce( + // https://github.com/microsoft/TypeScript/issues/30134 + (pat, args) => args ? pat.replace(args[0], args[1] as any) : pat, + escapeRegExp(glob), + ); } export function ensureNoTrailingSlash(url: string): string { diff --git a/src/webrtc/call.ts b/src/webrtc/call.ts index ebc7464f06d..b772d5d9701 100644 --- a/src/webrtc/call.ts +++ b/src/webrtc/call.ts @@ -1865,10 +1865,10 @@ export class MatrixCall extends TypedEventEmitter Date: Tue, 16 Aug 2022 15:33:19 +0200 Subject: [PATCH 15/28] Fix: Handle parsing of a beacon info event without asset (#2591) * test case * handle missing beacon info asset * default beacon info asset type to self * make BeaconLocationState.assetType optional --- spec/unit/models/beacon.spec.ts | 19 +++++++++++++++++++ src/content-helpers.ts | 6 +++--- src/models/beacon.ts | 2 +- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/spec/unit/models/beacon.spec.ts b/spec/unit/models/beacon.spec.ts index 73b6bc552d2..c0de3591b5b 100644 --- a/spec/unit/models/beacon.spec.ts +++ b/spec/unit/models/beacon.spec.ts @@ -15,6 +15,7 @@ limitations under the License. */ import { MatrixEvent } from "../../../src"; +import { M_BEACON_INFO } from "../../../src/@types/beacon"; import { isTimestampInDuration, Beacon, @@ -129,6 +130,24 @@ describe('Beacon', () => { expect(beacon.beaconInfo).toBeTruthy(); }); + it('creates beacon without error from a malformed event', () => { + const event = new MatrixEvent({ + type: M_BEACON_INFO.name, + room_id: roomId, + state_key: userId, + content: {}, + }); + const beacon = new Beacon(event); + + expect(beacon.beaconInfoId).toEqual(event.getId()); + expect(beacon.roomId).toEqual(roomId); + expect(beacon.isLive).toEqual(false); + expect(beacon.beaconInfoOwner).toEqual(userId); + expect(beacon.beaconInfoEventType).toEqual(liveBeaconEvent.getType()); + expect(beacon.identifier).toEqual(`${roomId}_${userId}`); + expect(beacon.beaconInfo).toBeTruthy(); + }); + describe('isLive()', () => { it('returns false when beacon is explicitly set to not live', () => { const beacon = new Beacon(notLiveBeaconEvent); diff --git a/src/content-helpers.ts b/src/content-helpers.ts index 8d417d0a912..82404ea6e30 100644 --- a/src/content-helpers.ts +++ b/src/content-helpers.ts @@ -247,7 +247,7 @@ export const makeBeaconInfoContent: MakeBeaconInfoContent = ( }); export type BeaconInfoState = MBeaconInfoContent & { - assetType: LocationAssetType; + assetType?: LocationAssetType; timestamp: number; }; /** @@ -255,14 +255,14 @@ export type BeaconInfoState = MBeaconInfoContent & { */ export const parseBeaconInfoContent = (content: MBeaconInfoEventContent): BeaconInfoState => { const { description, timeout, live } = content; - const { type: assetType } = M_ASSET.findIn(content); const timestamp = M_TIMESTAMP.findIn(content); + const asset = M_ASSET.findIn(content); return { description, timeout, live, - assetType, + assetType: asset?.type, timestamp, }; }; diff --git a/src/models/beacon.ts b/src/models/beacon.ts index 9df62bbe2b1..25abf2842ef 100644 --- a/src/models/beacon.ts +++ b/src/models/beacon.ts @@ -197,7 +197,7 @@ export class Beacon extends TypedEventEmitter Date.now() ? this._beaconInfo?.timestamp - 360000 /* 6min */ : this._beaconInfo?.timestamp; - this._isLive = this._beaconInfo?.live && + this._isLive = !!this._beaconInfo?.live && isTimestampInDuration(startTimestamp, this._beaconInfo?.timeout, Date.now()); if (prevLiveness !== this.isLive) { From 566b4ba56c587578fcf4f32af003528291c77365 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 16 Aug 2022 15:25:58 +0100 Subject: [PATCH 16/28] Resetting package fields for development --- package.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 9c243abc6b4..f8c73ea3549 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "keywords": [ "matrix-org" ], - "main": "./lib/index.js", + "main": "./src/index.ts", "browser": "./lib/browser-index.js", "matrix_src_main": "./src/index.ts", "matrix_src_browser": "./src/browser-index.js", @@ -125,6 +125,5 @@ "jestSonar": { "reportPath": "coverage", "sonar56x": true - }, - "typings": "./lib/index.d.ts" + } } From 3ae974e23e41e6175974791d2c2737ba479a7a2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 17 Aug 2022 12:23:06 +0200 Subject: [PATCH 17/28] Remove duplicate log of answering a call (#2595) --- src/webrtc/call.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/webrtc/call.ts b/src/webrtc/call.ts index b772d5d9701..90e20cc37ce 100644 --- a/src/webrtc/call.ts +++ b/src/webrtc/call.ts @@ -866,8 +866,6 @@ export class MatrixCall extends TypedEventEmitter Date: Mon, 22 Aug 2022 09:30:03 +0100 Subject: [PATCH 18/28] Lock file maintenance (#2608) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 376 +++++++++++++++++++++--------------------------------- 1 file changed, 142 insertions(+), 234 deletions(-) diff --git a/yarn.lock b/yarn.lock index f841a40b6a3..d5c8ddfa23d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3,11 +3,12 @@ "@actions/core@^1.4.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.9.0.tgz#20c1baac5d4bd2508ba1fc3e5f3fc4b8a80d4082" - integrity sha512-5pbM693Ih59ZdUhgk+fts+bUWTnIdHV3kwOSr+QIoFHMLg7Gzhwm0cifDY/AG68ekEJAkHnQVpcy4f6GjmzBCA== + version "1.9.1" + resolved "https://registry.yarnpkg.com/@actions/core/-/core-1.9.1.tgz#97c0201b1f9856df4f7c3a375cdcdb0c2a2f750b" + integrity sha512-5ad+U2YGrmmiw6du20AQW5XuWo7UKN2052FjSV7MX+Wfjf8sCqcsZe62NfgHys4QI4/Y+vQvLKYL8jWtA1ZBTA== dependencies: "@actions/http-client" "^2.0.1" + uuid "^8.3.2" "@actions/github@^5.0.0": version "5.0.3" @@ -99,16 +100,7 @@ dependencies: eslint-rule-composer "^0.3.0" -"@babel/generator@^7.12.11", "@babel/generator@^7.7.2": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.9.tgz#68337e9ea8044d6ddc690fb29acae39359cca0a5" - integrity sha512-wt5Naw6lJrL1/SGkipMiFxJjtyczUWTP38deiP1PO60HsBjDeKk08CGC3S8iVuvf0FmTdgKwU1KIXzSKL1G0Ug== - dependencies: - "@babel/types" "^7.18.9" - "@jridgewell/gen-mapping" "^0.3.2" - jsesc "^2.5.1" - -"@babel/generator@^7.18.10", "@babel/generator@^7.18.9": +"@babel/generator@^7.12.11", "@babel/generator@^7.18.10", "@babel/generator@^7.7.2": version "7.18.12" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.12.tgz#fa58daa303757bd6f5e4bbca91b342040463d9f4" integrity sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg== @@ -142,7 +134,7 @@ browserslist "^4.20.2" semver "^6.3.0" -"@babel/helper-create-class-features-plugin@^7.18.6": +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.18.9.tgz#d802ee16a64a9e824fcbf0a2ffc92f19d58550ce" integrity sha512-WvypNAYaVh23QcjpMR24CwZY2Nz6hqdOcFdPbNpV56hL5H6KiFheO7Xm1aPdlLQ7d5emYZX7VZwPp9x3z+2opw== @@ -327,12 +319,7 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.2.3", "@babel/parser@^7.9.4": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.9.tgz#f2dde0c682ccc264a9a8595efd030a5cc8fd2539" - integrity sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg== - -"@babel/parser@^7.18.10", "@babel/parser@^7.18.11", "@babel/parser@^7.18.6", "@babel/parser@^7.18.9": +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.18.11", "@babel/parser@^7.2.3", "@babel/parser@^7.9.4": version "7.18.11" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.11.tgz#68bb07ab3d380affa9a3f96728df07969645d2d9" integrity sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ== @@ -858,12 +845,12 @@ "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-transform-typescript@^7.18.6": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.8.tgz#303feb7a920e650f2213ef37b36bbf327e6fa5a0" - integrity sha512-p2xM8HI83UObjsZGofMV/EdYjamsDm6MoN3hXPYIT0+gxIoopE+B7rPYKAxfrz9K9PK7JafTTjqYC6qipLExYA== + version "7.18.12" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.18.12.tgz#712e9a71b9e00fde9f8c0238e0cceee86ab2f8fd" + integrity sha512-2vjjam0cum0miPkenUbQswKowuxs/NjMwIKEq0zwegRxXk12C9YOF9STXnaUptITOtOJHKHpzvvWYOjbm6tc0w== dependencies: - "@babel/helper-create-class-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" + "@babel/helper-create-class-features-plugin" "^7.18.9" + "@babel/helper-plugin-utils" "^7.18.9" "@babel/plugin-syntax-typescript" "^7.18.6" "@babel/plugin-transform-unicode-escapes@^7.18.10": @@ -1000,7 +987,7 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/template@^7.18.10", "@babel/template@^7.18.6": +"@babel/template@^7.18.10", "@babel/template@^7.18.6", "@babel/template@^7.3.3": version "7.18.10" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== @@ -1009,32 +996,7 @@ "@babel/parser" "^7.18.10" "@babel/types" "^7.18.10" -"@babel/template@^7.3.3": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.6.tgz#1283f4993e00b929d6e2d3c72fdc9168a2977a31" - integrity sha512-JoDWzPe+wgBsTTgdnIma3iHNFC7YVJoPssVBDjiHfNlyt4YcunDtcDOUmfVDfCK5MfdsaIoX9PkijPhjH3nYUw== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.18.6" - "@babel/types" "^7.18.6" - -"@babel/traverse@^7.1.6", "@babel/traverse@^7.7.2": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.9.tgz#deeff3e8f1bad9786874cb2feda7a2d77a904f98" - integrity sha512-LcPAnujXGwBgv3/WHv01pHtb2tihcyW1XuL9wd7jqh1Z8AQkTd+QVjMrMijrln0T7ED3UXLIy36P9Ao7W75rYg== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.18.9" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.18.9" - "@babel/types" "^7.18.9" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/traverse@^7.18.10", "@babel/traverse@^7.18.11", "@babel/traverse@^7.18.9": +"@babel/traverse@^7.1.6", "@babel/traverse@^7.18.10", "@babel/traverse@^7.18.11", "@babel/traverse@^7.18.9", "@babel/traverse@^7.7.2": version "7.18.11" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.11.tgz#3d51f2afbd83ecf9912bcbb5c4d94e3d2ddaa16f" integrity sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ== @@ -1050,15 +1012,7 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.9.tgz#7148d64ba133d8d73a41b3172ac4b83a1452205f" - integrity sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg== - dependencies: - "@babel/helper-validator-identifier" "^7.18.6" - to-fast-properties "^2.0.0" - -"@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.4.4": +"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.2.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4": version "7.18.10" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.10.tgz#4908e81b6b339ca7c6b7a555a5fc29446f26dde6" integrity sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ== @@ -1351,15 +1305,16 @@ integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.13", "@jridgewell/trace-mapping@^0.3.8", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.14" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.14.tgz#b231a081d8f66796e475ad588a1ef473112701ed" - integrity sha512-bJWEfQ9lPTvm3SneWwRFVLzrh6nhjwqw7TUFFBEMzwvg7t7PCDenf2lDwqo4NQXzdpgBXyFgDWnQA+2vkruksQ== + version "0.3.15" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774" + integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g== dependencies: "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" "@matrix-org/olm@https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.12.tgz": version "3.2.12" + uid "0bce3c86f9d36a4984d3c3e07df1c3fb4c679bd9" resolved "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.12.tgz#0bce3c86f9d36a4984d3c3e07df1c3fb4c679bd9" "@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3": @@ -1426,10 +1381,10 @@ "@octokit/types" "^6.0.3" universal-user-agent "^6.0.0" -"@octokit/openapi-types@^12.10.0": - version "12.10.1" - resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-12.10.1.tgz#57b5cc6c7b4e55d8642c93d06401fb1af4839899" - integrity sha512-P+SukKanjFY0ZhsK6wSVnQmxTP2eVPPE8OPSNuxaMYtgVzwJZgfGdwlYjf4RlRU4vLEw4ts2fsE2icG4nZ5ddQ== +"@octokit/openapi-types@^12.11.0": + version "12.11.0" + resolved "https://registry.yarnpkg.com/@octokit/openapi-types/-/openapi-types-12.11.0.tgz#da5638d64f2b919bca89ce6602d059f1b52d3ef0" + integrity sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ== "@octokit/plugin-paginate-rest@^2.16.8", "@octokit/plugin-paginate-rest@^2.17.0": version "2.21.3" @@ -1483,16 +1438,16 @@ "@octokit/plugin-rest-endpoint-methods" "^5.12.0" "@octokit/types@^6.0.3", "@octokit/types@^6.16.1", "@octokit/types@^6.39.0", "@octokit/types@^6.40.0": - version "6.40.0" - resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.40.0.tgz#f2e665196d419e19bb4265603cf904a820505d0e" - integrity sha512-MFZOU5r8SwgJWDMhrLUSvyJPtVsqA6VnbVI3TNbsmw+Jnvrktzvq2fYES/6RiJA/5Ykdwq4mJmtlYUfW7CGjmw== + version "6.41.0" + resolved "https://registry.yarnpkg.com/@octokit/types/-/types-6.41.0.tgz#e58ef78d78596d2fb7df9c6259802464b5f84a04" + integrity sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg== dependencies: - "@octokit/openapi-types" "^12.10.0" + "@octokit/openapi-types" "^12.11.0" "@sinclair/typebox@^0.24.1": - version "0.24.26" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.26.tgz#84f9e8c1d93154e734a7947609a1dc7c7a81cc22" - integrity sha512-1ZVIyyS1NXDRVT8GjWD5jULjhDyM3IsIHef2VGUMdnWOlX2tkPjyEX/7K0TGSH2S8EaPhp1ylFdjSjUGQ+gecg== + version "0.24.28" + resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.28.tgz#15aa0b416f82c268b1573ab653e4413c965fe794" + integrity sha512-dgJd3HLOkLmz4Bw50eZx/zJwtBq65nms3N9VBYu5LTjJ883oBFkTyXRlCB/ZGGwqYpJJHA5zW2Ibhl5ngITfow== "@sinonjs/commons@^1.7.0": version "1.8.3" @@ -1540,9 +1495,9 @@ "@babel/types" "^7.0.0" "@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": - version "7.17.1" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.17.1.tgz#1a0e73e8c28c7e832656db372b779bfd2ef37314" - integrity sha512-kVzjari1s2YVi77D3w1yuvohV2idweYXMCDzqBiVNN63TcDWrIlTVOYpqVrvbbyOE/IyzBoTKF0fdnLPEORFxA== + version "7.18.0" + resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.0.tgz#8134fd78cb39567465be65b9fdc16d378095f41f" + integrity sha512-v4Vwdko+pgymgS+A2UIaJru93zQd85vIGWObM5ekZNdXCKtDYqATlEYnWgfo86Q6I1Lh0oXnksDnMU1cwmlPDw== dependencies: "@babel/types" "^7.3.0" @@ -1597,11 +1552,11 @@ "@types/istanbul-lib-report" "*" "@types/jest@^28.0.0": - version "28.1.6" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-28.1.6.tgz#d6a9cdd38967d2d746861fb5be6b120e38284dd4" - integrity sha512-0RbGAFMfcBJKOmqRazM8L98uokwuwD5F8rHrv/ZMbrZBwVOWZUyPG6VFNscjYr/vjM3Vu4fRrCPbOs42AfemaQ== + version "28.1.7" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-28.1.7.tgz#a680c5d05b69634c2d54a63cb106d7fb1adaba16" + integrity sha512-acDN4VHD40V24tgu0iC44jchXavRNVFXQ/E6Z5XNsswgoSO/4NgsXoEYmPUGookKldlZQyIpmrEXsHI9cA3ZTA== dependencies: - jest-matcher-utils "^28.0.0" + expect "^28.0.0" pretty-format "^28.0.0" "@types/json-schema@^7.0.9": @@ -1633,19 +1588,19 @@ integrity sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA== "@types/node@*": - version "18.6.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.6.3.tgz#4e4a95b6fe44014563ceb514b2598b3e623d1c98" - integrity sha512-6qKpDtoaYLM+5+AFChLhHermMQxc3TOEFIDzrZLPRGHPrLEwqFkkT5Kx3ju05g6X7uDPazz3jHbKPX0KzCjntg== + version "18.7.6" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.6.tgz#31743bc5772b6ac223845e18c3fc26f042713c83" + integrity sha512-EdxgKRXgYsNITy5mjjXjVE/CS8YENSdhiagGrLqjG0pvA2owgJ6i4l7wy/PFZGC0B1/H20lWKN7ONVDNYDZm7A== "@types/node@16": - version "16.11.45" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.45.tgz#155b13a33c665ef2b136f7f245fa525da419e810" - integrity sha512-3rKg/L5x0rofKuuUt5zlXzOnKyIHXmIu5R8A0TuNDMF2062/AOIDBciFIjToLEJ/9F9DzkHNot+BpNsMI1OLdQ== + version "16.11.49" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.49.tgz#560b1ea774b61e19a89c3fc72d2dcaa3863f38b2" + integrity sha512-Abq9fBviLV93OiXMu+f6r0elxCzRwc0RC5f99cU892uBITL44pTvgvEqlRlPRi8EGcO1z7Cp8A4d0s/p3J/+Nw== "@types/prettier@^2.1.5": - version "2.6.4" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.4.tgz#ad899dad022bab6b5a9f0a0fe67c2f7a4a8950ed" - integrity sha512-fOwvpvQYStpb/zHMx0Cauwywu9yLDmzWiiQBC7gJyq5tYLUXFZvDG7VK1B7WBxxjBJNKFOZ0zLoOQn8vmATbhw== + version "2.7.0" + resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.0.tgz#ea03e9f0376a4446f44797ca19d9c46c36e352dc" + integrity sha512-RI1L7N4JnW5gQw2spvL7Sllfuf1SaHdrZpCHiBlCXjIlufi1SMNnbu2teze3/QE67Fg2tBlH7W+mi4hVNk4p0A== "@types/request@^2.48.5": version "2.48.8" @@ -1678,20 +1633,20 @@ integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== "@types/yargs@^17.0.8": - version "17.0.10" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.10.tgz#591522fce85d8739bca7b8bb90d048e4478d186a" - integrity sha512-gmEaFwpj/7f/ROdtIlci1R1VYU1J4j95m8T+Tj3iBgiBFKg1foE/PSl93bBd5T9LDXNPo8UlNN6W0qwD8O5OaA== + version "17.0.11" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.11.tgz#5e10ca33e219807c0eee0f08b5efcba9b6a42c06" + integrity sha512-aB4y9UDUXTSMxmM4MH+YnuR0g5Cph3FLQBoWoMB21DSvFVAxRVEHEMx3TLh+zUZYMCQtKiqazz0Q4Rre31f/OA== dependencies: "@types/yargs-parser" "*" "@typescript-eslint/eslint-plugin@^5.6.0": - version "5.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.33.0.tgz#059798888720ec52ffa96c5f868e31a8f70fa3ec" - integrity sha512-jHvZNSW2WZ31OPJ3enhLrEKvAZNyAFWZ6rx9tUwaessTc4sx9KmgMNhVcqVAl1ETnT5rU5fpXTLmY9YvC1DCNg== + version "5.33.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.33.1.tgz#c0a480d05211660221eda963cc844732fe9b1714" + integrity sha512-S1iZIxrTvKkU3+m63YUOxYPKaP+yWDQrdhxTglVDVEVBf+aCSw85+BmJnyUaQQsk5TXFG/LpBu9fa+LrAQ91fQ== dependencies: - "@typescript-eslint/scope-manager" "5.33.0" - "@typescript-eslint/type-utils" "5.33.0" - "@typescript-eslint/utils" "5.33.0" + "@typescript-eslint/scope-manager" "5.33.1" + "@typescript-eslint/type-utils" "5.33.1" + "@typescript-eslint/utils" "5.33.1" debug "^4.3.4" functional-red-black-tree "^1.0.1" ignore "^5.2.0" @@ -1700,68 +1655,68 @@ tsutils "^3.21.0" "@typescript-eslint/parser@^5.6.0": - version "5.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.33.0.tgz#26ec3235b74f0667414613727cb98f9b69dc5383" - integrity sha512-cgM5cJrWmrDV2KpvlcSkelTBASAs1mgqq+IUGKJvFxWrapHpaRy5EXPQz9YaKF3nZ8KY18ILTiVpUtbIac86/w== + version "5.33.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.33.1.tgz#e4b253105b4d2a4362cfaa4e184e2d226c440ff3" + integrity sha512-IgLLtW7FOzoDlmaMoXdxG8HOCByTBXrB1V2ZQYSEV1ggMmJfAkMWTwUjjzagS6OkfpySyhKFkBw7A9jYmcHpZA== dependencies: - "@typescript-eslint/scope-manager" "5.33.0" - "@typescript-eslint/types" "5.33.0" - "@typescript-eslint/typescript-estree" "5.33.0" + "@typescript-eslint/scope-manager" "5.33.1" + "@typescript-eslint/types" "5.33.1" + "@typescript-eslint/typescript-estree" "5.33.1" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.33.0": - version "5.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.33.0.tgz#509d7fa540a2c58f66bdcfcf278a3fa79002e18d" - integrity sha512-/Jta8yMNpXYpRDl8EwF/M8It2A9sFJTubDo0ATZefGXmOqlaBffEw0ZbkbQ7TNDK6q55NPHFshGBPAZvZkE8Pw== +"@typescript-eslint/scope-manager@5.33.1": + version "5.33.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.33.1.tgz#8d31553e1b874210018ca069b3d192c6d23bc493" + integrity sha512-8ibcZSqy4c5m69QpzJn8XQq9NnqAToC8OdH/W6IXPXv83vRyEDPYLdjAlUx8h/rbusq6MkW4YdQzURGOqsn3CA== dependencies: - "@typescript-eslint/types" "5.33.0" - "@typescript-eslint/visitor-keys" "5.33.0" + "@typescript-eslint/types" "5.33.1" + "@typescript-eslint/visitor-keys" "5.33.1" -"@typescript-eslint/type-utils@5.33.0": - version "5.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.33.0.tgz#92ad1fba973c078d23767ce2d8d5a601baaa9338" - integrity sha512-2zB8uEn7hEH2pBeyk3NpzX1p3lF9dKrEbnXq1F7YkpZ6hlyqb2yZujqgRGqXgRBTHWIUG3NGx/WeZk224UKlIA== +"@typescript-eslint/type-utils@5.33.1": + version "5.33.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.33.1.tgz#1a14e94650a0ae39f6e3b77478baff002cec4367" + integrity sha512-X3pGsJsD8OiqhNa5fim41YtlnyiWMF/eKsEZGsHID2HcDqeSC5yr/uLOeph8rNF2/utwuI0IQoAK3fpoxcLl2g== dependencies: - "@typescript-eslint/utils" "5.33.0" + "@typescript-eslint/utils" "5.33.1" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.33.0": - version "5.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.33.0.tgz#d41c584831805554b063791338b0220b613a275b" - integrity sha512-nIMt96JngB4MYFYXpZ/3ZNU4GWPNdBbcB5w2rDOCpXOVUkhtNlG2mmm8uXhubhidRZdwMaMBap7Uk8SZMU/ppw== +"@typescript-eslint/types@5.33.1": + version "5.33.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.33.1.tgz#3faef41793d527a519e19ab2747c12d6f3741ff7" + integrity sha512-7K6MoQPQh6WVEkMrMW5QOA5FO+BOwzHSNd0j3+BlBwd6vtzfZceJ8xJ7Um2XDi/O3umS8/qDX6jdy2i7CijkwQ== -"@typescript-eslint/typescript-estree@5.33.0": - version "5.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.33.0.tgz#02d9c9ade6f4897c09e3508c27de53ad6bfa54cf" - integrity sha512-tqq3MRLlggkJKJUrzM6wltk8NckKyyorCSGMq4eVkyL5sDYzJJcMgZATqmF8fLdsWrW7OjjIZ1m9v81vKcaqwQ== +"@typescript-eslint/typescript-estree@5.33.1": + version "5.33.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.33.1.tgz#a573bd360790afdcba80844e962d8b2031984f34" + integrity sha512-JOAzJ4pJ+tHzA2pgsWQi4804XisPHOtbvwUyqsuuq8+y5B5GMZs7lI1xDWs6V2d7gE/Ez5bTGojSK12+IIPtXA== dependencies: - "@typescript-eslint/types" "5.33.0" - "@typescript-eslint/visitor-keys" "5.33.0" + "@typescript-eslint/types" "5.33.1" + "@typescript-eslint/visitor-keys" "5.33.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.33.0": - version "5.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.33.0.tgz#46797461ce3146e21c095d79518cc0f8ec574038" - integrity sha512-JxOAnXt9oZjXLIiXb5ZIcZXiwVHCkqZgof0O8KPgz7C7y0HS42gi75PdPlqh1Tf109M0fyUw45Ao6JLo7S5AHw== +"@typescript-eslint/utils@5.33.1": + version "5.33.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.33.1.tgz#171725f924fe1fe82bb776522bb85bc034e88575" + integrity sha512-uphZjkMaZ4fE8CR4dU7BquOV6u0doeQAr8n6cQenl/poMaIyJtBu8eys5uk6u5HiDH01Mj5lzbJ5SfeDz7oqMQ== dependencies: "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.33.0" - "@typescript-eslint/types" "5.33.0" - "@typescript-eslint/typescript-estree" "5.33.0" + "@typescript-eslint/scope-manager" "5.33.1" + "@typescript-eslint/types" "5.33.1" + "@typescript-eslint/typescript-estree" "5.33.1" eslint-scope "^5.1.1" eslint-utils "^3.0.0" -"@typescript-eslint/visitor-keys@5.33.0": - version "5.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.33.0.tgz#fbcbb074e460c11046e067bc3384b5d66b555484" - integrity sha512-/XsqCzD4t+Y9p5wd9HZiptuGKBlaZO5showwqODii5C0nZawxWLF+Q6k5wYHBrQv96h6GYKyqqMHCSTqta8Kiw== +"@typescript-eslint/visitor-keys@5.33.1": + version "5.33.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.33.1.tgz#0155c7571c8cd08956580b880aea327d5c34a18b" + integrity sha512-nwIxOK8Z2MPWltLKMLOEZwmfBZReqUdbEoHQXeCpa+sRVARe5twpJGHCB4dk9903Yaf0nMAlGbQfaAH92F60eg== dependencies: - "@typescript-eslint/types" "5.33.0" + "@typescript-eslint/types" "5.33.1" eslint-visitor-keys "^3.3.0" JSONStream@^1.0.3: @@ -1773,9 +1728,9 @@ JSONStream@^1.0.3: through ">=2.2.7 <3" ace-builds@^1.4.13: - version "1.8.1" - resolved "https://registry.yarnpkg.com/ace-builds/-/ace-builds-1.8.1.tgz#5d318fa13d7e6ea947f8a50e42c570c573b29529" - integrity sha512-wjEQ4khMQYg9FfdEDoOtqdoHwcwFL48H0VB3te5b5A3eqHwxsTw8IX6+xzfisgborIb8dYU+1y9tcmtGFrCPIg== + version "1.9.6" + resolved "https://registry.yarnpkg.com/ace-builds/-/ace-builds-1.9.6.tgz#2d3721f90f0664b79be9288f6319dd57576ff1e7" + integrity sha512-M/Li4hPruMSbkkg35LgdbsIBq0WuwrV4ztP2pKaww47rC/MvDc1bOrYxwJrfgxdlzyLKrja5bn+9KwwuzqB2xQ== acorn-globals@^3.0.0: version "3.1.0" @@ -1818,7 +1773,7 @@ acorn@^7.0.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.5.0, acorn@^8.7.1: +acorn@^8.5.0, acorn@^8.8.0: version "8.8.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== @@ -2465,9 +2420,9 @@ camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001370: - version "1.0.30001374" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001374.tgz#3dab138e3f5485ba2e74bd13eca7fe1037ce6f57" - integrity sha512-mWvzatRx3w+j5wx/mpFN5v5twlPrabG8NqX2c6e45LCpymdoGqNvRkRutFUqpRTXKFQFNQJasvK0YT7suW6/Hw== + version "1.0.30001378" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001378.tgz#3d2159bf5a8f9ca093275b0d3ecc717b00f27b67" + integrity sha512-JVQnfoO7FK7WvU4ZkBRbPjaot4+YqxogSDosHv0Hv5mWpUESmN+UubMU6L/hGz8QlQ2aY5U0vR6MOs6j/CXpNA== caseless@~0.12.0: version "0.12.0" @@ -2993,9 +2948,9 @@ ecc-jsbn@~0.1.1: safer-buffer "^2.1.0" electron-to-chromium@^1.4.202: - version "1.4.212" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.212.tgz#20cd48e88288fd2428138c108804edb1961bf559" - integrity sha512-LjQUg1SpLj2GfyaPDVBUHdhmlDU1vDB4f0mJWSGkISoXQrn5/lH3ECPCuo2Bkvf6Y30wO+b69te+rZK/llZmjg== + version "1.4.225" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.225.tgz#3e27bdd157cbaf19768141f2e0f0f45071e52338" + integrity sha512-ICHvGaCIQR3P88uK8aRtx8gmejbVJyC6bB4LEC3anzBrIzdzC7aiZHY4iFfXhN4st6I7lMO0x4sgBHf/7kBvRw== elliptic@^6.5.3: version "6.5.4" @@ -3078,9 +3033,9 @@ es-to-primitive@^1.2.1: is-symbol "^1.0.2" es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50, es5-ext@^0.10.53, es5-ext@^0.10.61, es5-ext@~0.10.14, es5-ext@~0.10.2, es5-ext@~0.10.46: - version "0.10.61" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.61.tgz#311de37949ef86b6b0dcea894d1ffedb909d3269" - integrity sha512-yFhIqQAzu2Ca2I4SE2Au3rxVfmohU9Y7wqGR+s7+H7krk26NXhIRAZDgqd6xqjCEFUomDEA3/Bo/7fKmIkW1kA== + version "0.10.62" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5" + integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA== dependencies: es6-iterator "^2.0.3" es6-symbol "^3.1.3" @@ -3147,12 +3102,11 @@ eslint-import-resolver-node@^0.3.6: resolve "^1.20.0" eslint-module-utils@^2.7.3: - version "2.7.3" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee" - integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== + version "2.7.4" + resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974" + integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== dependencies: debug "^3.2.7" - find-up "^2.1.0" eslint-plugin-import@^2.25.4: version "2.26.0" @@ -3258,11 +3212,11 @@ eslint@8.20.0: v8-compile-cache "^2.0.3" espree@^9.3.2: - version "9.3.2" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.2.tgz#f58f77bd334731182801ced3380a8cc859091596" - integrity sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA== + version "9.3.3" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.3.tgz#2dd37c4162bb05f433ad3c1a52ddf8a49dc08e9d" + integrity sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng== dependencies: - acorn "^8.7.1" + acorn "^8.8.0" acorn-jsx "^5.3.2" eslint-visitor-keys "^3.3.0" @@ -3360,7 +3314,7 @@ exorcist@^2.0.0: mkdirp "^1.0.4" mold-source-map "^0.4.0" -expect@^28.1.0, expect@^28.1.3: +expect@^28.0.0, expect@^28.1.0, expect@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/expect/-/expect-28.1.3.tgz#90a7c1a124f1824133dd4533cce2d2bdcb6603ec" integrity sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g== @@ -3468,13 +3422,6 @@ find-cache-dir@^2.0.0: make-dir "^2.0.0" pkg-dir "^3.0.0" -find-up@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - integrity sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ== - dependencies: - locate-path "^2.0.0" - find-up@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" @@ -3728,7 +3675,7 @@ has-property-descriptors@^1.0.0: dependencies: get-intrinsic "^1.1.1" -has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3: +has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== @@ -3938,14 +3885,7 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.4: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== -is-core-module@^2.8.1: - version "2.9.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" - integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== - dependencies: - has "^1.0.3" - -is-core-module@^2.9.0: +is-core-module@^2.8.1, is-core-module@^2.9.0: version "2.10.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed" integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg== @@ -4306,11 +4246,11 @@ jest-leak-detector@^28.1.3: pretty-format "^28.1.3" jest-localstorage-mock@^2.4.6: - version "2.4.21" - resolved "https://registry.yarnpkg.com/jest-localstorage-mock/-/jest-localstorage-mock-2.4.21.tgz#920aa6fc8f8ab2f81e40433e48e2efdb2d81a6e0" - integrity sha512-IBXPBufnfPyr4VkoQeJ+zlfWlG84P0KbL4ejcV9j3xNI0v6OWznQlH6Ke9xjSarleR11090oSeWADSUow0PmFw== + version "2.4.22" + resolved "https://registry.yarnpkg.com/jest-localstorage-mock/-/jest-localstorage-mock-2.4.22.tgz#9d70be92bfc591c0be289ee2f71de1b4b2a5ca9b" + integrity sha512-60PWSDFQOS5v7JzSmYLM3dPLg0JLl+2Vc4lIEz/rj2yrXJzegsFLn7anwc5IL0WzJbBa/Las064CHbFg491/DQ== -jest-matcher-utils@^28.0.0, jest-matcher-utils@^28.1.3: +jest-matcher-utils@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz#5a77f1c129dd5ba3b4d7fc20728806c78893146e" integrity sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw== @@ -4714,14 +4654,6 @@ linkify-it@^3.0.1: dependencies: uc.micro "^1.0.1" -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - integrity sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA== - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - locate-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" @@ -5091,26 +5023,16 @@ object-keys@^1.1.1: resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== -object.assign@^4.1.0: - version "4.1.3" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.3.tgz#d36b7700ddf0019abb6b1df1bb13f6445f79051f" - integrity sha512-ZFJnX3zltyjcYJL0RoCJuzb+11zWGyaDbjgxZbdV7rFEcHQuYxrZqhow67aA7xpes6LhojyFDaBKAFfogQrikA== +object.assign@^4.1.0, object.assign@^4.1.2: + version "4.1.4" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.4.tgz#9673c7c7c351ab8c4d0b516f4343ebf4dfb7799f" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== dependencies: call-bind "^1.0.2" define-properties "^1.1.4" has-symbols "^1.0.3" object-keys "^1.1.1" -object.assign@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" - integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - has-symbols "^1.0.1" - object-keys "^1.1.1" - object.values@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" @@ -5151,13 +5073,6 @@ os-browserify@~0.3.0: resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" integrity sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A== -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" - p-limit@^2.0.0, p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -5172,13 +5087,6 @@ p-limit@^3.0.2, p-limit@^3.1.0: dependencies: yocto-queue "^0.1.0" -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - integrity sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg== - dependencies: - p-limit "^1.1.0" - p-locate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" @@ -5208,11 +5116,6 @@ p-retry@4: "@types/retry" "0.12.0" retry "^0.13.1" -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - integrity sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww== - p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" @@ -6431,9 +6334,9 @@ type@^1.0.1: integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== type@^2.5.0: - version "2.6.0" - resolved "https://registry.yarnpkg.com/type/-/type-2.6.0.tgz#3ca6099af5981d36ca86b78442973694278a219f" - integrity sha512-eiDBDOmkih5pMbo9OqsqPRGMljLodLcwd5XD5JbtNB0o89xZAwynY9EdCDsJU7LtcVCClu9DvM7/0Ep1hYX3EQ== + version "2.7.2" + resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" + integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== typedarray@^0.0.6: version "0.0.6" @@ -6600,6 +6503,11 @@ uuid@^3.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== +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== + v8-compile-cache@^2.0.3: version "2.3.0" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" @@ -6650,9 +6558,9 @@ vue-docgen-api@^3.26.0: vue-template-compiler "^2.0.0" vue-template-compiler@^2.0.0: - version "2.7.8" - resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.7.8.tgz#eadd54ed8fbff55b7deb07093a976c07f451a1dc" - integrity sha512-eQqdcUpJKJpBRPDdxCNsqUoT0edNvdt1jFjtVnVS/LPPmr0BU2jWzXlrf6BVMeODtdLewB3j8j3WjNiB+V+giw== + version "2.7.9" + resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.7.9.tgz#ffbeb1769ae6af65cd405a6513df6b1e20e33616" + integrity sha512-NPJxt6OjVlzmkixYg0SdIN2Lw/rMryQskObY89uAMcM9flS/HrmLK5LaN1ReBTuhBgnYuagZZEkSS6FESATQUQ== dependencies: de-indent "^1.0.2" he "^1.2.0" @@ -6771,9 +6679,9 @@ wrappy@1: integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== write-file-atomic@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.1.tgz#9faa33a964c1c85ff6f849b80b42a88c2c537c8f" - integrity sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ== + version "4.0.2" + resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.2.tgz#a9df01ae5b77858a027fd2e80768ee433555fcfd" + integrity sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg== dependencies: imurmurhash "^0.1.4" signal-exit "^3.0.7" @@ -6814,9 +6722,9 @@ yargs-parser@^20.2.2, yargs-parser@^20.2.9: integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== yargs-parser@^21.0.0: - version "21.0.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.1.tgz#0267f286c877a4f0f728fceb6f8a3e4cb95c6e35" - integrity sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg== + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== yargs@^16.2.0: version "16.2.0" From 37f8f736e0366403127bb535ae4d2ddf6a6a4cd5 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 22 Aug 2022 14:37:10 +0100 Subject: [PATCH 19/28] sliding sync: handle lone DELETE and INSERT operations If you leave a room you can get a lone DELETE op. If you join a room you can get a lone INSERT op. Up until now, we've assumed these operations happen at the ends of the list (e.g [0] or [length-1]) which is not guaranteed as it depends on the sort order (e.g sort alphabetically and join a room called 'D'). In this scenario, the indexes would not be tracked correctly. Fixed with integration tests. --- spec/integ/sliding-sync.spec.ts | 147 ++++++++++++++++++++++++++++++++ src/sliding-sync.ts | 104 +++++++++++++++------- 2 files changed, 221 insertions(+), 30 deletions(-) diff --git a/spec/integ/sliding-sync.spec.ts b/spec/integ/sliding-sync.spec.ts index 8c4a7ad1254..4cfe392158c 100644 --- a/spec/integ/sliding-sync.spec.ts +++ b/spec/integ/sliding-sync.spec.ts @@ -558,6 +558,153 @@ describe("SlidingSync", () => { await httpBackend.flushAllExpected(); await responseProcessed; await listPromise; + }); + + // this refers to a set of operations where the end result is no change. + it("should handle net zero operations correctly", async () => { + const indexToRoomId = { + 0: roomB, + 1: roomC, + }; + expect(slidingSync.getListData(0).roomIndexToRoomId).toEqual(indexToRoomId); + httpBackend.when("POST", syncUrl).respond(200, { + pos: "f", + // currently the list is [B,C] so we will insert D then immediately delete it + lists: [{ + count: 500, + ops: [ + { + op: "DELETE", index: 2, + }, + { + op: "INSERT", index: 0, room_id: roomA, + }, + { + op: "DELETE", index: 0, + }, + ], + }, + { + count: 50, + }], + }); + const listPromise = listenUntil(slidingSync, "SlidingSync.List", + (listIndex, joinedCount, roomIndexToRoomId) => { + expect(listIndex).toEqual(0); + expect(joinedCount).toEqual(500); + expect(roomIndexToRoomId).toEqual(indexToRoomId); + return true; + }); + const responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => { + return state === SlidingSyncState.Complete; + }); + await httpBackend.flushAllExpected(); + await responseProcessed; + await listPromise; + }); + + it("should handle deletions correctly", async () => { + expect(slidingSync.getListData(0).roomIndexToRoomId).toEqual({ + 0: roomB, + 1: roomC, + }); + httpBackend.when("POST", syncUrl).respond(200, { + pos: "g", + lists: [{ + count: 499, + ops: [ + { + op: "DELETE", index: 0, + }, + ], + }, + { + count: 50, + }], + }); + const listPromise = listenUntil(slidingSync, "SlidingSync.List", + (listIndex, joinedCount, roomIndexToRoomId) => { + expect(listIndex).toEqual(0); + expect(joinedCount).toEqual(499); + expect(roomIndexToRoomId).toEqual({ + 0: roomC, + }); + return true; + }); + const responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => { + return state === SlidingSyncState.Complete; + }); + await httpBackend.flushAllExpected(); + await responseProcessed; + await listPromise; + }); + + it("should handle insertions correctly", async () => { + expect(slidingSync.getListData(0).roomIndexToRoomId).toEqual({ + 0: roomC, + }); + httpBackend.when("POST", syncUrl).respond(200, { + pos: "h", + lists: [{ + count: 500, + ops: [ + { + op: "INSERT", index: 1, room_id: roomA, + }, + ], + }, + { + count: 50, + }], + }); + let listPromise = listenUntil(slidingSync, "SlidingSync.List", + (listIndex, joinedCount, roomIndexToRoomId) => { + expect(listIndex).toEqual(0); + expect(joinedCount).toEqual(500); + expect(roomIndexToRoomId).toEqual({ + 0: roomC, + 1: roomA, + }); + return true; + }); + let responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => { + return state === SlidingSyncState.Complete; + }); + await httpBackend.flushAllExpected(); + await responseProcessed; + await listPromise; + + httpBackend.when("POST", syncUrl).respond(200, { + pos: "h", + lists: [{ + count: 501, + ops: [ + { + op: "INSERT", index: 1, room_id: roomB, + }, + ], + }, + { + count: 50, + }], + }); + listPromise = listenUntil(slidingSync, "SlidingSync.List", + (listIndex, joinedCount, roomIndexToRoomId) => { + expect(listIndex).toEqual(0); + expect(joinedCount).toEqual(501); + expect(roomIndexToRoomId).toEqual({ + 0: roomC, + 1: roomB, + 2: roomA, + }); + return true; + }); + responseProcessed = listenUntil(slidingSync, "SlidingSync.Lifecycle", (state) => { + return state === SlidingSyncState.Complete; + }); + await httpBackend.flushAllExpected(); + await responseProcessed; + await listPromise; slidingSync.stop(); }); }); diff --git a/src/sliding-sync.ts b/src/sliding-sync.ts index 28026b3a999..08a29051506 100644 --- a/src/sliding-sync.ts +++ b/src/sliding-sync.ts @@ -530,6 +530,65 @@ export class SlidingSync extends TypedEventEmitter low; i--) { + if (this.lists[listIndex].isIndexInRange(i)) { + this.lists[listIndex].roomIndexToRoomId[i] = + this.lists[listIndex].roomIndexToRoomId[ + i - 1 + ]; + } + } + } + + private shiftLeft(listIndex: number, hi: number, low: number) { + // l h + // 0,1,2,3,4 <- before + // 0,1,3,4,4 <- after, low is deleted and hi is duplicated + for (let i = low; i < hi; i++) { + if (this.lists[listIndex].isIndexInRange(i)) { + this.lists[listIndex].roomIndexToRoomId[i] = + this.lists[listIndex].roomIndexToRoomId[ + i + 1 + ]; + } + } + } + + private removeEntry(listIndex: number, index: number) { + // work out the max index + let max = -1; + for (const n in this.lists[listIndex].roomIndexToRoomId) { + if (Number(n) > max) { + max = Number(n); + } + } + if (max < 0 || index > max) { + return; + } + // Everything higher than the gap needs to be shifted left. + this.shiftLeft(listIndex, max, index); + delete this.lists[listIndex].roomIndexToRoomId[max]; + } + + private addEntry(listIndex: number, index: number) { + // work out the max index + let max = -1; + for (const n in this.lists[listIndex].roomIndexToRoomId) { + if (Number(n) > max) { + max = Number(n); + } + } + if (max < 0 || index > max) { + return; + } + // Everything higher than the gap needs to be shifted right, +1 so we don't delete the highest element + this.shiftRight(listIndex, max+1, index); + } + private processListOps(list: ListResponse, listIndex: number): void { let gapIndex = -1; list.ops.forEach((op: Operation) => { @@ -537,6 +596,10 @@ export class SlidingSync extends TypedEventEmitter op.index) { + // we haven't been told where to shift from, so make way for a new room entry. + this.addEntry(listIndex, op.index); + } else if (gapIndex > op.index) { // the gap is further down the list, shift every element to the right // starting at the gap so we can just shift each element in turn: // [A,B,C,_] gapIndex=3, op.index=0 @@ -572,26 +624,13 @@ export class SlidingSync extends TypedEventEmitter op.index; i--) { - if (this.lists[listIndex].isIndexInRange(i)) { - this.lists[listIndex].roomIndexToRoomId[i] = - this.lists[listIndex].roomIndexToRoomId[ - i - 1 - ]; - } - } + this.shiftRight(listIndex, gapIndex, op.index); } else if (gapIndex < op.index) { // the gap is further up the list, shift every element to the left // starting at the gap so we can just shift each element in turn - for (let i = gapIndex; i < op.index; i++) { - if (this.lists[listIndex].isIndexInRange(i)) { - this.lists[listIndex].roomIndexToRoomId[i] = - this.lists[listIndex].roomIndexToRoomId[ - i + 1 - ]; - } - } + this.shiftLeft(listIndex, op.index, gapIndex); } + gapIndex = -1; // forget the gap, we don't need it anymore. } this.lists[listIndex].roomIndexToRoomId[op.index] = op.room_id; break; @@ -631,6 +670,11 @@ export class SlidingSync extends TypedEventEmitter Date: Mon, 22 Aug 2022 14:39:04 +0100 Subject: [PATCH 20/28] Add ability to override built in room name generator for an i18n'able one (#2609) * Add ability to override built in room name generator for an i18n'able one * Add tests --- spec/unit/room.spec.ts | 12 +++++ src/client.ts | 10 +++- src/models/room.ts | 105 +++++++++++++++++++++++++++++++++++------ 3 files changed, 111 insertions(+), 16 deletions(-) diff --git a/spec/unit/room.spec.ts b/spec/unit/room.spec.ts index 309d43f030d..695bc12271f 100644 --- a/spec/unit/room.spec.ts +++ b/spec/unit/room.spec.ts @@ -2545,4 +2545,16 @@ describe("Room", function() { }); }); }); + + describe("roomNameGenerator", () => { + const client = new TestClient(userA).client; + client.roomNameGenerator = jest.fn().mockReturnValue(null); + const room = new Room(roomId, client, userA); + + it("should call fn when recalculating room name", () => { + (client.roomNameGenerator as jest.Mock).mockClear(); + room.recalculate(); + expect(client.roomNameGenerator).toHaveBeenCalled(); + }); + }); }); diff --git a/src/client.ts b/src/client.ts index e1d805d9d88..acda99b7027 100644 --- a/src/client.ts +++ b/src/client.ts @@ -137,7 +137,7 @@ 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, RoomNameState } from "./models/room"; import { IAddThreePidOnlyBody, IBindThreePidBody, @@ -344,6 +344,12 @@ export interface ICreateClientOpts { fallbackICEServerAllowed?: boolean; cryptoCallbacks?: ICryptoCallbacks; + + /** + * Method to generate room names for empty rooms and rooms names based on membership. + * Defaults to a built-in English handler with basic pluralisation. + */ + roomNameGenerator?: (roomId: string, state: RoomNameState) => string | null; } export interface IMatrixClientCreateOpts extends ICreateClientOpts { @@ -918,6 +924,7 @@ export class MatrixClient extends TypedEventEmitter; protected syncedLeftRooms = false; @@ -1041,6 +1048,7 @@ export class MatrixClient extends TypedEventEmitter return this.getType() === RoomType.ElementVideo; } + private roomNameGenerator(state: RoomNameState): string { + if (this.client.roomNameGenerator) { + const name = this.client.roomNameGenerator(this.roomId, state); + if (name !== null) { + return name; + } + } + + switch (state.type) { + case RoomNameType.Actual: + return state.name; + case RoomNameType.Generated: + switch (state.subtype) { + case "Inviting": + return `Inviting ${memberNamesToRoomName(state.names, state.count)}`; + default: + return memberNamesToRoomName(state.names, state.count); + } + case RoomNameType.EmptyRoom: + if (state.oldName) { + return `Empty room (was ${state.oldName})`; + } else { + return "Empty room"; + } + } + } + /** * This is an internal method. Calculates the name of the room from the current * room state. @@ -2928,14 +2955,20 @@ export class Room extends TypedEventEmitter // check for an alias, if any. for now, assume first alias is the // official one. const mRoomName = this.currentState.getStateEvents(EventType.RoomName, ""); - if (mRoomName && mRoomName.getContent() && mRoomName.getContent().name) { - return mRoomName.getContent().name; + if (mRoomName?.getContent().name) { + return this.roomNameGenerator({ + type: RoomNameType.Actual, + name: mRoomName.getContent().name, + }); } } const alias = this.getCanonicalAlias(); if (alias) { - return alias; + return this.roomNameGenerator({ + type: RoomNameType.Actual, + name: alias, + }); } const joinedMemberCount = this.currentState.getJoinedMemberCount(); @@ -2967,8 +3000,7 @@ export class Room extends TypedEventEmitter }); } else { let otherMembers = this.currentState.getMembers().filter((m) => { - return m.userId !== userId && - (m.membership === "invite" || m.membership === "join"); + return m.userId !== userId && (m.membership === "invite" || m.membership === "join"); }); otherMembers = otherMembers.filter(({ userId }) => { // filter service members @@ -2986,24 +3018,33 @@ export class Room extends TypedEventEmitter } if (inviteJoinCount) { - return memberNamesToRoomName(otherNames, inviteJoinCount); + return this.roomNameGenerator({ + type: RoomNameType.Generated, + names: otherNames, + count: inviteJoinCount, + }); } const myMembership = this.getMyMembership(); // if I have created a room and invited people through // 3rd party invites if (myMembership == 'join') { - const thirdPartyInvites = - this.currentState.getStateEvents(EventType.RoomThirdPartyInvite); + const thirdPartyInvites = this.currentState.getStateEvents(EventType.RoomThirdPartyInvite); - if (thirdPartyInvites && thirdPartyInvites.length) { + if (thirdPartyInvites?.length) { const thirdPartyNames = thirdPartyInvites.map((i) => { return i.getContent().display_name; }); - return `Inviting ${memberNamesToRoomName(thirdPartyNames)}`; + return this.roomNameGenerator({ + type: RoomNameType.Generated, + subtype: "Inviting", + names: thirdPartyNames, + count: thirdPartyNames.length + 1, + }); } } + // let's try to figure out who was here before let leftNames = otherNames; // if we didn't have heroes, try finding them in the room state @@ -3014,11 +3055,20 @@ export class Room extends TypedEventEmitter m.membership !== "join"; }).map((m) => m.name); } + + let oldName: string; if (leftNames.length) { - return `Empty room (was ${memberNamesToRoomName(leftNames)})`; - } else { - return "Empty room"; + oldName = this.roomNameGenerator({ + type: RoomNameType.Generated, + names: leftNames, + count: leftNames.length + 1, + }); } + + return this.roomNameGenerator({ + type: RoomNameType.EmptyRoom, + oldName, + }); } /** @@ -3203,8 +3253,33 @@ const ALLOWED_TRANSITIONS: Record = { [EventStatus.CANCELLED]: [], }; -// TODO i18n -function memberNamesToRoomName(names: string[], count = (names.length + 1)) { +export enum RoomNameType { + EmptyRoom, + Generated, + Actual, +} + +export interface EmptyRoomNameState { + type: RoomNameType.EmptyRoom; + oldName?: string; +} + +export interface GeneratedRoomNameState { + type: RoomNameType.Generated; + subtype?: "Inviting"; + names: string[]; + count: number; +} + +export interface ActualRoomNameState { + type: RoomNameType.Actual; + name: string; +} + +export type RoomNameState = EmptyRoomNameState | GeneratedRoomNameState | ActualRoomNameState; + +// Can be overriden by IMatrixClientCreateOpts::memberNamesToRoomNameFn +function memberNamesToRoomName(names: string[], count: number): string { const countWithoutMe = count - 1; if (!names.length) { return "Empty room"; From b265d795a427c6d30ccdf279a09f7836509df863 Mon Sep 17 00:00:00 2001 From: Robin Date: Mon, 22 Aug 2022 11:04:32 -0400 Subject: [PATCH 21/28] Re-emit room state events on rooms (#2607) * Re-emit room state events on rooms This also fixes some potential memory leaks and abuse of removeAllListeners in sync.ts. * Remove some stray whitespace * Deduplicate some code to appease SonarCloud * Name helper function more explicitly --- .eslintrc.js | 4 ++ src/ReEmitter.ts | 29 +++++++++ src/models/room.ts | 64 ++++++++++++++++---- src/sliding-sync-sdk.ts | 60 +------------------ src/sync.ts | 128 ++++++++++++++++------------------------ 5 files changed, 140 insertions(+), 145 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 12653d74475..5ed62980e7f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -57,6 +57,10 @@ module.exports = { // We're okay with assertion errors when we ask for them "@typescript-eslint/no-non-null-assertion": "off", + // The non-TypeScript rule produces false positives + "func-call-spacing": "off", + "@typescript-eslint/func-call-spacing": ["error"], + "quotes": "off", // We use a `logger` intermediary module "no-console": "error", diff --git a/src/ReEmitter.ts b/src/ReEmitter.ts index 5a352b8f077..91dbafd443b 100644 --- a/src/ReEmitter.ts +++ b/src/ReEmitter.ts @@ -24,7 +24,16 @@ import { ListenerMap, TypedEventEmitter } from "./models/typed-event-emitter"; export class ReEmitter { constructor(private readonly target: EventEmitter) {} + // Map from emitter to event name to re-emitter + private reEmitters = new Map void>>(); + public reEmit(source: EventEmitter, eventNames: string[]): void { + let reEmittersByEvent = this.reEmitters.get(source); + if (!reEmittersByEvent) { + reEmittersByEvent = new Map(); + this.reEmitters.set(source, reEmittersByEvent); + } + for (const eventName of eventNames) { // We include the source as the last argument for event handlers which may need it, // such as read receipt listeners on the client class which won't have the context @@ -44,7 +53,20 @@ export class ReEmitter { this.target.emit(eventName, ...args, source); }; source.on(eventName, forSource); + reEmittersByEvent.set(eventName, forSource); + } + } + + public stopReEmitting(source: EventEmitter, eventNames: string[]): void { + const reEmittersByEvent = this.reEmitters.get(source); + if (!reEmittersByEvent) return; // We were never re-emitting these events in the first place + + for (const eventName of eventNames) { + source.off(eventName, reEmittersByEvent.get(eventName)); + reEmittersByEvent.delete(eventName); } + + if (reEmittersByEvent.size === 0) this.reEmitters.delete(source); } } @@ -62,4 +84,11 @@ export class TypedReEmitter< ): void { super.reEmit(source, eventNames); } + + public stopReEmitting( + source: TypedEventEmitter, + eventNames: T[], + ): void { + super.stopReEmitting(source, eventNames); + } } diff --git a/src/models/room.ts b/src/models/room.ts index da945649750..46c623c5fcc 100644 --- a/src/models/room.ts +++ b/src/models/room.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. @@ -37,7 +37,8 @@ import { import { IRoomVersionsCapability, MatrixClient, PendingEventOrdering, RoomVersionStability } from "../client"; import { GuestAccess, HistoryVisibility, JoinRule, ResizeMethod } from "../@types/partials"; import { Filter, IFilterDefinition } from "../filter"; -import { RoomState } from "./room-state"; +import { RoomState, RoomStateEvent, RoomStateEventHandlerMap } from "./room-state"; +import { BeaconEvent, BeaconEventHandlerMap } from "./beacon"; import { Thread, ThreadEvent, @@ -172,16 +173,19 @@ export enum RoomEvent { } type EmittedEvents = RoomEvent + | RoomStateEvent.Events + | RoomStateEvent.Members + | RoomStateEvent.NewMember + | RoomStateEvent.Update + | RoomStateEvent.Marker | ThreadEvent.New | ThreadEvent.Update | ThreadEvent.NewReply - | RoomEvent.Timeline - | RoomEvent.TimelineReset - | RoomEvent.TimelineRefresh - | RoomEvent.HistoryImportedWithinTimeline - | RoomEvent.OldStateUpdated - | RoomEvent.CurrentStateUpdated - | MatrixEventEvent.BeforeRedaction; + | MatrixEventEvent.BeforeRedaction + | BeaconEvent.New + | BeaconEvent.Update + | BeaconEvent.Destroy + | BeaconEvent.LivenessChange; export type RoomEventHandlerMap = { [RoomEvent.MyMembership]: (room: Room, membership: string, prevMembership?: string) => void; @@ -205,7 +209,21 @@ export type RoomEventHandlerMap = { ) => void; [RoomEvent.TimelineRefresh]: (room: Room, eventTimelineSet: EventTimelineSet) => void; [ThreadEvent.New]: (thread: Thread, toStartOfTimeline: boolean) => void; -} & ThreadHandlerMap & MatrixEventHandlerMap; +} & ThreadHandlerMap + & MatrixEventHandlerMap + & Pick< + RoomStateEventHandlerMap, + RoomStateEvent.Events + | RoomStateEvent.Members + | RoomStateEvent.NewMember + | RoomStateEvent.Update + | RoomStateEvent.Marker + | BeaconEvent.New + > + & Pick< + BeaconEventHandlerMap, + BeaconEvent.Update | BeaconEvent.Destroy | BeaconEvent.LivenessChange + >; export class Room extends TypedEventEmitter { public readonly reEmitter: TypedReEmitter; @@ -1068,6 +1086,32 @@ export class Room extends TypedEventEmitter if (previousCurrentState !== this.currentState) { this.emit(RoomEvent.CurrentStateUpdated, this, previousCurrentState, this.currentState); + + // Re-emit various events on the current room state + // TODO: If currentState really only exists for backwards + // compatibility, shouldn't we be doing this some other way? + this.reEmitter.stopReEmitting(previousCurrentState, [ + RoomStateEvent.Events, + RoomStateEvent.Members, + RoomStateEvent.NewMember, + RoomStateEvent.Update, + RoomStateEvent.Marker, + BeaconEvent.New, + BeaconEvent.Update, + BeaconEvent.Destroy, + BeaconEvent.LivenessChange, + ]); + this.reEmitter.reEmit(this.currentState, [ + RoomStateEvent.Events, + RoomStateEvent.Members, + RoomStateEvent.NewMember, + RoomStateEvent.Update, + RoomStateEvent.Marker, + BeaconEvent.New, + BeaconEvent.Update, + BeaconEvent.Destroy, + BeaconEvent.LivenessChange, + ]); } } diff --git a/src/sliding-sync-sdk.ts b/src/sliding-sync-sdk.ts index 74a03b99b45..8c749f9263f 100644 --- a/src/sliding-sync-sdk.ts +++ b/src/sliding-sync-sdk.ts @@ -19,13 +19,11 @@ import { logger } from './logger'; import * as utils from "./utils"; import { EventTimeline } from "./models/event-timeline"; import { ClientEvent, IStoredClientOpts, MatrixClient, PendingEventOrdering } from "./client"; -import { ISyncStateData, SyncState } from "./sync"; +import { ISyncStateData, SyncState, _createAndReEmitRoom } from "./sync"; import { MatrixEvent } from "./models/event"; import { Crypto } from "./crypto"; import { IMinimalEvent, IRoomEvent, IStateEvent, IStrippedState } from "./sync-accumulator"; import { MatrixError } from "./http-api"; -import { RoomStateEvent } from "./models/room-state"; -import { RoomMemberEvent } from "./models/room-member"; import { Extension, ExtensionState, @@ -290,7 +288,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 = _createAndReEmitRoom(this.client, roomId, this.opts); } this.processRoomData(this.client, room, roomData); } @@ -536,7 +534,6 @@ export class SlidingSyncSdk { } if (limited) { - deregisterStateListeners(room); room.resetLiveTimeline( roomData.prev_batch, null, // TODO this.opts.canResetEntireTimeline(room.roomId) ? null : syncEventData.oldSyncToken, @@ -546,7 +543,6 @@ export class SlidingSyncSdk { // reason to stop incrementally tracking notifications and // reset the timeline. this.client.resetNotifTimelineSet(); - registerStateListeners(this.client, room); } } */ @@ -816,58 +812,6 @@ function ensureNameEvent(client: MatrixClient, roomId: string, roomData: MSC3575 // 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 4abd4fb5bb2..0a84c19a754 100644 --- a/src/sync.ts +++ b/src/sync.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. @@ -51,7 +51,7 @@ import { MatrixError, Method } from "./http-api"; import { ISavedSync } from "./store"; import { EventType } from "./@types/event"; import { IPushRules } from "./@types/PushRules"; -import { RoomState, RoomStateEvent, IMarkerFoundOptions } from "./models/room-state"; +import { RoomStateEvent, IMarkerFoundOptions } from "./models/room-state"; import { RoomMemberEvent } from "./models/room-member"; import { BeaconEvent } from "./models/beacon"; import { IEventsResponse } from "./@types/requests"; @@ -199,85 +199,13 @@ export class SyncApi { * @return {Room} */ public createRoom(roomId: string): Room { - const client = this.client; - const { - timelineSupport, - } = client; - const room = new Room(roomId, client, client.getUserId(), { - lazyLoadMembers: this.opts.lazyLoadMembers, - pendingEventOrdering: this.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, - ]); - this.registerStateListeners(room); - // Register listeners again after the state reference changes - room.on(RoomEvent.CurrentStateUpdated, (targetRoom, previousCurrentState) => { - if (targetRoom !== room) { - return; - } - - this.deregisterStateListeners(previousCurrentState); - this.registerStateListeners(room); - }); - return room; - } + const room = _createAndReEmitRoom(this.client, roomId, this.opts); - /** - * @param {Room} room - * @private - */ - private registerStateListeners(room: Room): void { - const client = this.client; - // 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. (TODO: find a better way?) - client.reEmitter.reEmit(room.currentState, [ - RoomStateEvent.Events, - RoomStateEvent.Members, - RoomStateEvent.NewMember, - RoomStateEvent.Update, - BeaconEvent.New, - BeaconEvent.Update, - BeaconEvent.Destroy, - BeaconEvent.LivenessChange, - ]); - - 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, - ]); - }); - - room.currentState.on(RoomStateEvent.Marker, (markerEvent, markerFoundOptions) => { + room.on(RoomStateEvent.Marker, (markerEvent, markerFoundOptions) => { this.onMarkerStateEvent(room, markerEvent, markerFoundOptions); }); - } - /** - * @param {RoomState} roomState The roomState to clear listeners from - * @private - */ - private deregisterStateListeners(roomState: RoomState): void { - // could do with a better way of achieving this. - roomState.removeAllListeners(RoomStateEvent.Events); - roomState.removeAllListeners(RoomStateEvent.Members); - roomState.removeAllListeners(RoomStateEvent.NewMember); - roomState.removeAllListeners(RoomStateEvent.Marker); + return room; } /** When we see the marker state change in the room, we know there is some @@ -1792,3 +1720,49 @@ function createNewUser(client: MatrixClient, userId: string): User { return user; } +// /!\ This function is not intended for public use! It's only exported from +// here in order to share some common logic with sliding-sync-sdk.ts. +export function _createAndReEmitRoom(client: MatrixClient, roomId: string, opts: Partial): Room { + 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, + RoomStateEvent.Events, + RoomStateEvent.Members, + RoomStateEvent.NewMember, + RoomStateEvent.Update, + BeaconEvent.New, + BeaconEvent.Update, + BeaconEvent.Destroy, + BeaconEvent.LivenessChange, + ]); + + // We need to add a listener for RoomState.members in order to hook them + // correctly. + room.on(RoomStateEvent.NewMember, (event, state, member) => { + member.user = client.getUser(member.userId); + client.reEmitter.reEmit(member, [ + RoomMemberEvent.Name, + RoomMemberEvent.Typing, + RoomMemberEvent.PowerLevel, + RoomMemberEvent.Membership, + ]); + }); + + return room; +} From de3b3960d29d036de4d36b010164f4611e7ecd46 Mon Sep 17 00:00:00 2001 From: Kegan Dougal Date: Mon, 22 Aug 2022 18:20:51 +0100 Subject: [PATCH 22/28] Add tags and not_tags to the list of valid sliding sync filters --- src/sliding-sync.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sliding-sync.ts b/src/sliding-sync.ts index 28026b3a999..6c19a35fa7d 100644 --- a/src/sliding-sync.ts +++ b/src/sliding-sync.ts @@ -47,6 +47,8 @@ export interface MSC3575Filter { room_types?: string[]; not_room_types?: string[]; spaces?: string[]; + tags?: string[]; + not_tags?: string[]; } /** From 438fc70615caf7ccb509902eda84365415eff3d9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 23 Aug 2022 08:42:56 +0200 Subject: [PATCH 23/28] Update all (#2614) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- package.json | 4 ++-- yarn.lock | 50 ++++++++++++++++++++++++++++++++------------------ 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index f8c73ea3549..b991e763c51 100644 --- a/package.json +++ b/package.json @@ -92,10 +92,10 @@ "better-docs": "^2.4.0-beta.9", "browserify": "^17.0.0", "docdash": "^1.2.0", - "eslint": "8.20.0", + "eslint": "8.22.0", "eslint-config-google": "^0.14.0", "eslint-plugin-import": "^2.25.4", - "eslint-plugin-matrix-org": "^0.5.0", + "eslint-plugin-matrix-org": "^0.6.0", "exorcist": "^2.0.0", "fake-indexeddb": "^4.0.0", "jest": "^28.0.0", diff --git a/yarn.lock b/yarn.lock index d5c8ddfa23d..e869e3751a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1041,15 +1041,20 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@humanwhocodes/config-array@^0.9.2": - version "0.9.5" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7" - integrity sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw== +"@humanwhocodes/config-array@^0.10.4": + version "0.10.4" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.10.4.tgz#01e7366e57d2ad104feea63e72248f22015c520c" + integrity sha512-mXAIHxZT3Vcpg83opl1wGlVZ9xydbfZO3r5YfRSH6Gpp2J/PfdBP0wbDa2sO6/qRbcalpoevVyW6A/fI6LfeMw== dependencies: "@humanwhocodes/object-schema" "^1.2.1" debug "^4.1.1" minimatch "^3.0.4" +"@humanwhocodes/gitignore-to-minimatch@^1.0.2": + version "1.0.2" + resolved "https://registry.yarnpkg.com/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz#316b0a63b91c10e53f242efb4ace5c3b34e8728d" + integrity sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA== + "@humanwhocodes/object-schema@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" @@ -1593,9 +1598,9 @@ integrity sha512-EdxgKRXgYsNITy5mjjXjVE/CS8YENSdhiagGrLqjG0pvA2owgJ6i4l7wy/PFZGC0B1/H20lWKN7ONVDNYDZm7A== "@types/node@16": - version "16.11.49" - resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.49.tgz#560b1ea774b61e19a89c3fc72d2dcaa3863f38b2" - integrity sha512-Abq9fBviLV93OiXMu+f6r0elxCzRwc0RC5f99cU892uBITL44pTvgvEqlRlPRi8EGcO1z7Cp8A4d0s/p3J/+Nw== + version "16.11.54" + resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.54.tgz#1bc17ff09bf340d9350c32200adab22f22753376" + integrity sha512-ryOpwe15+BtTUxKFfzABjaI/EtXLPBSBEW4B6D5ygWNcORLVKG/1/FC3WwAr5d7t6lCnlVPRsCY0NH680QT+Pg== "@types/prettier@^2.1.5": version "2.7.0" @@ -3127,10 +3132,10 @@ eslint-plugin-import@^2.25.4: resolve "^1.22.0" tsconfig-paths "^3.14.1" -eslint-plugin-matrix-org@^0.5.0: - version "0.5.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-matrix-org/-/eslint-plugin-matrix-org-0.5.2.tgz#eb355b1a81906ea814235d0b224e8162db7cbbf4" - integrity sha512-qJbyxp9cOi35Qpn3WCBqohCJaMSVp3ntOJ3WbjpREbCQdyrFze6MJAayl7GNidbNsdP7ejHTi0PtZzyKLcfLzQ== +eslint-plugin-matrix-org@^0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-matrix-org/-/eslint-plugin-matrix-org-0.6.1.tgz#deab0636a1fe999d9c2a42929c2b486334ec8ead" + integrity sha512-kq7fCbOdj6OvPF50gJtTVSgg6TbQCOxwwZktyIGQJfZyGNWhew77ptTnmaxgxq+RIQ+rzNcWrcMGO5eQC9fZAg== eslint-rule-composer@^0.3.0: version "0.3.0" @@ -3170,13 +3175,14 @@ 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.20.0: - version "8.20.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.20.0.tgz#048ac56aa18529967da8354a478be4ec0a2bc81b" - integrity sha512-d4ixhz5SKCa1D6SCPrivP7yYVi7nyD6A4vs6HIAul9ujBzcEmZVM3/0NN/yu5nKhmO1wjp5xQ46iRfmDGlOviA== +eslint@8.22.0: + version "8.22.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.22.0.tgz#78fcb044196dfa7eef30a9d65944f6f980402c48" + integrity sha512-ci4t0sz6vSRKdmkOGmprBo6fmI4PrphDFMy5JEq/fNS0gQkJM3rLmrqcp8ipMcdobH3KtUP40KniAE9W19S4wA== dependencies: "@eslint/eslintrc" "^1.3.0" - "@humanwhocodes/config-array" "^0.9.2" + "@humanwhocodes/config-array" "^0.10.4" + "@humanwhocodes/gitignore-to-minimatch" "^1.0.2" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -3186,14 +3192,17 @@ eslint@8.20.0: eslint-scope "^7.1.1" eslint-utils "^3.0.0" eslint-visitor-keys "^3.3.0" - espree "^9.3.2" + espree "^9.3.3" esquery "^1.4.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" + find-up "^5.0.0" functional-red-black-tree "^1.0.1" glob-parent "^6.0.1" globals "^13.15.0" + globby "^11.1.0" + grapheme-splitter "^1.0.4" ignore "^5.2.0" import-fresh "^3.0.0" imurmurhash "^0.1.4" @@ -3211,7 +3220,7 @@ eslint@8.20.0: text-table "^0.2.0" v8-compile-cache "^2.0.3" -espree@^9.3.2: +espree@^9.3.2, espree@^9.3.3: version "9.3.3" resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.3.tgz#2dd37c4162bb05f433ad3c1a52ddf8a49dc08e9d" integrity sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng== @@ -3640,6 +3649,11 @@ graceful-fs@^4.1.9, graceful-fs@^4.2.9: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== +grapheme-splitter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" + integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== + har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" From 6571b6a1ab38a491f2657e319a79f7524022bf4c Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 23 Aug 2022 10:53:01 +0100 Subject: [PATCH 24/28] Prepare changelog for v19.4.0-rc.1 --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 00691cac080..bb200a6b4e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +Changes in [19.4.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v19.4.0-rc.1) (2022-08-23) +============================================================================================================ + +## ✨ Features + * Re-emit room state events on rooms ([\#2607](https://github.com/matrix-org/matrix-js-sdk/pull/2607)). + * Add ability to override built in room name generator for an i18n'able one ([\#2609](https://github.com/matrix-org/matrix-js-sdk/pull/2609)). + * Add txn_id support to sliding sync ([\#2567](https://github.com/matrix-org/matrix-js-sdk/pull/2567)). + +## 🐛 Bug Fixes + * Fixed a sliding sync bug which could cause the `roomIndexToRoomId` map to be incorrect when a new room is added in the middle of the list or when an existing room is deleted from the middle of the list. ([\#2610](https://github.com/matrix-org/matrix-js-sdk/pull/2610)). + * Fix: Handle parsing of a beacon info event without asset ([\#2591](https://github.com/matrix-org/matrix-js-sdk/pull/2591)). Fixes vector-im/element-web#23078. + * Fix finding event read up to if stable private read receipts is missing ([\#2585](https://github.com/matrix-org/matrix-js-sdk/pull/2585)). Fixes vector-im/element-web#23027. + * fixed a sliding sync issue where history could be interpreted as live events. ([\#2583](https://github.com/matrix-org/matrix-js-sdk/pull/2583)). + Changes in [19.3.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v19.3.0) (2022-08-16) ================================================================================================== From 528e9343ae6a6ddd28cb388c31a4868610a290b3 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 23 Aug 2022 10:53:02 +0100 Subject: [PATCH 25/28] v19.4.0-rc.1 --- package.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index b991e763c51..abc351282af 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-js-sdk", - "version": "19.3.0", + "version": "19.4.0-rc.1", "description": "Matrix Client-Server SDK for Javascript", "engines": { "node": ">=12.9.0" @@ -32,7 +32,7 @@ "keywords": [ "matrix-org" ], - "main": "./src/index.ts", + "main": "./lib/index.js", "browser": "./lib/browser-index.js", "matrix_src_main": "./src/index.ts", "matrix_src_browser": "./src/browser-index.js", @@ -125,5 +125,6 @@ "jestSonar": { "reportPath": "coverage", "sonar56x": true - } + }, + "typings": "./lib/index.d.ts" } From 8716c1ab9ba93659173b806097c46a2be115199f Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 29 Aug 2022 16:53:09 -0600 Subject: [PATCH 26/28] Convert several internal maps to real maps --- spec/unit/crypto/algorithms/megolm.spec.ts | 4 +- spec/unit/crypto/backup.spec.ts | 2 +- src/crypto/algorithms/base.ts | 8 ++-- src/crypto/algorithms/megolm.ts | 22 +++++----- src/crypto/index.ts | 51 +++++++++++----------- src/models/event-timeline-set.ts | 36 +++++++-------- src/models/event.ts | 18 +++----- src/models/relations-container.ts | 40 ++++++++--------- src/models/room-state.ts | 15 +++---- src/utils.ts | 24 ++++++++++ 10 files changed, 118 insertions(+), 102 deletions(-) diff --git a/spec/unit/crypto/algorithms/megolm.spec.ts b/spec/unit/crypto/algorithms/megolm.spec.ts index 9aa3c5c785f..b9f16c742ec 100644 --- a/spec/unit/crypto/algorithms/megolm.spec.ts +++ b/spec/unit/crypto/algorithms/megolm.spec.ts @@ -32,8 +32,8 @@ import { ClientEvent, MatrixClient, RoomMember } from '../../../../src'; import { DeviceInfo, IDevice } from '../../../../src/crypto/deviceinfo'; import { DeviceTrustLevel } from '../../../../src/crypto/CrossSigning'; -const MegolmDecryption = algorithms.DECRYPTION_CLASSES['m.megolm.v1.aes-sha2']; -const MegolmEncryption = algorithms.ENCRYPTION_CLASSES['m.megolm.v1.aes-sha2']; +const MegolmDecryption = algorithms.DECRYPTION_CLASSES.get('m.megolm.v1.aes-sha2'); +const MegolmEncryption = algorithms.ENCRYPTION_CLASSES.get('m.megolm.v1.aes-sha2'); const ROOM_ID = '!ROOM:ID'; diff --git a/spec/unit/crypto/backup.spec.ts b/spec/unit/crypto/backup.spec.ts index 6759fe16152..8e264740406 100644 --- a/spec/unit/crypto/backup.spec.ts +++ b/spec/unit/crypto/backup.spec.ts @@ -34,7 +34,7 @@ import { IAbortablePromise, MatrixScheduler } from '../../../src'; const Olm = global.Olm; -const MegolmDecryption = algorithms.DECRYPTION_CLASSES['m.megolm.v1.aes-sha2']; +const MegolmDecryption = algorithms.DECRYPTION_CLASSES.get('m.megolm.v1.aes-sha2'); const ROOM_ID = '!ROOM:ID'; diff --git a/src/crypto/algorithms/base.ts b/src/crypto/algorithms/base.ts index 22bd4505d57..898a04dd811 100644 --- a/src/crypto/algorithms/base.ts +++ b/src/crypto/algorithms/base.ts @@ -34,7 +34,7 @@ import { IRoomEncryption } from "../RoomList"; * * @type {Object.} */ -export const ENCRYPTION_CLASSES: Record EncryptionAlgorithm> = {}; +export const ENCRYPTION_CLASSES = new Map EncryptionAlgorithm>(); type DecryptionClassParams = Omit; @@ -44,7 +44,7 @@ type DecryptionClassParams = Omit; * * @type {Object.} */ -export const DECRYPTION_CLASSES: Record DecryptionAlgorithm> = {}; +export const DECRYPTION_CLASSES = new Map DecryptionAlgorithm>(); export interface IParams { userId: string; @@ -297,6 +297,6 @@ export function registerAlgorithm( encryptor: new (params: IParams) => EncryptionAlgorithm, decryptor: new (params: Omit) => DecryptionAlgorithm, ): void { - ENCRYPTION_CLASSES[algorithm] = encryptor; - DECRYPTION_CLASSES[algorithm] = decryptor; + ENCRYPTION_CLASSES.set(algorithm, encryptor); + DECRYPTION_CLASSES.set(algorithm, decryptor); } diff --git a/src/crypto/algorithms/megolm.ts b/src/crypto/algorithms/megolm.ts index 1807905ba18..122164f44b6 100644 --- a/src/crypto/algorithms/megolm.ts +++ b/src/crypto/algorithms/megolm.ts @@ -1191,7 +1191,7 @@ class MegolmEncryption extends EncryptionAlgorithm { class MegolmDecryption extends DecryptionAlgorithm { // events which we couldn't decrypt due to unknown sessions / indexes: map from // senderKey|sessionId to Set of MatrixEvents - private pendingEvents: Record>> = {}; + private pendingEvents = new Map>>(); // this gets stubbed out by the unit tests. private olmlib = olmlib; @@ -1343,10 +1343,10 @@ class MegolmDecryption extends DecryptionAlgorithm { const content = event.getWireContent(); const senderKey = content.sender_key; const sessionId = content.session_id; - if (!this.pendingEvents[senderKey]) { - this.pendingEvents[senderKey] = new Map(); + if (!this.pendingEvents.has(senderKey)) { + this.pendingEvents.set(senderKey, new Map>()); } - const senderPendingEvents = this.pendingEvents[senderKey]; + const senderPendingEvents = this.pendingEvents.get(senderKey); if (!senderPendingEvents.has(sessionId)) { senderPendingEvents.set(sessionId, new Set()); } @@ -1364,7 +1364,7 @@ class MegolmDecryption extends DecryptionAlgorithm { const content = event.getWireContent(); const senderKey = content.sender_key; const sessionId = content.session_id; - const senderPendingEvents = this.pendingEvents[senderKey]; + const senderPendingEvents = this.pendingEvents.get(senderKey); const pendingEvents = senderPendingEvents?.get(sessionId); if (!pendingEvents) { return; @@ -1375,7 +1375,7 @@ class MegolmDecryption extends DecryptionAlgorithm { senderPendingEvents.delete(sessionId); } if (senderPendingEvents.size === 0) { - delete this.pendingEvents[senderKey]; + this.pendingEvents.delete(senderKey); } } @@ -1711,7 +1711,7 @@ class MegolmDecryption extends DecryptionAlgorithm { * @return {Boolean} whether all messages were successfully decrypted */ private async retryDecryption(senderKey: string, sessionId: string): Promise { - const senderPendingEvents = this.pendingEvents[senderKey]; + const senderPendingEvents = this.pendingEvents.get(senderKey); if (!senderPendingEvents) { return true; } @@ -1732,16 +1732,16 @@ class MegolmDecryption extends DecryptionAlgorithm { })); // If decrypted successfully, they'll have been removed from pendingEvents - return !this.pendingEvents[senderKey]?.has(sessionId); + return !this.pendingEvents.get(senderKey)?.has(sessionId); } public async retryDecryptionFromSender(senderKey: string): Promise { - const senderPendingEvents = this.pendingEvents[senderKey]; + const senderPendingEvents = this.pendingEvents.get(senderKey); if (!senderPendingEvents) { return true; } - delete this.pendingEvents[senderKey]; + this.pendingEvents.delete(senderKey); await Promise.all([...senderPendingEvents].map(async ([_sessionId, pending]) => { await Promise.all([...pending].map(async (ev) => { @@ -1753,7 +1753,7 @@ class MegolmDecryption extends DecryptionAlgorithm { })); })); - return !this.pendingEvents[senderKey]; + return !this.pendingEvents.has(senderKey); } public async sendSharedHistoryInboundSessions(devicesByUser: Record): Promise { diff --git a/src/crypto/index.ts b/src/crypto/index.ts index 451d2d8c82a..65391648195 100644 --- a/src/crypto/index.ts +++ b/src/crypto/index.ts @@ -278,9 +278,9 @@ export class Crypto extends TypedEventEmitter = {}; + private roomEncryptors = new Map(); // map from algorithm to DecryptionAlgorithm instance, for each room - private roomDecryptors: Record> = {}; + private roomDecryptors = new Map>(); private deviceKeys: Record = {}; // type: key @@ -422,7 +422,7 @@ export class Crypto extends TypedEventEmitter { const trackMembers = async () => { // not an encrypted room - if (!this.roomEncryptors[roomId]) { + if (!this.roomEncryptors.has(roomId)) { return; } const room = this.clientStore.getRoom(roomId); @@ -2785,7 +2785,7 @@ export class Crypto extends TypedEventEmitter { // check for rooms with encryption enabled - const alg = this.roomEncryptors[room.roomId]; + const alg = this.roomEncryptors.get(room.roomId); if (!alg) { return false; } @@ -3533,7 +3533,7 @@ export class Crypto extends TypedEventEmitter; + let decryptors: Map; let alg: DecryptionAlgorithm; roomId = roomId || null; if (roomId) { - decryptors = this.roomDecryptors[roomId]; + decryptors = this.roomDecryptors.get(roomId); if (!decryptors) { - this.roomDecryptors[roomId] = decryptors = {}; + decryptors = new Map(); + this.roomDecryptors.set(roomId, decryptors); } - alg = decryptors[algorithm]; + alg = decryptors.get(algorithm); if (alg) { return alg; } } - const AlgClass = algorithms.DECRYPTION_CLASSES[algorithm]; + const AlgClass = algorithms.DECRYPTION_CLASSES.get(algorithm); if (!AlgClass) { throw new algorithms.DecryptionError( 'UNKNOWN_ENCRYPTION_ALGORITHM', @@ -3777,7 +3778,7 @@ export class Crypto extends TypedEventEmitter; + private _eventIdToTimeline = new Map(); private filter?: Filter; /** @@ -138,7 +138,7 @@ export class EventTimelineSet extends TypedEventEmitter(); this.filter = opts.filter; @@ -210,7 +210,7 @@ export class EventTimelineSet extends TypedEventEmitter(); } else { this.timelines.push(newTimeline); } @@ -288,7 +288,7 @@ export class EventTimelineSet extends TypedEventEmitter = {}; -function intern(str: string): string { - if (!interns[str]) { - interns[str] = str; - } - return interns[str]; -} - /* eslint-disable camelcase */ export interface IContent { [key: string]: any; @@ -326,17 +318,17 @@ export class MatrixEvent extends TypedEventEmitter { if (typeof event[prop] !== "string") return; - event[prop] = intern(event[prop]); + event[prop] = internaliseString(event[prop]); }); ["membership", "avatar_url", "displayname"].forEach((prop) => { if (typeof event.content?.[prop] !== "string") return; - event.content[prop] = intern(event.content[prop]); + event.content[prop] = internaliseString(event.content[prop]); }); ["rel_type"].forEach((prop) => { if (typeof event.content?.["m.relates_to"]?.[prop] !== "string") return; - event.content["m.relates_to"][prop] = intern(event.content["m.relates_to"][prop]); + event.content["m.relates_to"][prop] = internaliseString(event.content["m.relates_to"][prop]); }); this.txnId = event.txn_id || null; @@ -796,6 +788,8 @@ export class MatrixEvent extends TypedEventEmitter>>(); constructor(private readonly client: MatrixClient, private readonly room?: Room) { } @@ -57,14 +51,15 @@ export class RelationsContainer { relationType: RelationType | string, eventType: EventType | string, ): Relations | undefined { - return this.relations[eventId]?.[relationType]?.[eventType]; + return this.relations.get(eventId)?.get(relationType)?.get(eventType); } public getAllChildEventsForEvent(parentEventId: string): MatrixEvent[] { - const relationsForEvent = this.relations[parentEventId] ?? {}; + const relationsForEvent = this.relations.get(parentEventId) + ?? new Map>(); const events: MatrixEvent[] = []; - for (const relationsRecord of Object.values(relationsForEvent)) { - for (const relations of Object.values(relationsRecord)) { + for (const relationsRecord of relationsForEvent.values()) { + for (const relations of relationsRecord.values()) { events.push(...relations.getRelations()); } } @@ -79,11 +74,11 @@ export class RelationsContainer { * @param {MatrixEvent} event The event to check as relation target. */ public aggregateParentEvent(event: MatrixEvent): void { - const relationsForEvent = this.relations[event.getId()]; + const relationsForEvent = this.relations.get(event.getId()); if (!relationsForEvent) return; - for (const relationsWithRelType of Object.values(relationsForEvent)) { - for (const relationsWithEventType of Object.values(relationsWithRelType)) { + for (const relationsWithRelType of relationsForEvent.values()) { + for (const relationsWithEventType of relationsWithRelType.values()) { relationsWithEventType.setTargetEvent(event); } } @@ -123,23 +118,26 @@ export class RelationsContainer { const { event_id: relatesToEventId, rel_type: relationType } = relation; const eventType = event.getType(); - let relationsForEvent = this.relations[relatesToEventId]; + let relationsForEvent = this.relations.get(relatesToEventId); if (!relationsForEvent) { - relationsForEvent = this.relations[relatesToEventId] = {}; + relationsForEvent = new Map>(); + this.relations.set(relatesToEventId, relationsForEvent); } - let relationsWithRelType = relationsForEvent[relationType]; + let relationsWithRelType = relationsForEvent.get(relationType); if (!relationsWithRelType) { - relationsWithRelType = relationsForEvent[relationType] = {}; + relationsWithRelType = new Map(); + relationsForEvent.set(relationType, relationsWithRelType); } - let relationsWithEventType = relationsWithRelType[eventType]; + let relationsWithEventType = relationsWithRelType.get(eventType); if (!relationsWithEventType) { - relationsWithEventType = relationsWithRelType[eventType] = new Relations( + relationsWithEventType = new Relations( relationType, eventType, this.client, ); + relationsWithRelType.set(eventType, relationsWithEventType); const room = this.room ?? timelineSet?.room; const relatesToEvent = timelineSet?.findEventById(relatesToEventId) diff --git a/src/models/room-state.ts b/src/models/room-state.ts index c7d3ac32502..b0104cf707c 100644 --- a/src/models/room-state.ts +++ b/src/models/room-state.ts @@ -79,7 +79,7 @@ export class RoomState extends TypedEventEmitter public readonly reEmitter = new TypedReEmitter(this); private sentinels: Record = {}; // userId: RoomMember // stores fuzzy matches to a list of userIDs (applies utils.removeHiddenChars to keys) - private displayNameToUserIds: Record = {}; + private displayNameToUserIds = new Map(); private userIdsToDisplayNames: Record = {}; private tokenToInvite: Record = {}; // 3pid invite state_key to m.room.member invite private joinedMemberCount: number = null; // cache of the number of joined members @@ -709,7 +709,7 @@ export class RoomState extends TypedEventEmitter * @return {string[]} An array of user IDs or an empty array. */ public getUserIdsWithDisplayName(displayName: string): string[] { - return this.displayNameToUserIds[utils.removeHiddenChars(displayName)] || []; + return this.displayNameToUserIds.get(utils.removeHiddenChars(displayName)) ?? []; } /** @@ -941,11 +941,11 @@ export class RoomState extends TypedEventEmitter // the lot. const strippedOldName = utils.removeHiddenChars(oldName); - const existingUserIds = this.displayNameToUserIds[strippedOldName]; + const existingUserIds = this.displayNameToUserIds.get(strippedOldName); if (existingUserIds) { // remove this user ID from this array const filteredUserIDs = existingUserIds.filter((id) => id !== userId); - this.displayNameToUserIds[strippedOldName] = filteredUserIDs; + this.displayNameToUserIds.set(strippedOldName, filteredUserIDs); } } @@ -954,10 +954,9 @@ export class RoomState extends TypedEventEmitter const strippedDisplayname = displayName && utils.removeHiddenChars(displayName); // an empty stripped displayname (undefined/'') will be set to MXID in room-member.js if (strippedDisplayname) { - if (!this.displayNameToUserIds[strippedDisplayname]) { - this.displayNameToUserIds[strippedDisplayname] = []; - } - this.displayNameToUserIds[strippedDisplayname].push(userId); + const arr = this.displayNameToUserIds.get(strippedDisplayname) ?? []; + arr.push(userId); + this.displayNameToUserIds.set(strippedDisplayname, arr); } } } diff --git a/src/utils.ts b/src/utils.ts index 63e3475b8fb..69784bcd042 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -28,6 +28,30 @@ import { MatrixClient, MatrixEvent } from "."; import { M_TIMESTAMP } from "./@types/location"; import { ReceiptType } from "./@types/read_receipts"; +const interns = new Map(); + +/** + * Internalises a string, reusing a known pointer or storing the pointer + * if needed for future strings. + * @param str The string to internalise. + * @returns The internalised string. + */ +export function internaliseString(str: string): string { + // Unwrap strings before entering the map, if we somehow got a wrapped + // string as our input. This should only happen from tests. + if ((str as unknown) instanceof String) { + str = str.toString(); + } + + // Check the map to see if we can store the value + if (!interns.has(str)) { + interns.set(str, str); + } + + // Return any cached string reference + return interns.get(str); +} + /** * Encode a dictionary of query parameters. * Omits any undefined/null values. From 06e8d98911a802bd1c81a27d925ceaad83441803 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Wed, 31 Aug 2022 16:24:26 +0100 Subject: [PATCH 27/28] Prepare changelog for v19.4.0 --- CHANGELOG.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bb200a6b4e6..1951ec10256 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,5 @@ -Changes in [19.4.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v19.4.0-rc.1) (2022-08-23) -============================================================================================================ +Changes in [19.4.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v19.4.0) (2022-08-31) +================================================================================================== ## ✨ Features * Re-emit room state events on rooms ([\#2607](https://github.com/matrix-org/matrix-js-sdk/pull/2607)). @@ -7,8 +7,11 @@ Changes in [19.4.0-rc.1](https://github.com/matrix-org/matrix-js-sdk/releases/ta * Add txn_id support to sliding sync ([\#2567](https://github.com/matrix-org/matrix-js-sdk/pull/2567)). ## 🐛 Bug Fixes - * Fixed a sliding sync bug which could cause the `roomIndexToRoomId` map to be incorrect when a new room is added in the middle of the list or when an existing room is deleted from the middle of the list. ([\#2610](https://github.com/matrix-org/matrix-js-sdk/pull/2610)). - * Fix: Handle parsing of a beacon info event without asset ([\#2591](https://github.com/matrix-org/matrix-js-sdk/pull/2591)). Fixes vector-im/element-web#23078. + * Refactor Sync and fix `initialSyncLimit` ([\#2587](https://github.com/matrix-org/matrix-js-sdk/pull/2587)). + * Use deep equality comparisons when searching for outgoing key requests by target ([\#2623](https://github.com/matrix-org/matrix-js-sdk/pull/2623)). Contributed by @duxovni. + * Fix room membership race with PREPARED event ([\#2613](https://github.com/matrix-org/matrix-js-sdk/pull/2613)). Contributed by @jotto. + * fixed a sliding sync bug which could cause the `roomIndexToRoomId` map to be incorrect when a new room is added in the middle of the list or when an existing room is deleted from the middle of the list. ([\#2610](https://github.com/matrix-org/matrix-js-sdk/pull/2610)). + * Fix: Handle parsing of a beacon info event without asset ([\#2591](https://github.com/matrix-org/matrix-js-sdk/pull/2591)). Fixes vector-im/element-web#23078. Contributed by @kerryarchibald. * Fix finding event read up to if stable private read receipts is missing ([\#2585](https://github.com/matrix-org/matrix-js-sdk/pull/2585)). Fixes vector-im/element-web#23027. * fixed a sliding sync issue where history could be interpreted as live events. ([\#2583](https://github.com/matrix-org/matrix-js-sdk/pull/2583)). From 1efeb1ec0ef6324a262e606716ea435890a6b39a Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Wed, 31 Aug 2022 16:24:27 +0100 Subject: [PATCH 28/28] v19.4.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index abc351282af..954496c7c7f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-js-sdk", - "version": "19.4.0-rc.1", + "version": "19.4.0", "description": "Matrix Client-Server SDK for Javascript", "engines": { "node": ">=12.9.0"