diff --git a/package.json b/package.json index 6ad57b04a7d..262cbecc447 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ }, "scripts": { "prepublishOnly": "yarn build", + "postinstall": "patch-package", "start": "echo THIS IS FOR LEGACY PURPOSES ONLY. && babel src -w -s -d lib --verbose --extensions \".ts,.js\"", "dist": "echo 'This is for the release script so it can make assets (browser bundle).' && yarn build", "clean": "rimraf lib dist", @@ -58,7 +59,7 @@ "bs58": "^5.0.0", "content-type": "^1.0.4", "loglevel": "^1.7.1", - "matrix-events-sdk": "^0.0.1-beta.7", + "matrix-events-sdk": "0.0.1-beta.7", "p-retry": "4", "qs": "^6.9.6", "unhomoglyph": "^1.0.6" @@ -105,6 +106,8 @@ "jest-sonar-reporter": "^2.0.0", "jsdoc": "^3.6.6", "matrix-mock-request": "^2.5.0", + "patch-package": "^6.5.0", + "postinstall-postinstall": "^2.1.0", "rimraf": "^3.0.2", "terser": "^5.5.1", "tsify": "^5.0.2", diff --git a/patches/matrix-events-sdk+0.0.1-beta.7.patch b/patches/matrix-events-sdk+0.0.1-beta.7.patch new file mode 100644 index 00000000000..1d61653639f --- /dev/null +++ b/patches/matrix-events-sdk+0.0.1-beta.7.patch @@ -0,0 +1,12 @@ +diff --git a/node_modules/matrix-events-sdk/lib/NamespacedMap.d.ts b/node_modules/matrix-events-sdk/lib/NamespacedMap.d.ts +index c141b11..461f528 100644 +--- a/node_modules/matrix-events-sdk/lib/NamespacedMap.d.ts ++++ b/node_modules/matrix-events-sdk/lib/NamespacedMap.d.ts +@@ -1,6 +1,6 @@ + import { NamespacedValue } from "./NamespacedValue"; + import { Optional } from "./types"; +-declare type NS = NamespacedValue, Optional>; ++declare type NS = NamespacedValue; + /** + * A `Map` implementation which accepts a NamespacedValue as a key, and arbitrary value. The + * namespaced value must be a string type. diff --git a/spec/integ/matrix-client-syncing.spec.ts b/spec/integ/matrix-client-syncing.spec.ts index 418e2d16add..216fbdbb1f6 100644 --- a/spec/integ/matrix-client-syncing.spec.ts +++ b/spec/integ/matrix-client-syncing.spec.ts @@ -707,15 +707,11 @@ describe("MatrixClient syncing", () => { awaitSyncEvent(2), ]).then(() => { const room = client!.getRoom(roomOne)!; - const stateAtStart = room.getLiveTimeline().getState( - EventTimeline.BACKWARDS, - ); + const stateAtStart = room.getLiveTimeline().getState(EventTimeline.BACKWARDS)!; const startRoomNameEvent = stateAtStart.getStateEvents('m.room.name', ''); expect(startRoomNameEvent.getContent().name).toEqual('Old room name'); - const stateAtEnd = room.getLiveTimeline().getState( - EventTimeline.FORWARDS, - ); + const stateAtEnd = room.getLiveTimeline().getState(EventTimeline.FORWARDS)!; const endRoomNameEvent = stateAtEnd.getStateEvents('m.room.name', ''); expect(endRoomNameEvent.getContent().name).toEqual('A new room name'); }); diff --git a/spec/unit/event-timeline.spec.ts b/spec/unit/event-timeline.spec.ts index 59f35dcce02..21434ec7111 100644 --- a/spec/unit/event-timeline.spec.ts +++ b/spec/unit/event-timeline.spec.ts @@ -55,13 +55,13 @@ describe("EventTimeline", function() { ]; timeline.initialiseState(events); // @ts-ignore private prop - const timelineStartState = timeline.startState; + const timelineStartState = timeline.startState!; expect(mocked(timelineStartState).setStateEvents).toHaveBeenCalledWith( events, { timelineWasEmpty: undefined }, ); // @ts-ignore private prop - const timelineEndState = timeline.endState; + const timelineEndState = timeline.endState!; expect(mocked(timelineEndState).setStateEvents).toHaveBeenCalledWith( events, { timelineWasEmpty: undefined }, @@ -185,14 +185,14 @@ describe("EventTimeline", function() { sentinel.name = "Old Alice"; sentinel.membership = "join"; - mocked(timeline.getState(EventTimeline.FORWARDS)).getSentinelMember + mocked(timeline.getState(EventTimeline.FORWARDS)!).getSentinelMember .mockImplementation(function(uid) { if (uid === userA) { return sentinel; } return null; }); - mocked(timeline.getState(EventTimeline.BACKWARDS)).getSentinelMember + mocked(timeline.getState(EventTimeline.BACKWARDS)!).getSentinelMember .mockImplementation(function(uid) { if (uid === userA) { return oldSentinel; @@ -225,14 +225,14 @@ describe("EventTimeline", function() { sentinel.name = "Old Alice"; sentinel.membership = "join"; - mocked(timeline.getState(EventTimeline.FORWARDS)).getSentinelMember + mocked(timeline.getState(EventTimeline.FORWARDS)!).getSentinelMember .mockImplementation(function(uid) { if (uid === userA) { return sentinel; } return null; }); - mocked(timeline.getState(EventTimeline.BACKWARDS)).getSentinelMember + mocked(timeline.getState(EventTimeline.BACKWARDS)!).getSentinelMember .mockImplementation(function(uid) { if (uid === userA) { return oldSentinel; @@ -269,15 +269,15 @@ describe("EventTimeline", function() { timeline.addEvent(events[0], { toStartOfTimeline: false }); timeline.addEvent(events[1], { toStartOfTimeline: false }); - expect(timeline.getState(EventTimeline.FORWARDS).setStateEvents). + expect(timeline.getState(EventTimeline.FORWARDS)!.setStateEvents). toHaveBeenCalledWith([events[0]], { timelineWasEmpty: undefined }); - expect(timeline.getState(EventTimeline.FORWARDS).setStateEvents). + expect(timeline.getState(EventTimeline.FORWARDS)!.setStateEvents). toHaveBeenCalledWith([events[1]], { timelineWasEmpty: undefined }); expect(events[0].forwardLooking).toBe(true); expect(events[1].forwardLooking).toBe(true); - expect(timeline.getState(EventTimeline.BACKWARDS).setStateEvents). + expect(timeline.getState(EventTimeline.BACKWARDS)!.setStateEvents). not.toHaveBeenCalled(); }); @@ -298,15 +298,15 @@ describe("EventTimeline", function() { timeline.addEvent(events[0], { toStartOfTimeline: true }); timeline.addEvent(events[1], { toStartOfTimeline: true }); - expect(timeline.getState(EventTimeline.BACKWARDS).setStateEvents). + expect(timeline.getState(EventTimeline.BACKWARDS)!.setStateEvents). toHaveBeenCalledWith([events[0]], { timelineWasEmpty: undefined }); - expect(timeline.getState(EventTimeline.BACKWARDS).setStateEvents). + expect(timeline.getState(EventTimeline.BACKWARDS)!.setStateEvents). toHaveBeenCalledWith([events[1]], { timelineWasEmpty: undefined }); expect(events[0].forwardLooking).toBe(false); expect(events[1].forwardLooking).toBe(false); - expect(timeline.getState(EventTimeline.FORWARDS).setStateEvents). + expect(timeline.getState(EventTimeline.FORWARDS)!.setStateEvents). not.toHaveBeenCalled(); }); diff --git a/spec/unit/webrtc/call.spec.ts b/spec/unit/webrtc/call.spec.ts index 01b110b4fad..8b42d9fe61a 100644 --- a/spec/unit/webrtc/call.spec.ts +++ b/spec/unit/webrtc/call.spec.ts @@ -757,7 +757,7 @@ describe('Call', function() { describe("transferToCall", () => { it("should send the required events", async () => { - const targetCall = new MatrixCall({ client: client.client }); + const targetCall = new MatrixCall({ client: client.client, roomId: "!roomId:server" }); const sendEvent = jest.spyOn(client.client, "sendEvent"); await call.transferToCall(targetCall); diff --git a/src/@types/PushRules.ts b/src/@types/PushRules.ts index f3aeca156ba..59af4906074 100644 --- a/src/@types/PushRules.ts +++ b/src/@types/PushRules.ts @@ -30,7 +30,7 @@ export enum TweakName { export type Tweak = { set_tweak: N; - value: V; + value?: V; }; export type TweakHighlight = Tweak; @@ -76,7 +76,8 @@ export interface IPushRuleCondition { export interface IEventMatchCondition extends IPushRuleCondition { key: string; - pattern: string; + pattern?: string; + value?: string; } export interface IContainsDisplayNameCondition extends IPushRuleCondition { diff --git a/src/autodiscovery.ts b/src/autodiscovery.ts index f2128c0d549..d26e4d5c683 100644 --- a/src/autodiscovery.ts +++ b/src/autodiscovery.ts @@ -32,6 +32,28 @@ export enum AutoDiscoveryAction { FAIL_ERROR = "FAIL_ERROR", } +enum AutoDiscoveryError { + Invalid = "Invalid homeserver discovery response", + GenericFailure = "Failed to get autodiscovery configuration from server", + InvalidHsBaseUrl = "Invalid base_url for m.homeserver", + InvalidHomeserver = "Homeserver URL does not appear to be a valid Matrix homeserver", + InvalidIsBaseUrl = "Invalid base_url for m.identity_server", + InvalidIdentityServer = "Identity server URL does not appear to be a valid identity server", + InvalidIs = "Invalid identity server discovery response", + MissingWellknown = "No .well-known JSON file found", + InvalidJson = "Invalid JSON", +} + +interface WellKnownConfig extends Omit { + state: AutoDiscoveryAction; + error?: IWellKnownConfig["error"] | null; +} + +interface ClientConfig { + "m.homeserver": WellKnownConfig; + "m.identity_server": WellKnownConfig; +} + /** * Utilities for automatically discovery resources, such as homeservers * for users to log in to. @@ -42,36 +64,25 @@ export class AutoDiscovery { // translate the meaning of the states in the spec, but also // support our own if needed. - public static readonly ERROR_INVALID = "Invalid homeserver discovery response"; + public static readonly ERROR_INVALID = AutoDiscoveryError.Invalid; - public static readonly ERROR_GENERIC_FAILURE = "Failed to get autodiscovery configuration from server"; + public static readonly ERROR_GENERIC_FAILURE = AutoDiscoveryError.GenericFailure; - public static readonly ERROR_INVALID_HS_BASE_URL = "Invalid base_url for m.homeserver"; + public static readonly ERROR_INVALID_HS_BASE_URL = AutoDiscoveryError.InvalidHsBaseUrl; - public static readonly ERROR_INVALID_HOMESERVER = "Homeserver URL does not appear to be a valid Matrix homeserver"; + public static readonly ERROR_INVALID_HOMESERVER = AutoDiscoveryError.InvalidHomeserver; - public static readonly ERROR_INVALID_IS_BASE_URL = "Invalid base_url for m.identity_server"; + public static readonly ERROR_INVALID_IS_BASE_URL = AutoDiscoveryError.InvalidIsBaseUrl; - // eslint-disable-next-line - public static readonly ERROR_INVALID_IDENTITY_SERVER = "Identity server URL does not appear to be a valid identity server"; + public static readonly ERROR_INVALID_IDENTITY_SERVER = AutoDiscoveryError.InvalidIdentityServer; - public static readonly ERROR_INVALID_IS = "Invalid identity server discovery response"; + public static readonly ERROR_INVALID_IS = AutoDiscoveryError.InvalidIs; - public static readonly ERROR_MISSING_WELLKNOWN = "No .well-known JSON file found"; + public static readonly ERROR_MISSING_WELLKNOWN = AutoDiscoveryError.MissingWellknown; - public static readonly ERROR_INVALID_JSON = "Invalid JSON"; + public static readonly ERROR_INVALID_JSON = AutoDiscoveryError.InvalidJson; - public static readonly ALL_ERRORS = [ - AutoDiscovery.ERROR_INVALID, - AutoDiscovery.ERROR_GENERIC_FAILURE, - AutoDiscovery.ERROR_INVALID_HS_BASE_URL, - AutoDiscovery.ERROR_INVALID_HOMESERVER, - AutoDiscovery.ERROR_INVALID_IS_BASE_URL, - AutoDiscovery.ERROR_INVALID_IDENTITY_SERVER, - AutoDiscovery.ERROR_INVALID_IS, - AutoDiscovery.ERROR_MISSING_WELLKNOWN, - AutoDiscovery.ERROR_INVALID_JSON, - ]; + public static readonly ALL_ERRORS = Object.keys(AutoDiscoveryError); /** * The auto discovery failed. The client is expected to communicate @@ -120,13 +131,13 @@ export class AutoDiscovery { * configuration, which may include error states. Rejects on unexpected * failure, not when verification fails. */ - public static async fromDiscoveryConfig(wellknown: any): Promise { + public static async fromDiscoveryConfig(wellknown: any): Promise { // Step 1 is to get the config, which is provided to us here. // We default to an error state to make the first few checks easier to // write. We'll update the properties of this object over the duration // of this function. - const clientConfig = { + const clientConfig: ClientConfig = { "m.homeserver": { state: AutoDiscovery.FAIL_ERROR, error: AutoDiscovery.ERROR_INVALID, @@ -197,7 +208,7 @@ export class AutoDiscovery { if (wellknown["m.identity_server"]) { // We prepare a failing identity server response to save lines later // in this branch. - const failingClientConfig = { + const failingClientConfig: ClientConfig = { "m.homeserver": clientConfig["m.homeserver"], "m.identity_server": { state: AutoDiscovery.FAIL_PROMPT, @@ -279,7 +290,7 @@ export class AutoDiscovery { * configuration, which may include error states. Rejects on unexpected * failure, not when discovery fails. */ - public static async findClientConfig(domain: string): Promise { + public static async findClientConfig(domain: string): Promise { if (!domain || typeof(domain) !== "string" || domain.length === 0) { throw new Error("'domain' must be a string of non-zero length"); } @@ -298,7 +309,7 @@ export class AutoDiscovery { // We default to an error state to make the first few checks easier to // write. We'll update the properties of this object over the duration // of this function. - const clientConfig = { + const clientConfig: ClientConfig = { "m.homeserver": { state: AutoDiscovery.FAIL_ERROR, error: AutoDiscovery.ERROR_INVALID, @@ -367,18 +378,18 @@ export class AutoDiscovery { * @return {string|boolean} The sanitized URL or a falsey value if the URL is invalid. * @private */ - private static sanitizeWellKnownUrl(url: string): string | boolean { + private static sanitizeWellKnownUrl(url: string): string | false { if (!url) return false; try { - let parsed = null; + let parsed: URL | undefined; try { parsed = new URL(url); } catch (e) { logger.error("Could not parse url", e); } - if (!parsed || !parsed.hostname) return false; + if (!parsed?.hostname) return false; if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return false; const port = parsed.port ? `:${parsed.port}` : ""; @@ -448,12 +459,17 @@ export class AutoDiscovery { }; } } catch (err) { - const error = err as Error | string | undefined; + const error = err as AutoDiscoveryError | string | undefined; + let reason = ""; + if (typeof error === "object") { + reason = (error)?.message; + } + return { error, raw: {}, action: AutoDiscoveryAction.FAIL_PROMPT, - reason: (error)?.message || "General failure", + reason: reason || "General failure", }; } @@ -463,7 +479,7 @@ export class AutoDiscovery { action: AutoDiscoveryAction.SUCCESS, }; } catch (err) { - const error = err as Error | string | undefined; + const error = err as Error; return { error, raw: {}, diff --git a/src/client.ts b/src/client.ts index 7e37c22afaf..341b2cbecd7 100644 --- a/src/client.ts +++ b/src/client.ts @@ -507,7 +507,7 @@ export interface IUploadKeySignaturesResponse { } export interface IPreviewUrlResponse { - [key: string]: string | number; + [key: string]: undefined | string | number; "og:title": string; "og:type": string; "og:url": string; @@ -705,8 +705,9 @@ export interface IMyDevice { display_name?: string; last_seen_ip?: string; last_seen_ts?: number; - [UNSTABLE_MSC3852_LAST_SEEN_UA.stable]?: string; - [UNSTABLE_MSC3852_LAST_SEEN_UA.unstable]?: string; + // UNSTABLE_MSC3852_LAST_SEEN_UA + last_seen_user_agent?: string; + "org.matrix.msc3852.last_seen_user_agent"?: string; } export interface Keys { @@ -3121,7 +3122,7 @@ export class MatrixClient extends TypedEventEmitter { const privKey = decodeRecoveryKey(recoveryKey); - return this.restoreKeyBackup(privKey, targetRoomId, targetSessionId, backupInfo, opts); + return this.restoreKeyBackup(privKey, targetRoomId!, targetSessionId!, backupInfo, opts); } public async restoreKeyBackupWithCache( @@ -3283,7 +3284,7 @@ export class MatrixClient extends TypedEventEmitter { if (!threadId?.startsWith(EVENT_ID_PREFIX) && threadId !== null) { @@ -3787,10 +3788,10 @@ export class MatrixClient extends TypedEventEmitter { return ev.isRelation(THREAD_RELATION_TYPE.name) && !ev.status; })?.getId() ?? threadId, @@ -4097,7 +4098,7 @@ export class MatrixClient extends TypedEventEmitter { if (typeof threadId !== "string" && threadId !== null) { @@ -4314,7 +4315,7 @@ export class MatrixClient extends TypedEventEmitter { @@ -4358,7 +4359,7 @@ export class MatrixClient extends TypedEventEmitter { @@ -5226,11 +5227,11 @@ export class MatrixClient extends TypedEventEmitter { if (res.state) { - const roomState = eventTimeline.getState(dir); + const roomState = eventTimeline.getState(dir)!; const stateEvents = res.state.map(this.getEventMapper()); roomState.setUnknownStateEvents(stateEvents); } @@ -5758,7 +5759,7 @@ export class MatrixClient extends TypedEventEmitter { if (res.state) { - const roomState = eventTimeline.getState(dir); + const roomState = eventTimeline.getState(dir)!; const stateEvents = res.state.map(this.getEventMapper()); roomState.setUnknownStateEvents(stateEvents); } diff --git a/src/content-helpers.ts b/src/content-helpers.ts index f6763fb8c55..4dbb37b212a 100644 --- a/src/content-helpers.ts +++ b/src/content-helpers.ts @@ -118,12 +118,12 @@ export function makeEmoteMessage(body: string) { /** Location content helpers */ export const getTextForLocationEvent = ( - uri: string, + uri: string | undefined, assetType: LocationAssetType, - timestamp: number, - description?: string, + timestamp?: number, + description?: string | null, ): string => { - const date = `at ${new Date(timestamp).toISOString()}`; + const date = `at ${new Date(timestamp!).toISOString()}`; const assetName = assetType === LocationAssetType.Self ? 'User' : undefined; const quotedDescription = description ? `"${description}"` : undefined; @@ -147,10 +147,10 @@ export const getTextForLocationEvent = ( export const makeLocationContent = ( // this is first but optional // to avoid a breaking change - text: string | undefined, - uri: string, - timestamp: number, - description?: string, + text?: string, + uri?: string, + timestamp?: number, + description?: string | null, assetType?: LocationAssetType, ): LegacyLocationEventContent & MLocationEventContent => { const defaultedText = text ?? @@ -187,7 +187,7 @@ export const parseLocationEvent = (wireEventContent: LocationEventWireContent): const assetType = asset?.type ?? LocationAssetType.Self; const fallbackText = text ?? wireEventContent.body; - return makeLocationContent(fallbackText, geoUri, timestamp, description, assetType); + return makeLocationContent(fallbackText, geoUri, timestamp ?? undefined, description, assetType); }; /** @@ -201,7 +201,7 @@ export type MakeTopicContent = ( export const makeTopicContent: MakeTopicContent = (topic, htmlTopic) => { const renderings = [{ body: topic, mimetype: "text/plain" }]; if (isProvided(htmlTopic)) { - renderings.push({ body: htmlTopic, mimetype: "text/html" }); + renderings.push({ body: htmlTopic!, mimetype: "text/html" }); } return { topic, [M_TOPIC.name]: renderings }; }; @@ -247,14 +247,14 @@ export const makeBeaconInfoContent: MakeBeaconInfoContent = ( export type BeaconInfoState = MBeaconInfoContent & { assetType?: LocationAssetType; - timestamp: number; + timestamp?: number; }; /** * Flatten beacon info event content */ export const parseBeaconInfoContent = (content: MBeaconInfoEventContent): BeaconInfoState => { const { description, timeout, live } = content; - const timestamp = M_TIMESTAMP.findIn(content); + const timestamp = M_TIMESTAMP.findIn(content) ?? undefined; const asset = M_ASSET.findIn(content); return { @@ -290,14 +290,14 @@ export const makeBeaconContent: MakeBeaconContent = ( }, }); -export type BeaconLocationState = MLocationContent & { +export type BeaconLocationState = Omit & { uri?: string; // override from MLocationContent to allow optionals timestamp?: number; }; export const parseBeaconContent = (content: MBeaconEventContent): BeaconLocationState => { const location = M_LOCATION.findIn(content); - const timestamp = M_TIMESTAMP.findIn(content); + const timestamp = M_TIMESTAMP.findIn(content) ?? undefined; return { description: location?.description, diff --git a/src/crypto/backup.ts b/src/crypto/backup.ts index f57cfe29359..fd4377e148c 100644 --- a/src/crypto/backup.ts +++ b/src/crypto/backup.ts @@ -302,7 +302,7 @@ export class BackupManager { || now - this.sessionLastCheckAttemptedTime[targetSessionId!] > KEY_BACKUP_CHECK_RATE_LIMIT ) { this.sessionLastCheckAttemptedTime[targetSessionId!] = now; - await this.baseApis.restoreKeyBackupWithCache(targetRoomId, targetSessionId, this.backupInfo, {}); + await this.baseApis.restoreKeyBackupWithCache(targetRoomId!, targetSessionId!, this.backupInfo, {}); } } diff --git a/src/crypto/store/indexeddb-crypto-store-backend.ts b/src/crypto/store/indexeddb-crypto-store-backend.ts index faeb5c829a6..f525c0c959a 100644 --- a/src/crypto/store/indexeddb-crypto-store-backend.ts +++ b/src/crypto/store/indexeddb-crypto-store-backend.ts @@ -201,7 +201,7 @@ export class Backend implements CryptoStore { let stateIndex = 0; let result: OutgoingRoomKeyRequest; - function onsuccess(this: IDBRequest) { + function onsuccess(this: IDBRequest) { const cursor = this.result; if (cursor) { // got a match @@ -256,7 +256,7 @@ export class Backend implements CryptoStore { let stateIndex = 0; const results: OutgoingRoomKeyRequest[] = []; - function onsuccess(this: IDBRequest) { + function onsuccess(this: IDBRequest) { const cursor = this.result; if (cursor) { const keyReq = cursor.value; @@ -309,7 +309,7 @@ export class Backend implements CryptoStore { ): Promise { let result: OutgoingRoomKeyRequest | null = null; - function onsuccess(this: IDBRequest) { + function onsuccess(this: IDBRequest) { const cursor = this.result; if (!cursor) { return; diff --git a/src/http-api/errors.ts b/src/http-api/errors.ts index faa379784f4..be63c94fdf0 100644 --- a/src/http-api/errors.ts +++ b/src/http-api/errors.ts @@ -49,7 +49,7 @@ export class HTTPError extends Error { */ export class MatrixError extends HTTPError { public readonly errcode?: string; - public readonly data: IErrorJson; + public data: IErrorJson; constructor( errorJson: IErrorJson = {}, diff --git a/src/interactive-auth.ts b/src/interactive-auth.ts index ef3bc8bd891..5ba5956695e 100644 --- a/src/interactive-auth.ts +++ b/src/interactive-auth.ts @@ -21,6 +21,7 @@ limitations under the License. import { logger } from './logger'; import { MatrixClient } from "./client"; import { defer, IDeferred } from "./utils"; +import { MatrixError } from "./http-api"; const EMAIL_STAGE_TYPE = "m.login.email.identity"; const MSISDN_STAGE_TYPE = "m.login.msisdn"; @@ -111,7 +112,7 @@ interface IOpts { sessionId?: string; clientSecret?: string; emailSid?: string; - doRequest(auth: IAuthData, background: boolean): Promise; + doRequest(auth: IAuthData | null, background: boolean): Promise; stateUpdated(nextStage: AuthType, status: IStageStatus): void; requestEmailToken(email: string, secret: string, attempt: number, session: string): Promise<{ sid: string }>; busyChanged?(busy: boolean): void; @@ -328,7 +329,7 @@ export class InteractiveAuth { * @param {string} loginType login type for the stage * @return {object?} any parameters from the server for this stage */ - public getStageParams(loginType: string): Record { + public getStageParams(loginType: string): Record | undefined { return this.data.params?.[loginType]; } @@ -428,10 +429,10 @@ export class InteractiveAuth { this.requestingEmailToken = true; try { const requestTokenResult = await this.requestEmailTokenCallback( - this.inputs.emailAddress, + this.inputs.emailAddress!, this.clientSecret, this.emailAttempt++, - this.data.session, + this.data.session!, ); this.emailSid = requestTokenResult.sid; logger.trace("Email token request succeeded"); @@ -454,16 +455,16 @@ export class InteractiveAuth { * This can be set to true for requests that just poll to see if auth has * been completed elsewhere. */ - private async doRequest(auth: IAuthData, background = false): Promise { + private async doRequest(auth: IAuthData | null, background = false): Promise { try { const result = await this.requestCallback(auth, background); this.attemptAuthDeferred!.resolve(result); this.attemptAuthDeferred = null; } catch (error) { // sometimes UI auth errors don't come with flows - const errorFlows = error.data?.flows ?? null; + const errorFlows = (error).data?.flows ?? null; const haveFlows = this.data.flows || Boolean(errorFlows); - if (error.httpStatus !== 401 || !error.data || !haveFlows) { + if ((error).httpStatus !== 401 || !(error).data || !haveFlows) { // doesn't look like an interactive-auth failure. if (!background) { this.attemptAuthDeferred?.reject(error); @@ -474,20 +475,23 @@ export class InteractiveAuth { logger.log("Background poll request failed doing UI auth: ignoring", error); } } - if (!error.data) { - error.data = {}; + 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 // has not yet been validated). This appears to be a Synapse bug, which // we workaround here. - if (!error.data.flows && !error.data.completed && !error.data.session) { - error.data.flows = this.data.flows; - error.data.completed = this.data.completed; - error.data.session = this.data.session; + if (!(error).data.flows && + !(error).data.completed && + !(error).data.session + ) { + (error).data.flows = this.data.flows; + (error).data.completed = this.data.completed; + (error).data.session = this.data.session; } - this.data = error.data; + this.data = (error).data; try { this.startNextAuthStage(); } catch (e) { diff --git a/src/models/MSC3089Branch.ts b/src/models/MSC3089Branch.ts index e8c5af14acb..25ce51a20a1 100644 --- a/src/models/MSC3089Branch.ts +++ b/src/models/MSC3089Branch.ts @@ -145,7 +145,7 @@ export class MSC3089Branch { let event: MatrixEvent | undefined = room.getUnfilteredTimelineSet().findEventById(this.id); // keep scrolling back if needed until we find the event or reach the start of the room: - while (!event && room.getLiveTimeline().getState(EventTimeline.BACKWARDS).paginationToken) { + while (!event && room.getLiveTimeline().getState(EventTimeline.BACKWARDS)!.paginationToken) { await this.client.scrollback(room, 100); event = room.getUnfilteredTimelineSet().findEventById(this.id); } diff --git a/src/models/beacon.ts b/src/models/beacon.ts index 0ec955ffaa2..79876c469f9 100644 --- a/src/models/beacon.ts +++ b/src/models/beacon.ts @@ -132,19 +132,19 @@ export class Beacon extends TypedEventEmitter 1) { this.livenessWatchTimeout = setTimeout( () => { this.monitorLiveness(); }, expiryInMs, ); } - } else if (this.beaconInfo.timestamp > Date.now()) { + } else if (this.beaconInfo.timestamp! > Date.now()) { // beacon start timestamp is in the future // check liveness again then this.livenessWatchTimeout = setTimeout( () => { this.monitorLiveness(); }, - this.beaconInfo.timestamp - Date.now(), + this.beaconInfo.timestamp! - Date.now(), ); } } @@ -165,6 +165,7 @@ export class Beacon extends TypedEventEmitter Date.now() ? - this.beaconInfo.timestamp - 360000 /* 6min */ : + const startTimestamp = this.beaconInfo.timestamp! > Date.now() ? + this.beaconInfo.timestamp! - 360000 /* 6min */ : this.beaconInfo.timestamp; - this._isLive = !!this._beaconInfo?.live && + this._isLive = !!this._beaconInfo?.live && !!startTimestamp && isTimestampInDuration(startTimestamp, this._beaconInfo?.timeout, Date.now()); if (prevLiveness !== this.isLive) { diff --git a/src/models/event-timeline-set.ts b/src/models/event-timeline-set.ts index abf20ebf8bf..e41eec15881 100644 --- a/src/models/event-timeline-set.ts +++ b/src/models/event-timeline-set.ts @@ -625,7 +625,7 @@ export class EventTimelineSet extends TypedEventEmitter | null> = { @@ -126,10 +126,12 @@ export class EventTimeline { */ constructor(private readonly eventTimelineSet: EventTimelineSet) { this.roomId = eventTimelineSet.room?.roomId ?? null; - this.startState = new RoomState(this.roomId); - this.startState.paginationToken = null; - this.endState = new RoomState(this.roomId); - this.endState.paginationToken = null; + if (this.roomId) { + this.startState = new RoomState(this.roomId); + this.startState.paginationToken = null; + this.endState = new RoomState(this.roomId); + this.endState.paginationToken = null; + } // this is used by client.js this.paginationRequests = { 'b': null, 'f': null }; @@ -167,12 +169,8 @@ export class EventTimeline { Object.freeze(e); } - this.startState.setStateEvents(stateEvents, { - timelineWasEmpty, - }); - this.endState.setStateEvents(stateEvents, { - timelineWasEmpty, - }); + this.startState?.setStateEvents(stateEvents, { timelineWasEmpty }); + this.endState?.setStateEvents(stateEvents, { timelineWasEmpty }); } /** @@ -190,7 +188,7 @@ export class EventTimeline { public forkLive(direction: Direction): EventTimeline { const forkState = this.getState(direction); const timeline = new EventTimeline(this.eventTimelineSet); - timeline.startState = forkState.clone(); + timeline.startState = forkState?.clone(); // Now clobber the end state of the new live timeline with that from the // previous live timeline. It will be identical except that we'll keep // using the same RoomMember objects for the 'live' set of members with any @@ -198,7 +196,7 @@ export class EventTimeline { timeline.endState = forkState; // Firstly, we just stole the current timeline's end state, so it needs a new one. // Make an immutable copy of the state so back pagination will get the correct sentinels. - this.endState = forkState.clone(); + this.endState = forkState?.clone(); return timeline; } @@ -214,8 +212,8 @@ export class EventTimeline { public fork(direction: Direction): EventTimeline { const forkState = this.getState(direction); const timeline = new EventTimeline(this.eventTimelineSet); - timeline.startState = forkState.clone(); - timeline.endState = forkState.clone(); + timeline.startState = forkState?.clone(); + timeline.endState = forkState?.clone(); return timeline; } @@ -276,7 +274,7 @@ export class EventTimeline { * * @return {RoomState} state at the start/end of the timeline */ - public getState(direction: Direction): RoomState { + public getState(direction: Direction): RoomState | undefined { if (direction == EventTimeline.BACKWARDS) { return this.startState; } else if (direction == EventTimeline.FORWARDS) { @@ -296,7 +294,7 @@ export class EventTimeline { * @return {?string} pagination token */ public getPaginationToken(direction: Direction): string | null { - return this.getState(direction).paginationToken; + return this.getState(direction)?.paginationToken ?? null; } /** @@ -304,12 +302,15 @@ export class EventTimeline { * * @param {?string} token pagination token * - * @param {string} direction EventTimeline.BACKWARDS to set the paginatio + * @param {string} direction EventTimeline.BACKWARDS to set the pagination * token for going backwards in time; EventTimeline.FORWARDS to set the * pagination token for going forwards in time. */ public setPaginationToken(token: string | null, direction: Direction): void { - this.getState(direction).paginationToken = token; + const state = this.getState(direction); + if (state) { + state.paginationToken = token; + } } /** @@ -408,16 +409,14 @@ export class EventTimeline { const timelineSet = this.getTimelineSet(); if (timelineSet.room) { - EventTimeline.setEventMetadata(event, roomState, toStartOfTimeline); + EventTimeline.setEventMetadata(event, roomState!, toStartOfTimeline); // modify state but only on unfiltered timelineSets if ( event.isState() && timelineSet.room.getUnfilteredTimelineSet() === timelineSet ) { - roomState.setStateEvents([event], { - timelineWasEmpty, - }); + roomState?.setStateEvents([event], { timelineWasEmpty }); // it is possible that the act of setting the state event means we // can set more metadata (specifically sender/target props), so try // it again if the prop wasn't previously set. It may also mean that @@ -428,8 +427,8 @@ export class EventTimeline { // back in time, else we'll set the .sender value for BEFORE the given // member event, whereas we want to set the .sender value for the ACTUAL // member event itself. - if (!event.sender || (event.getType() === "m.room.member" && !toStartOfTimeline)) { - EventTimeline.setEventMetadata(event, roomState, toStartOfTimeline); + if (!event.sender || (event.getType() === EventType.RoomMember && !toStartOfTimeline)) { + EventTimeline.setEventMetadata(event, roomState!, toStartOfTimeline); } } } diff --git a/src/models/event.ts b/src/models/event.ts index 6fbe6a391ba..6ba8a65110e 100644 --- a/src/models/event.ts +++ b/src/models/event.ts @@ -413,7 +413,7 @@ export class MatrixEvent extends TypedEventEmitter1433502692297 */ public getTs(): number { - return this.event.origin_server_ts; + return this.event.origin_server_ts!; } /** @@ -625,8 +625,8 @@ export class MatrixEvent extends TypedEventEmitter { // state at the start and end of that timeline. These are more // for backwards-compatibility than anything else. this.timeline = this.getLiveTimeline().getEvents(); - this.oldState = this.getLiveTimeline() - .getState(EventTimeline.BACKWARDS); - this.currentState = this.getLiveTimeline() - .getState(EventTimeline.FORWARDS); + this.oldState = this.getLiveTimeline().getState(EventTimeline.BACKWARDS)!; + this.currentState = this.getLiveTimeline().getState(EventTimeline.FORWARDS)!; // Let people know to register new listeners for the new state // references. The reference won't necessarily change every time so only @@ -1564,8 +1562,8 @@ export class Room extends ReadReceipt { pendingEvents = true, }: ICreateFilterOpts = {}, ): EventTimelineSet { - if (this.filteredTimelineSets[filter.filterId]) { - return this.filteredTimelineSets[filter.filterId]; + if (this.filteredTimelineSets[filter.filterId!]) { + return this.filteredTimelineSets[filter.filterId!]; } const opts = Object.assign({ filter, pendingEvents }, this.opts); const timelineSet = new EventTimelineSet(this, opts); @@ -1574,7 +1572,7 @@ export class Room extends ReadReceipt { RoomEvent.TimelineReset, ]); if (useSyncEvents) { - this.filteredTimelineSets[filter.filterId] = timelineSet; + this.filteredTimelineSets[filter.filterId!] = timelineSet; this.timelineSets.push(timelineSet); } @@ -1623,7 +1621,7 @@ export class Room extends ReadReceipt { } private async getThreadListFilter(filterType = ThreadFilterType.All): Promise { - const myUserId = this.client.getUserId(); + const myUserId = this.client.getUserId()!; const filter = new Filter(myUserId); const definition: IFilterDefinition = { @@ -1635,7 +1633,7 @@ export class Room extends ReadReceipt { }; if (filterType === ThreadFilterType.My) { - definition.room.timeline[FILTER_RELATED_BY_SENDERS.name] = [myUserId]; + definition!.room!.timeline![FILTER_RELATED_BY_SENDERS.name] = [myUserId]; } filter.setDefinition(definition); @@ -1681,7 +1679,7 @@ export class Room extends ReadReceipt { return event.getSender() === this.client.getUserId(); }); if (filterType !== ThreadFilterType.My || currentUserParticipated) { - timelineSet.getLiveTimeline().addEvent(thread.rootEvent, { + timelineSet.getLiveTimeline().addEvent(thread.rootEvent!, { toStartOfTimeline: false, }); } @@ -1851,8 +1849,8 @@ export class Room extends ReadReceipt { * @param {Filter} filter the filter whose timelineSet is to be forgotten */ public removeFilteredTimelineSet(filter: Filter): void { - const timelineSet = this.filteredTimelineSets[filter.filterId]; - delete this.filteredTimelineSets[filter.filterId]; + const timelineSet = this.filteredTimelineSets[filter.filterId!]; + delete this.filteredTimelineSets[filter.filterId!]; const i = this.timelineSets.indexOf(timelineSet); if (i > -1) { this.timelineSets.splice(i, 1); @@ -2187,7 +2185,7 @@ export class Room extends ReadReceipt { // call setEventMetadata to set up event.sender etc // as event is shared over all timelineSets, we set up its metadata based // on the unfiltered timelineSet. - EventTimeline.setEventMetadata(event, this.getLiveTimeline().getState(EventTimeline.FORWARDS), false); + EventTimeline.setEventMetadata(event, this.getLiveTimeline().getState(EventTimeline.FORWARDS)!, false); this.txnToEvent[txnId] = event; if (this.pendingEventList) { diff --git a/src/models/thread.ts b/src/models/thread.ts index 117100d6923..0e3b6c3b51c 100644 --- a/src/models/thread.ts +++ b/src/models/thread.ts @@ -200,7 +200,7 @@ export class Thread extends ReadReceipt { }; public get roomState(): RoomState { - return this.room.getLiveTimeline().getState(EventTimeline.FORWARDS); + return this.room.getLiveTimeline().getState(EventTimeline.FORWARDS)!; } private addEventToTimeline(event: MatrixEvent, toStartOfTimeline: boolean): void { diff --git a/src/pushprocessor.ts b/src/pushprocessor.ts index 2976417fdc1..e979032d89b 100644 --- a/src/pushprocessor.ts +++ b/src/pushprocessor.ts @@ -219,8 +219,11 @@ export class PushProcessor { return null; } - private templateRuleToRaw(kind: PushRuleKind, tprule: any): any { - const rawrule = { + private templateRuleToRaw( + kind: PushRuleKind, + tprule: IPushRule, + ): Pick | null { + const rawrule: Pick = { 'rule_id': tprule.rule_id, 'actions': tprule.actions, 'conditions': [], @@ -234,7 +237,7 @@ export class PushProcessor { if (!tprule.rule_id) { return null; } - rawrule.conditions.push({ + rawrule.conditions!.push({ 'kind': ConditionKind.EventMatch, 'key': 'room_id', 'value': tprule.rule_id, @@ -244,7 +247,7 @@ export class PushProcessor { if (!tprule.rule_id) { return null; } - rawrule.conditions.push({ + rawrule.conditions!.push({ 'kind': ConditionKind.EventMatch, 'key': 'user_id', 'value': tprule.rule_id, @@ -254,7 +257,7 @@ export class PushProcessor { if (!tprule.pattern) { return null; } - rawrule.conditions.push({ + rawrule.conditions!.push({ 'kind': ConditionKind.EventMatch, 'key': 'content.body', 'pattern': tprule.pattern, @@ -474,7 +477,7 @@ export class PushProcessor { return actionObj; } - public ruleMatchesEvent(rule: IPushRule, ev: MatrixEvent): boolean { + public ruleMatchesEvent(rule: Partial & Pick, ev: MatrixEvent): boolean { if (!rule.conditions?.length) return true; let ret = true; diff --git a/src/webrtc/call.ts b/src/webrtc/call.ts index b495dfec659..93f5e18a89f 100644 --- a/src/webrtc/call.ts +++ b/src/webrtc/call.ts @@ -68,7 +68,7 @@ import { MatrixError } from "../http-api"; */ interface CallOpts { - roomId?: string; + roomId: string; client: MatrixClient; forceTURN?: boolean; turnServers?: Array; @@ -278,7 +278,7 @@ export class MatrixCall extends TypedEventEmitter; // A queue for candidates waiting to go out. // We try to amalgamate candidates into a single candidate message where @@ -291,7 +291,7 @@ export class MatrixCall extends TypedEventEmitter = []; private screensharingSenders: Array = []; private inviteOrAnswerSent = false; - private waitForLocalAVStream: boolean; + private waitForLocalAVStream?: boolean; private successor?: MatrixCall; private opponentMember?: RoomMember; private opponentVersion?: number | string; @@ -312,7 +312,7 @@ export class MatrixCall extends TypedEventEmitter { stats.push(item); }); @@ -797,9 +797,9 @@ export class MatrixCall extends TypedEventEmitter