From 17efcad6fe393304228f3746eca3009e77e4f39a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 10 Jul 2021 15:43:47 +0100 Subject: [PATCH 1/6] Improve and consolidate typing --- src/@types/partials.ts | 7 + src/@types/requests.ts | 29 +- src/@types/search.ts | 118 +++++ src/@types/spaces.ts | 40 ++ src/@types/synapse.ts | 40 ++ src/client.ts | 921 ++++++++++++++++++++------------- src/content-helpers.ts | 14 +- src/crypto/EncryptionSetup.ts | 7 +- src/crypto/RoomList.ts | 6 +- src/crypto/SecretStorage.ts | 10 +- src/crypto/algorithms/base.ts | 3 +- src/crypto/backup.ts | 37 +- src/crypto/dehydration.ts | 10 +- src/crypto/index.ts | 41 +- src/crypto/keybackup.ts | 1 + src/crypto/olmlib.ts | 6 +- src/filter.ts | 4 +- src/models/MSC3089Branch.ts | 4 +- src/models/MSC3089TreeSpace.ts | 6 +- src/models/room.ts | 9 +- src/models/search-result.js | 60 --- src/models/search-result.ts | 60 +++ src/store/index.ts | 11 +- src/store/memory.ts | 2 +- src/store/stub.ts | 3 +- src/sync.ts | 2 +- 26 files changed, 953 insertions(+), 498 deletions(-) create mode 100644 src/@types/search.ts create mode 100644 src/@types/spaces.ts create mode 100644 src/@types/synapse.ts delete mode 100644 src/models/search-result.js create mode 100644 src/models/search-result.ts diff --git a/src/@types/partials.ts b/src/@types/partials.ts index ecdc6525a..0d82c0bc0 100644 --- a/src/@types/partials.ts +++ b/src/@types/partials.ts @@ -39,3 +39,10 @@ export enum Preset { } export type ResizeMethod = "crop" | "scale"; + +// TODO move to http-api after TSification +export interface IAbortablePromise extends Promise { + abort(): void; +} + +export type IdServerUnbindResult = "no-support" | "success"; diff --git a/src/@types/requests.ts b/src/@types/requests.ts index eaf682831..e31618193 100644 --- a/src/@types/requests.ts +++ b/src/@types/requests.ts @@ -16,6 +16,8 @@ limitations under the License. import { Callback } from "../client"; import { Preset, Visibility } from "./partials"; +import { SearchKey } from "./search"; +import { IRoomEventFilter } from "../filter"; // allow camelcase as these are things go onto the wire /* eslint-disable camelcase */ @@ -63,12 +65,12 @@ export interface IGuestAccessOpts { } export interface ISearchOpts { - keys?: string[]; + keys?: SearchKey[]; query: string; } export interface IEventSearchOpts { - filter: any; // TODO: Types + filter?: IRoomEventFilter; term: string; } @@ -104,9 +106,11 @@ export interface IRoomDirectoryOptions { server?: string; limit?: number; since?: string; - - // TODO: Proper types - filter?: any & {generic_search_term: string}; + filter?: { + generic_search_term: string; + }; + include_all_networks?: boolean; + third_party_instance_id?: string; } export interface IUploadOpts { @@ -119,4 +123,19 @@ export interface IUploadOpts { progressHandler?: (state: {loaded: number, total: number}) => void; } +export interface IAddThreePidOnlyBody { + auth?: { + type: string; + session?: string; + }; + client_secret: string; + sid: string; +} + +export interface IBindThreePidBody { + client_secret: string; + id_server: string; + id_access_token: string; + sid: string; +} /* eslint-enable camelcase */ diff --git a/src/@types/search.ts b/src/@types/search.ts new file mode 100644 index 000000000..f1af733cd --- /dev/null +++ b/src/@types/search.ts @@ -0,0 +1,118 @@ +/* +Copyright 2021 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Types relating to the /search API + +import { IRoomEvent, IStateEvent } from "../sync-accumulator"; +import { IRoomEventFilter } from "../filter"; +import { SearchResult } from "../models/search-result"; + +/* eslint-disable camelcase */ +export interface IEventWithRoomId extends IRoomEvent { + room_id: string; +} + +export interface IStateEventWithRoomId extends IStateEvent { + room_id: string; +} + +export interface IMatrixProfile { + avatar_url?: string; + displayname?: string; +} + +export interface IResultContext { + events_before: IEventWithRoomId[]; + events_after: IEventWithRoomId[]; + profile_info: Record; + start?: string; + end?: string; +} + +export interface ISearchResult { + rank: number; + result: IEventWithRoomId; + context: IResultContext; +} + +enum GroupKey { + RoomId = "room_id", + Sender = "sender", +} + +export interface IResultRoomEvents { + count: number; + highlights: string[]; + results: ISearchResult[]; + state?: { [roomId: string]: IStateEventWithRoomId[] }; + groups?: { + [groupKey in GroupKey]: { + [value: string]: { + next_batch?: string; + order: number; + results: string[]; + } + }; + } + next_batch?: string; +} + +interface IResultCategories { + room_events: IResultRoomEvents; +} + +export type SearchKey = "content.body" | "content.name" | "content.topic"; + +export enum SearchOrderBy { + Recent = "recent", + Rank = "rank", +} + +export interface ISearchRequestBody { + search_categories: { + room_events: { + search_term: string; + keys?: SearchKey[]; + filter?: IRoomEventFilter; + order_by?: SearchOrderBy; + event_context?: { + before_limit?: number; + after_limit?: number; + include_profile?: boolean; + }; + include_state?: boolean; + groupings?: { + group_by: { + key: GroupKey; + }[]; + } + }; + }; +} + +export interface ISearchResponse { + search_categories: IResultCategories; +} + +export interface ISearchResults { + _query?: ISearchRequestBody; + results: SearchResult[]; + highlights: string[]; + count?: number; + next_batch?: string; + pendingRequest?: Promise; +} +/* eslint-enable camelcase */ diff --git a/src/@types/spaces.ts b/src/@types/spaces.ts new file mode 100644 index 000000000..17e677d44 --- /dev/null +++ b/src/@types/spaces.ts @@ -0,0 +1,40 @@ +/* +Copyright 2021 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { IPublicRoomsChunk } from "../client"; + +// Types relating to Rooms of type `m.space` and related APIs + +/* eslint-disable camelcase */ +export interface ISpaceSummaryRoom extends IPublicRoomsChunk { + num_refs: number; + room_type: string; +} + +export interface ISpaceSummaryEvent { + room_id: string; + event_id: string; + origin_server_ts: number; + type: string; + state_key: string; + content: { + order?: string; + suggested?: boolean; + auto_join?: boolean; + via?: string[]; + }; +} +/* eslint-enable camelcase */ diff --git a/src/@types/synapse.ts b/src/@types/synapse.ts new file mode 100644 index 000000000..1d4ce41ac --- /dev/null +++ b/src/@types/synapse.ts @@ -0,0 +1,40 @@ +/* +Copyright 2021 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { IdServerUnbindResult } from "./partials"; + +// Types relating to Synapse Admin APIs + +/* eslint-disable camelcase */ +export interface ISynapseAdminWhoisResponse { + user_id: string; + devices: { + [deviceId: string]: { + sessions: { + connections: { + ip: string; + last_seen: number; // millis since epoch + user_agent: string; + }[]; + }[]; + }; + }; +} + +export interface ISynapseAdminDeactivateResponse { + id_server_unbind_result: IdServerUnbindResult; +} +/* eslint-enable camelcase */ diff --git a/src/client.ts b/src/client.ts index e5424224c..516a3b0dd 100644 --- a/src/client.ts +++ b/src/client.ts @@ -20,22 +20,22 @@ limitations under the License. */ import { EventEmitter } from "events"; -import { SyncApi } from "./sync"; -import { EventStatus, IDecryptOptions, MatrixEvent } from "./models/event"; +import { ISyncStateData, SyncApi } from "./sync"; +import { EventStatus, IContent, IDecryptOptions, MatrixEvent } from "./models/event"; import { StubStore } from "./store/stub"; import { createNewMatrixCall, MatrixCall } from "./webrtc/call"; -import { Filter } from "./filter"; +import { Filter, IFilterDefinition } from "./filter"; import { CallEventHandler } from './webrtc/callEventHandler'; import * as utils from './utils'; import { sleep } from './utils'; import { Group } from "./models/group"; -import { EventTimeline } from "./models/event-timeline"; +import { Direction, EventTimeline } from "./models/event-timeline"; import { PushAction, PushProcessor } from "./pushprocessor"; import { AutoDiscovery } from "./autodiscovery"; import * as olmlib from "./crypto/olmlib"; import { decodeBase64, encodeBase64 } from "./crypto/olmlib"; import { ReEmitter } from './ReEmitter'; -import { RoomList } from './crypto/RoomList'; +import { IRoomEncryption, RoomList } from './crypto/RoomList'; import { logger } from './logger'; import { SERVICE_TYPES } from './service-types'; import { @@ -47,36 +47,39 @@ import { PREFIX_UNSTABLE, retryNetworkOperation, } from "./http-api"; -import { Crypto, fixBackupKey, IBootstrapCrossSigningOpts, IMegolmSessionData, isCryptoAvailable } from './crypto'; +import { + Crypto, + fixBackupKey, + IBootstrapCrossSigningOpts, + ICheckOwnCrossSigningTrustOpts, + IMegolmSessionData, + isCryptoAvailable, + VerificationMethod, +} from './crypto'; import { DeviceInfo, IDevice } from "./crypto/deviceinfo"; import { decodeRecoveryKey } from './crypto/recoverykey'; import { keyFromAuthData } from './crypto/key_passphrase'; import { User } from "./models/user"; import { getHttpUriForMxc } from "./content-repo"; import { SearchResult } from "./models/search-result"; -import { DEHYDRATION_ALGORITHM, IDehydratedDevice, IDehydratedDeviceKeyInfo } from "./crypto/dehydration"; import { + DEHYDRATION_ALGORITHM, + IDehydratedDevice, + IDehydratedDeviceKeyInfo, + IDeviceKeys, + IOneTimeKey, +} from "./crypto/dehydration"; +import { + IKeyBackupInfo, IKeyBackupPrepareOpts, IKeyBackupRestoreOpts, IKeyBackupRestoreResult, - IKeyBackupInfo, + IKeyBackupSession, } from "./crypto/keybackup"; import { IIdentityServerProvider } from "./@types/IIdentityServerProvider"; import type Request from "request"; import { MatrixScheduler } from "./scheduler"; -import { ICryptoCallbacks, NotificationCountType } from "./matrix"; -import { ISecretStorageKeyInfo } from "./crypto/api"; -import { MemoryCryptoStore } from "./crypto/store/memory-crypto-store"; -import { LocalStorageCryptoStore } from "./crypto/store/localStorage-crypto-store"; -import { IndexedDBCryptoStore } from "./crypto/store/indexeddb-crypto-store"; -import { MemoryStore } from "./store/memory"; -import { LocalIndexedDBStoreBackend } from "./store/indexeddb-local-backend"; -import { RemoteIndexedDBStoreBackend } from "./store/indexeddb-remote-backend"; -import { SyncState } from "./sync.api"; -import { EventTimelineSet } from "./models/event-timeline-set"; -import { VerificationRequest } from "./crypto/verification/request/VerificationRequest"; -import { Base as Verification } from "./crypto/verification/Base"; -import * as ContentHelpers from "./content-helpers"; +import { ICryptoCallbacks, IMinimalEvent, IRoomEvent, IStateEvent, NotificationCountType } from "./matrix"; import { CrossSigningKey, IAddSecretStorageKeyOpts, @@ -84,10 +87,21 @@ import { IEncryptedEventInfo, IImportRoomKeysOpts, IRecoveryKey, + ISecretStorageKeyInfo, } from "./crypto/api"; -import { CrossSigningInfo, DeviceTrustLevel, UserTrustLevel } from "./crypto/CrossSigning"; +import { MemoryCryptoStore } from "./crypto/store/memory-crypto-store"; +import { LocalStorageCryptoStore } from "./crypto/store/localStorage-crypto-store"; +import { IndexedDBCryptoStore } from "./crypto/store/indexeddb-crypto-store"; +import { SyncState } from "./sync.api"; +import { EventTimelineSet } from "./models/event-timeline-set"; +import { VerificationRequest } from "./crypto/verification/request/VerificationRequest"; +import { Base 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 { + IAddThreePidOnlyBody, + IBindThreePidBody, ICreateRoomOpts, IEventSearchOpts, IGuestAccessOpts, @@ -102,23 +116,37 @@ import { } from "./@types/requests"; import { EventType, + MsgType, + RelationType, RoomCreateTypeField, RoomType, UNSTABLE_MSC3088_ENABLED, UNSTABLE_MSC3088_PURPOSE, UNSTABLE_MSC3089_TREE_SUBTYPE, } from "./@types/event"; -import { IImageInfo, Preset } from "./@types/partials"; +import { IAbortablePromise, IdServerUnbindResult, IImageInfo, Preset, Visibility } from "./@types/partials"; import { EventMapper, eventMapperFor, MapperOpts } from "./event-mapper"; import url from "url"; import { randomString } from "./randomstring"; import { ReadStream } from "fs"; import { WebStorageSessionStore } from "./store/session/webstorage"; -import { BackupManager, IKeyBackupCheck, IPreparedKeyBackupVersion, TrustInfo } from "./crypto/backup"; +import { BackupManager, IKeyBackup, IKeyBackupCheck, IPreparedKeyBackupVersion, TrustInfo } from "./crypto/backup"; import { DEFAULT_TREE_POWER_LEVELS_TEMPLATE, MSC3089TreeSpace } from "./models/MSC3089TreeSpace"; import { ISignatures } from "./@types/signed"; +import { IStore } from "./store"; +import { ISecretRequest } from "./crypto/SecretStorage"; +import { + IEventWithRoomId, + ISearchRequestBody, + ISearchResponse, + ISearchResults, + IStateEventWithRoomId, + SearchOrderBy, +} from "./@types/search"; +import { ISynapseAdminDeactivateResponse, ISynapseAdminWhoisResponse } from "./@types/synapse"; +import { ISpaceSummaryEvent, ISpaceSummaryRoom } from "./@types/spaces"; -export type Store = StubStore | MemoryStore | LocalIndexedDBStoreBackend | RemoteIndexedDBStoreBackend; +export type Store = IStore; export type SessionStore = WebStorageSessionStore; export type CryptoStore = MemoryCryptoStore | LocalStorageCryptoStore | IndexedDBCryptoStore; @@ -132,7 +160,7 @@ const TURN_CHECK_INTERVAL = 10 * 60 * 1000; // poll for turn credentials every 1 interface IOlmDevice { pickledAccount: string; - sessions: Array>; + sessions: Array>; pickleKey: string; } @@ -261,7 +289,7 @@ export interface ICreateClientOpts { */ unstableClientRelationAggregation?: boolean; - verificationMethods?: Array; + verificationMethods?: Array; /** * Whether relaying calls through a TURN server should be forced. Default false. @@ -404,7 +432,6 @@ export interface ISignedKey { algorithms: string[]; device_id: string; } -/* eslint-enable camelcase */ export type KeySignatures = Record>; interface IUploadKeySignaturesResponse { @@ -427,6 +454,233 @@ export interface IPreviewUrlResponse { "matrix:image:size"?: number; } +interface ITurnServerResponse { + uris: string[]; + username: string; + password: string; + ttl: number; +} + +interface ITurnServer { + urls: string[]; + username: string; + credential: string; +} + +interface IServerVersions { + versions: string; + unstable_features: Record; +} + +interface IClientWellKnown { + [key: string]: any; + "m.homeserver": { + base_url: string; + }; + "m.identity_server"?: { + base_url: string; + }; +} + +interface IKeyBackupPath { + path: string; + queryData?: { + version: string; + }; +} + +interface IMediaConfig { + "m.upload.size"?: number; +} + +interface IThirdPartySigned { + sender: string; + mxid: string; + token: string; + signatures: ISignatures; +} + +interface IJoinRequestBody { + third_party_signed?: IThirdPartySigned; +} + +interface ITagMetadata { + [key: string]: any; + order: number; +} + +interface IMessagesResponse { + start: string; + end: string; + chunk: IRoomEvent[]; + state: IStateEvent[]; +} + +interface IRequestTokenResponse { + sid: string; + submit_url?: string; +} + +interface IRequestMsisdnTokenResponse extends IRequestTokenResponse { + msisdn: string; + success: boolean; + intl_fmt: string; +} + +interface IUploadKeysRequest { + device_keys?: Required; + one_time_keys?: { + [userId: string]: { + [deviceId: string]: number; + }; + }; + "org.matrix.msc2732.fallback_keys"?: Record; +} + +interface IOpenIDToken { + access_token: string; + token_type: "Bearer" | string; + matrix_server_name: string; + expires_in: number; +} + +interface IRoomInitialSyncResponse { + room_id: string; + membership: "invite" | "join" | "leave" | "ban", + messages?: { + start?: string; + end?: string; + chunk: IEventWithRoomId[]; + }; + state?: IStateEventWithRoomId[]; + visibility: Visibility; + account_data?: IMinimalEvent[]; + presence: any; // undocumented +} + +interface IJoinedMembersResponse { + joined: { + [userId: string]: { + display_name: string; + avatar_url: string; + }; + }; +} + +export interface IPublicRoomsChunk { + room_id: string; + name?: string; + avatar_url?: string; + topic?: string; + canonical_alias?: string; + aliases?: string[]; + world_readable: boolean; + guest_can_join: boolean; + num_joined_members: number; +} + +interface IPublicRoomsResponse { + chunk: IPublicRoomsChunk[]; + next_batch?: string; + prev_batch?: string; + total_room_count_estimate?: number; +} + +interface IUserDirectoryResponse { + results: { + user_id: string; + display_name?: string; + avatar_url?: string; + }[]; + limited: boolean; +} + +interface IThreepid { + medium: "email" | "msisdn"; + address: string; + validated_at: number; + added_at: number; +} + +interface IMyDevice { + device_id: string; + display_name?: string; + last_seen_ip?: string; + last_seen_ts?: number; +} + +interface IPusher { + pushkey: string; + kind: string; + app_id: string; + app_display_name: string; + device_display_name: string; + profile_tag?: string; + lang: string; + data: { + url?: string; + format?: string; + brand?: string; // undocumented + }; +} + +interface IDownloadKeyResult { + failures: { [serverName: string]: object }; + device_keys: { + [userId: string]: { + [deviceId: string]: IDeviceKeys & { + unsigned?: { + device_display_name: string; + }; + }; + } + }; +} + +interface IClaimOTKsResult { + failures: { [serverName: string]: object }; + one_time_keys: { + [userId: string]: { + [deviceId: string]: string; + }; + }; +} + +export interface IFieldType { + regexp: string; + placeholder: string; +} + +export interface IInstance { + desc: string; + icon?: string; + fields: object; + network_id: string; + // XXX: this is undocumented but we rely on it. + instance_id: string; +} + +export interface IProtocol { + user_fields: string[]; + location_fields: string[]; + icon: string; + field_types: Record; + instances: IInstance[]; +} + +interface IThirdPartyLocation { + alias: string; + protocol: string; + fields: object; +} + +interface IThirdPartyUser { + userid: string; + protocol: string; + fields: object; +} +/* eslint-enable camelcase */ + /** * Represents a Matrix Client. Only directly construct this if you want to use * custom modules. Normally, {@link createClient} should be used @@ -445,7 +699,7 @@ export class MatrixClient extends EventEmitter { public scheduler: MatrixScheduler; public clientRunning = false; public timelineSupport = false; - public urlPreviewCache: { [key: string]: Promise } = {}; // TODO: Types + public urlPreviewCache: { [key: string]: Promise } = {}; public unstableClientRelationAggregation = false; public identityServer: IIdentityServerProvider; public sessionStore: SessionStore; // XXX: Intended private, used in code. @@ -465,10 +719,10 @@ export class MatrixClient extends EventEmitter { protected canSupportVoip = false; protected peekSync: SyncApi = null; protected isGuestAccount = false; - protected ongoingScrollbacks: {[roomId: string]: {promise?: Promise, errorTs?: number}} = {}; // TODO: Types + protected ongoingScrollbacks: {[roomId: string]: {promise?: Promise, errorTs?: number}} = {}; protected notifTimelineSet: EventTimelineSet = null; protected cryptoStore: CryptoStore; - protected verificationMethods: string[]; + protected verificationMethods: VerificationMethod[]; protected fallbackICEServerAllowed = false; protected roomList: RoomList; protected syncApi: SyncApi; @@ -476,7 +730,7 @@ export class MatrixClient extends EventEmitter { protected syncLeftRoomsPromise: Promise; protected syncedLeftRooms = false; protected clientOpts: IStoredClientOpts; - protected clientWellKnownIntervalID: number; + protected clientWellKnownIntervalID: NodeJS.Timeout; protected canResetTimelineCallback: ResetTimelineCallback; // The pushprocessor caches useful things, so keep one and re-use it @@ -484,17 +738,17 @@ export class MatrixClient extends EventEmitter { // Promise to a response of the server's /versions response // TODO: This should expire: https://github.com/matrix-org/matrix-js-sdk/issues/1020 - protected serverVersionsPromise: Promise; + protected serverVersionsPromise: Promise; protected cachedCapabilities: { capabilities: ICapabilities; expiration: number; }; - protected clientWellKnown: any; - protected clientWellKnownPromise: Promise; - protected turnServers: any[] = []; // TODO: Types + protected clientWellKnown: IClientWellKnown; + protected clientWellKnownPromise: Promise; + protected turnServers: ITurnServer[] = []; protected turnServersExpiry = 0; - protected checkTurnServersIntervalID: number; + protected checkTurnServersIntervalID: NodeJS.Timeout; protected exportedOlmDeviceToImport: IOlmDevice; protected txnCtr = 0; @@ -714,7 +968,7 @@ export class MatrixClient extends EventEmitter { if (this.canSupportVoip) { this.checkTurnServersIntervalID = setInterval(() => { this.checkTurnServers(); - }, TURN_CHECK_INTERVAL) as any as number; // XXX: Typecast because we know better + }, TURN_CHECK_INTERVAL); // noinspection ES6MissingAwait this.checkTurnServers(); } @@ -726,7 +980,7 @@ export class MatrixClient extends EventEmitter { } // shallow-copy the opts dict before modifying and storing it - this.clientOpts = Object.assign({}, opts); // XXX: Typecast because we're about to add the missing props + this.clientOpts = Object.assign({}, opts) as IStoredClientOpts; this.clientOpts.crypto = this.crypto; this.clientOpts.canResetEntireTimeline = (roomId) => { if (!this.canResetTimelineCallback) { @@ -738,11 +992,9 @@ export class MatrixClient extends EventEmitter { this.syncApi.sync(); if (this.clientOpts.clientWellKnownPollPeriod !== undefined) { - this.clientWellKnownIntervalID = - // XXX: Typecast on timer ID because we know better - setInterval(() => { - this.fetchClientWellKnown(); - }, 1000 * this.clientOpts.clientWellKnownPollPeriod) as any as number; + this.clientWellKnownIntervalID = setInterval(() => { + this.fetchClientWellKnown(); + }, 1000 * this.clientOpts.clientWellKnownPollPeriod); this.fetchClientWellKnown(); } } @@ -1055,7 +1307,7 @@ export class MatrixClient extends EventEmitter { * this object. * @return {?Object} */ - public getSyncStateData(): any { // TODO: Unify types. + public getSyncStateData(): ISyncStateData | null { if (!this.syncApi) { return null; } @@ -1505,7 +1757,7 @@ export class MatrixClient extends EventEmitter { return this.crypto.beginKeyVerification(method, userId, deviceId); } - public checkSecretStorageKey(key: any, info: any): Promise { // TODO: Types + public checkSecretStorageKey(key: Uint8Array, info: ISecretStorageKeyInfo): Promise { if (!this.crypto) { throw new Error("End-to-end encryption disabled"); } @@ -1635,9 +1887,9 @@ export class MatrixClient extends EventEmitter { /** * Check the copy of our cross-signing key that we have in the device list and * see if we can get the private key. If so, mark it as trusted. - * @param {Object} opts TODO + * @param {Object} opts ICheckOwnCrossSigningTrustOpts object */ - public checkOwnCrossSigningTrust(opts?: any): Promise { // TODO: Types + public checkOwnCrossSigningTrust(opts?: ICheckOwnCrossSigningTrustOpts): Promise { if (!this.crypto) { throw new Error("End-to-end encryption disabled"); } @@ -1659,7 +1911,11 @@ export class MatrixClient extends EventEmitter { return this.crypto.checkCrossSigningPrivateKey(privateKey, expectedPublicKey); } - public legacyDeviceVerification(userId: string, deviceId: string, method: string): Promise { + public legacyDeviceVerification( + userId: string, + deviceId: string, + method: VerificationMethod, + ): Promise { if (!this.crypto) { throw new Error("End-to-end encryption disabled"); } @@ -1942,9 +2198,9 @@ export class MatrixClient extends EventEmitter { * @param {string} name the name of the secret to request * @param {string[]} devices the devices to request the secret from * - * @return {string} the contents of the secret + * @return {ISecretRequest} the secret request object */ - public requestSecret(name: string, devices: string[]): any { // TODO types + public requestSecret(name: string, devices: string[]): ISecretRequest { if (!this.crypto) { throw new Error("End-to-end encryption disabled"); } @@ -2045,7 +2301,7 @@ export class MatrixClient extends EventEmitter { * @param {object} config The encryption config for the room. * @return {Promise} A promise that will resolve when encryption is set up. */ - public setRoomEncryption(roomId: string, config: any): Promise { + public setRoomEncryption(roomId: string, config: IRoomEncryption): Promise { if (!this.crypto) { throw new Error("End-to-End encryption disabled"); } @@ -2067,7 +2323,7 @@ export class MatrixClient extends EventEmitter { // if there is an 'm.room.encryption' event in this room, it should be // encrypted (independently of whether we actually support encryption) - const ev = room.currentState.getStateEvents("m.room.encryption", ""); + const ev = room.currentState.getStateEvents(EventType.RoomEncryption, ""); if (ev) { return true; } @@ -2101,7 +2357,7 @@ export class MatrixClient extends EventEmitter { * @return {Promise} a promise which resolves to a list of * session export objects */ - public exportRoomKeys(): Promise { // TODO: Types + public exportRoomKeys(): Promise { if (!this.crypto) { return Promise.reject(new Error("End-to-end encryption disabled")); } @@ -2342,7 +2598,7 @@ export class MatrixClient extends EventEmitter { ); } - private makeKeyBackupPath(roomId: string, sessionId: string, version: string): { path: string, queryData: any } { + private makeKeyBackupPath(roomId: string, sessionId: string, version: string): IKeyBackupPath { let path; if (sessionId !== undefined) { path = utils.encodeUri("/room_keys/keys/$roomId/$sessionId", { @@ -2356,11 +2612,8 @@ export class MatrixClient extends EventEmitter { } else { path = "/room_keys/keys"; } - const queryData = version === undefined ? undefined : { version: version }; - return { - path: path, - queryData: queryData, - }; + const queryData = version === undefined ? undefined : { version }; + return { path, queryData }; } /** @@ -2372,8 +2625,7 @@ export class MatrixClient extends EventEmitter { * @return {Promise} a promise that will resolve when the keys * are uploaded */ - // TODO: Verify types - public sendKeyBackup(roomId: string, sessionId: string, version: string, data: any): Promise { + public sendKeyBackup(roomId: string, sessionId: string, version: string, data: IKeyBackup): Promise { if (!this.crypto) { throw new Error("End-to-end encryption disabled"); } @@ -2566,9 +2818,7 @@ export class MatrixClient extends EventEmitter { let totalKeyCount = 0; let keys = []; - const path = this.makeKeyBackupPath( - targetRoomId, targetSessionId, backupInfo.version, - ); + const path = this.makeKeyBackupPath(targetRoomId, targetSessionId, backupInfo.version); const algorithm = await BackupManager.makeAlgorithm(backupInfo, async () => { return privKey; }); @@ -2606,9 +2856,7 @@ export class MatrixClient extends EventEmitter { if (!roomData.sessions) continue; totalKeyCount += Object.keys(roomData.sessions).length; - const roomKeys = await algorithm.decryptSessions( - roomData.sessions, - ); + const roomKeys = await algorithm.decryptSessions(roomData.sessions); for (const k of roomKeys) { k.room_id = roomId; keys.push(k); @@ -2616,9 +2864,7 @@ export class MatrixClient extends EventEmitter { } } else if (res.sessions) { totalKeyCount = Object.keys(res.sessions).length; - keys = await algorithm.decryptSessions( - res.sessions, - ); + keys = await algorithm.decryptSessions(res.sessions); for (const k of keys) { k.room_id = targetRoomId; } @@ -2720,7 +2966,7 @@ export class MatrixClient extends EventEmitter { * @param {module:client.callback} callback Optional. * @return {Promise} Resolves with an object containing the config. */ - public getMediaConfig(callback?: Callback): Promise { // TODO: Types + public getMediaConfig(callback?: Callback): Promise { return this.http.authedRequest( callback, "GET", "/config", undefined, undefined, { prefix: PREFIX_MEDIA_R0, @@ -2760,7 +3006,7 @@ export class MatrixClient extends EventEmitter { const replacedRooms = new Set(); for (const r of allRooms) { - const createEvent = r.currentState.getStateEvents('m.room.create', ''); + const createEvent = r.currentState.getStateEvents(EventType.RoomCreate, ''); // invites are included in this list and we don't know their create events yet if (createEvent) { const predecessor = createEvent.getContent()['predecessor']; @@ -2771,7 +3017,7 @@ export class MatrixClient extends EventEmitter { } return allRooms.filter((r) => { - const tombstone = r.currentState.getStateEvents('m.room.tombstone', ''); + const tombstone = r.currentState.getStateEvents(EventType.RoomTombstone, ''); if (tombstone && replacedRooms.has(r.roomId)) { return false; } @@ -2803,10 +3049,10 @@ export class MatrixClient extends EventEmitter { * @param {string} eventType The event type * @param {Object} content the contents object for the event * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: an empty object * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public setAccountData(eventType: EventType | string, content: any, callback?: Callback): Promise { + public setAccountData(eventType: EventType | string, content: IContent, callback?: Callback): Promise<{}> { const path = utils.encodeUri("/user/$userId/account_data/$type", { $userId: this.credentials.userId, $type: eventType, @@ -2838,7 +3084,7 @@ export class MatrixClient extends EventEmitter { * data event. * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public async getAccountDataFromServer(eventType: string): Promise { + public async getAccountDataFromServer(eventType: string): Promise> { if (this.isInitialSyncComplete()) { const event = this.store.getAccountData(eventType); if (!event) { @@ -2878,10 +3124,10 @@ export class MatrixClient extends EventEmitter { * Sets the users that the current user should ignore. * @param {string[]} userIds the user IDs to ignore * @param {module:client.callback} [callback] Optional. - * @return {Promise} Resolves: Account data event + * @return {Promise} Resolves: an empty object * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public setIgnoredUsers(userIds: string[], callback?: Callback): Promise { + public setIgnoredUsers(userIds: string[], callback?: Callback): Promise<{}> { const content = { ignored_users: {} }; userIds.map((u) => content.ignored_users[u] = {}); return this.setAccountData("m.ignored_user_list", content, callback); @@ -2924,7 +3170,7 @@ export class MatrixClient extends EventEmitter { return Promise.resolve(room); } - let signPromise = Promise.resolve(); + let signPromise: Promise = Promise.resolve(); if (opts.inviteSignUrl) { signPromise = this.http.requestOtherUrl( @@ -2941,11 +3187,10 @@ export class MatrixClient extends EventEmitter { const reqOpts = { qsStringifyOptions: { arrayFormat: 'repeat' } }; try { - const data: any = {}; - // XXX: Explicit cast due to underlying types not existing - const signedInviteObj = >(await signPromise); + const data: IJoinRequestBody = {}; + const signedInviteObj = await signPromise; if (signedInviteObj) { - data['third_party_signed'] = signedInviteObj; + data.third_party_signed = signedInviteObj; } const path = utils.encodeUri("/join/$roomid", { $roomid: roomIdOrAlias }); @@ -2971,10 +3216,10 @@ export class MatrixClient extends EventEmitter { * @param {MatrixEvent} event The event to resend. * @param {Room} room Optional. The room the event is in. Will update the * timeline entry if provided. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: to an ISendEventResponse object * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public resendEvent(event: MatrixEvent, room: Room): Promise { // TODO: Types + public resendEvent(event: MatrixEvent, room: Room): Promise { this.updatePendingEventStatus(room, event, EventStatus.SENDING); return this.encryptAndSendEvent(room, event); } @@ -3008,8 +3253,8 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: TODO * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public setRoomName(roomId: string, name: string, callback?: Callback): Promise { - return this.sendStateEvent(roomId, "m.room.name", { name: name }, undefined, callback); + public setRoomName(roomId: string, name: string, callback?: Callback): Promise { + return this.sendStateEvent(roomId, EventType.RoomName, { name: name }, undefined, callback); } /** @@ -3019,8 +3264,8 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: TODO * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public setRoomTopic(roomId: string, topic: string, callback?: Callback): Promise { - return this.sendStateEvent(roomId, "m.room.topic", { topic: topic }, undefined, callback); + public setRoomTopic(roomId: string, topic: string, callback?: Callback): Promise { + return this.sendStateEvent(roomId, EventType.RoomTopic, { topic: topic }, undefined, callback); } /** @@ -3044,18 +3289,16 @@ export class MatrixClient extends EventEmitter { * @param {string} tagName name of room tag to be set * @param {object} metadata associated with that tag to be stored * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: to an empty object * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public setRoomTag(roomId: string, tagName: string, metadata: any, callback?: Callback): Promise { // TODO: Types + public setRoomTag(roomId: string, tagName: string, metadata: ITagMetadata, callback?: Callback): Promise<{}> { const path = utils.encodeUri("/user/$userId/rooms/$roomId/tags/$tag", { $userId: this.credentials.userId, $roomId: roomId, $tag: tagName, }); - return this.http.authedRequest( - callback, "PUT", path, undefined, metadata, - ); + return this.http.authedRequest(callback, "PUT", path, undefined, metadata); } /** @@ -3081,18 +3324,21 @@ export class MatrixClient extends EventEmitter { * @param {string} eventType event type to be set * @param {object} content event content * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: to an empty object {} * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public setRoomAccountData(roomId: string, eventType: string, content: any, callback?: Callback): Promise { + public setRoomAccountData( + roomId: string, + eventType: string, + content: Record, + callback?: Callback, + ): Promise<{}> { const path = utils.encodeUri("/user/$userId/rooms/$roomId/account_data/$type", { $userId: this.credentials.userId, $roomId: roomId, $type: eventType, }); - return this.http.authedRequest( - callback, "PUT", path, undefined, content, - ); + return this.http.authedRequest(callback, "PUT", path, undefined, content); } /** @@ -3102,7 +3348,7 @@ export class MatrixClient extends EventEmitter { * @param {Number} powerLevel * @param {MatrixEvent} event * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: to an ISendEventResponse object * @return {module:http-api.MatrixError} Rejects: with an error response. */ public setPowerLevel( @@ -3111,11 +3357,11 @@ export class MatrixClient extends EventEmitter { powerLevel: number, event: MatrixEvent, callback?: Callback, - ): Promise { + ): Promise { let content = { users: {}, }; - if (event && event.getType() === "m.room.power_levels") { + if (event?.getType() === EventType.RoomPowerLevels) { // take a copy of the content to ensure we don't corrupt // existing client state with a failed power level change content = utils.deepCopy(event.getContent()) as typeof content; @@ -3124,9 +3370,7 @@ export class MatrixClient extends EventEmitter { const path = utils.encodeUri("/rooms/$roomId/state/m.room.power_levels", { $roomId: roomId, }); - return this.http.authedRequest( - callback, "PUT", path, undefined, content, - ); + return this.http.authedRequest(callback, "PUT", path, undefined, content); } /** @@ -3135,13 +3379,13 @@ export class MatrixClient extends EventEmitter { * @param {Object} content * @param {string} txnId Optional. * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: to an empty object {} * @return {module:http-api.MatrixError} Rejects: with an error response. */ public sendEvent( roomId: string, eventType: string, - content: any, + content: IContent, txnId?: string, callback?: Callback, ): Promise { @@ -3153,7 +3397,7 @@ export class MatrixClient extends EventEmitter { * @param {object} eventObject An object with the partial structure of an event, to which event_id, user_id, room_id and origin_server_ts will be added. * @param {string} txnId the txnId. * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: to an empty object {} * @return {module:http-api.MatrixError} Rejects: with an error response. */ private sendCompleteEvent( @@ -3419,15 +3663,20 @@ export class MatrixClient extends EventEmitter { * @param {Object} content * @param {string} txnId Optional. * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: to an ISendEventResponse object * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public sendMessage(roomId: string, content: any, txnId?: string, callback?: Callback): Promise { + public sendMessage( + roomId: string, + content: IContent, + txnId?: string, + callback?: Callback, + ): Promise { if (utils.isFunction(txnId)) { callback = txnId as any as Callback; // for legacy txnId = undefined; } - return this.sendEvent(roomId, "m.room.message", content, txnId, callback); + return this.sendEvent(roomId, EventType.RoomMessage, content, txnId, callback); } /** @@ -3435,7 +3684,7 @@ export class MatrixClient extends EventEmitter { * @param {string} body * @param {string} txnId Optional. * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: to an empty object {} * @return {module:http-api.MatrixError} Rejects: with an error response. */ public sendTextMessage( @@ -3453,7 +3702,7 @@ export class MatrixClient extends EventEmitter { * @param {string} body * @param {string} txnId Optional. * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: to a ISendEventResponse object * @return {module:http-api.MatrixError} Rejects: with an error response. */ public sendNotice(roomId: string, body: string, txnId?: string, callback?: Callback): Promise { @@ -3466,7 +3715,7 @@ export class MatrixClient extends EventEmitter { * @param {string} body * @param {string} txnId Optional. * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: to a ISendEventResponse object * @return {module:http-api.MatrixError} Rejects: with an error response. */ public sendEmoteMessage( @@ -3485,7 +3734,7 @@ export class MatrixClient extends EventEmitter { * @param {Object} info * @param {string} text * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: to a ISendEventResponse object * @return {module:http-api.MatrixError} Rejects: with an error response. */ public sendImageMessage( @@ -3500,7 +3749,7 @@ export class MatrixClient extends EventEmitter { text = undefined; } const content = { - msgtype: "m.image", + msgtype: MsgType.Image, url: url, info: info, body: text, @@ -3514,7 +3763,7 @@ export class MatrixClient extends EventEmitter { * @param {Object} info * @param {string} text * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: to a ISendEventResponse object * @return {module:http-api.MatrixError} Rejects: with an error response. */ public sendStickerMessage( @@ -3541,7 +3790,7 @@ export class MatrixClient extends EventEmitter { * @param {string} body * @param {string} htmlBody * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: to a ISendEventResponse object * @return {module:http-api.MatrixError} Rejects: with an error response. */ public sendHtmlMessage( @@ -3559,7 +3808,7 @@ export class MatrixClient extends EventEmitter { * @param {string} body * @param {string} htmlBody * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: to a ISendEventResponse object * @return {module:http-api.MatrixError} Rejects: with an error response. */ public sendHtmlNotice( @@ -3577,7 +3826,7 @@ export class MatrixClient extends EventEmitter { * @param {string} body * @param {string} htmlBody * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: to a ISendEventResponse object * @return {module:http-api.MatrixError} Rejects: with an error response. */ public sendHtmlEmote( @@ -3594,12 +3843,12 @@ export class MatrixClient extends EventEmitter { * Send a receipt. * @param {Event} event The event being acknowledged * @param {string} receiptType The kind of receipt e.g. "m.read" - * @param {object} opts Additional content to send alongside the receipt. + * @param {object} body Additional content to send alongside the receipt. * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: to an empty object {} * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public sendReceipt(event: MatrixEvent, receiptType: string, body: any, callback?: Callback): Promise { + public sendReceipt(event: MatrixEvent, receiptType: string, body: any, callback?: Callback): Promise<{}> { if (typeof (body) === 'function') { callback = body as any as Callback; // legacy body = {}; @@ -3614,9 +3863,7 @@ export class MatrixClient extends EventEmitter { $receiptType: receiptType, $eventId: event.getId(), }); - const promise = this.http.authedRequest( - callback, "POST", path, undefined, body || {}, - ); + const promise = this.http.authedRequest(callback, "POST", path, undefined, body || {}); const room = this.getRoom(event.getRoomId()); if (room) { @@ -3633,10 +3880,10 @@ export class MatrixClient extends EventEmitter { * other users and homeservers. Default false (send to everyone). This * property is unstable and may change in the future. * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: to an empty object * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public async sendReadReceipt(event: MatrixEvent, opts: { hidden?: boolean }, callback?: Callback): Promise { + public async sendReadReceipt(event: MatrixEvent, opts: { hidden?: boolean }, callback?: Callback): Promise<{}> { if (typeof (opts) === 'function') { callback = opts as any as Callback; // legacy opts = {}; @@ -3676,7 +3923,7 @@ export class MatrixClient extends EventEmitter { rmEventId: string, rrEvent: MatrixEvent, opts: { hidden?: boolean }, - ): Promise { // TODO: Types + ): Promise<{}> { const room = this.getRoom(roomId); if (room && room.hasPendingEvent(rmEventId)) { throw new Error(`Cannot set read marker to a pending event (${rmEventId})`); @@ -3750,10 +3997,10 @@ export class MatrixClient extends EventEmitter { * @param {boolean} isTyping * @param {Number} timeoutMs * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: to an empty object {} * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public sendTyping(roomId: string, isTyping: boolean, timeoutMs: number, callback?: Callback): Promise { + public sendTyping(roomId: string, isTyping: boolean, timeoutMs: number, callback?: Callback): Promise<{}> { if (this.isGuest()) { return Promise.resolve({}); // guests cannot send typing notifications so don't bother. } @@ -3768,9 +4015,7 @@ export class MatrixClient extends EventEmitter { if (isTyping) { data.timeout = timeoutMs ? timeoutMs : 20000; } - return this.http.authedRequest( - callback, "PUT", path, undefined, data, - ); + return this.http.authedRequest(callback, "PUT", path, undefined, data); } /** @@ -3794,7 +4039,7 @@ export class MatrixClient extends EventEmitter { const upgradeHistory = [currentRoom]; // Work backwards first, looking at create events. - let createEvent = currentRoom.currentState.getStateEvents("m.room.create", ""); + let createEvent = currentRoom.currentState.getStateEvents(EventType.RoomCreate, ""); while (createEvent) { logger.log(`Looking at ${createEvent.getId()}`); const predecessor = createEvent.getContent()['predecessor']; @@ -3804,8 +4049,7 @@ export class MatrixClient extends EventEmitter { if (!refRoom) break; // end of the chain if (verifyLinks) { - const tombstone = refRoom.currentState - .getStateEvents("m.room.tombstone", ""); + const tombstone = refRoom.currentState.getStateEvents(EventType.RoomTombstone, ""); if (!tombstone || tombstone.getContent()['replacement_room'] !== refRoom.roomId) { @@ -3815,7 +4059,7 @@ export class MatrixClient extends EventEmitter { // Insert at the front because we're working backwards from the currentRoom upgradeHistory.splice(0, 0, refRoom); - createEvent = refRoom.currentState.getStateEvents("m.room.create", ""); + createEvent = refRoom.currentState.getStateEvents(EventType.RoomCreate, ""); } else { // No further create events to look at break; @@ -3823,14 +4067,14 @@ export class MatrixClient extends EventEmitter { } // Work forwards next, looking at tombstone events - let tombstoneEvent = currentRoom.currentState.getStateEvents("m.room.tombstone", ""); + let tombstoneEvent = currentRoom.currentState.getStateEvents(EventType.RoomTombstone, ""); while (tombstoneEvent) { const refRoom = this.getRoom(tombstoneEvent.getContent()['replacement_room']); if (!refRoom) break; // end of the chain if (refRoom.roomId === currentRoom.roomId) break; // Tombstone is referencing it's own room if (verifyLinks) { - createEvent = refRoom.currentState.getStateEvents("m.room.create", ""); + createEvent = refRoom.currentState.getStateEvents(EventType.RoomCreate, ""); if (!createEvent || !createEvent.getContent()['predecessor']) break; const predecessor = createEvent.getContent()['predecessor']; @@ -3848,7 +4092,7 @@ export class MatrixClient extends EventEmitter { // Set the current room to the reference room so we know where we're at currentRoom = refRoom; - tombstoneEvent = currentRoom.currentState.getStateEvents("m.room.tombstone", ""); + tombstoneEvent = currentRoom.currentState.getStateEvents(EventType.RoomTombstone, ""); } return upgradeHistory; @@ -4124,10 +4368,13 @@ export class MatrixClient extends EventEmitter { * @param {string} info The kind of info to set (e.g. 'avatar_url') * @param {Object} data The JSON object to set. * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: to an empty object {} * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public setProfileInfo(info: string, data: any, callback?: Callback): Promise { + // eslint-disable-next-line camelcase + public setProfileInfo(info: "avatar_url", data: { avatar_url: string }, callback?: Callback): Promise<{}>; + public setProfileInfo(info: "displayname", data: { displayname: string }, callback?: Callback): Promise<{}>; + public setProfileInfo(info: string, data: object, callback?: Callback): Promise<{}> { const path = utils.encodeUri("/profile/$userId/$info", { $userId: this.credentials.userId, $info: info, @@ -4143,10 +4390,8 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: {} an empty object. * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public async setDisplayName(name: string, callback?: Callback): Promise { - const prom = await this.setProfileInfo( - "displayname", { displayname: name }, callback, - ); + public async setDisplayName(name: string, callback?: Callback): Promise<{}> { + const prom = await this.setProfileInfo("displayname", { displayname: name }, callback); // XXX: synthesise a profile update for ourselves because Synapse is broken and won't const user = this.getUser(this.getUserId()); if (user) { @@ -4162,10 +4407,8 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: {} an empty object. * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public async setAvatarUrl(url: string, callback?: Callback): Promise { - const prom = await this.setProfileInfo( - "avatar_url", { avatar_url: url }, callback, - ); + public async setAvatarUrl(url: string, callback?: Callback): Promise<{}> { + const prom = await this.setProfileInfo("avatar_url", { avatar_url: url }, callback); // XXX: synthesise a profile update for ourselves because Synapse is broken and won't const user = this.getUser(this.getUserId()); if (user) { @@ -4207,20 +4450,14 @@ export class MatrixClient extends EventEmitter { */ public _unstable_setStatusMessage(newMessage: string): Promise { // eslint-disable-line camelcase const type = "im.vector.user_status"; - return Promise.all(this.getRooms().map((room) => { + return Promise.all(this.getRooms().map(async (room) => { const isJoined = room.getMyMembership() === "join"; const looksLikeDm = room.getInvitedAndJoinedMemberCount() === 2; - if (!isJoined || !looksLikeDm) { - return Promise.resolve(); - } + if (!isJoined || !looksLikeDm) return; // Check power level separately as it's a bit more expensive. const maySend = room.currentState.mayClientSendStateEvent(type, this); - if (!maySend) { - return Promise.resolve(); - } - return this.sendStateEvent(room.roomId, type, { - status: newMessage, - }, this.getUserId()); + if (!maySend) return; + await this.sendStateEvent(room.roomId, type, { status: newMessage }, this.getUserId()); })).then(); // .then to fix return type } @@ -4318,8 +4555,9 @@ export class MatrixClient extends EventEmitter { room.roomId, room.oldState.paginationToken, limit, - 'b'); - }).then((res: any) => { // TODO: Types + Direction.Backward, + ); + }).then((res: IMessagesResponse) => { const matrixEvents = res.chunk.map(this.getEventMapper()); if (res.state) { const stateEvents = res.state.map(this.getEventMapper()); @@ -4354,7 +4592,7 @@ export class MatrixClient extends EventEmitter { /** * @param {object} [options] - * @param {boolean} options.preventReEmit don't reemit events emitted on an event mapped by this mapper on the client + * @param {boolean} options.preventReEmit don't re-emit events emitted on an event mapped by this mapper on the client * @param {boolean} options.decrypt decrypt event proactively * @return {Function} */ @@ -4460,16 +4698,14 @@ export class MatrixClient extends EventEmitter { roomId: string, fromToken: string, limit: number, - dir: string, + dir: Direction, timelineFilter?: Filter, - ): Promise { // TODO: Types - const path = utils.encodeUri( - "/rooms/$roomId/messages", { $roomId: roomId }, - ); + ): Promise { + const path = utils.encodeUri("/rooms/$roomId/messages", { $roomId: roomId }); if (limit === undefined) { limit = 30; } - const params: any = { + const params: Record = { from: fromToken, limit: limit, dir: dir, @@ -4691,13 +4927,13 @@ export class MatrixClient extends EventEmitter { * @return {module:http-api.MatrixError} Rejects: with an error response. */ public setGuestAccess(roomId: string, opts: IGuestAccessOpts): Promise { - const writePromise = this.sendStateEvent(roomId, "m.room.guest_access", { + const writePromise = this.sendStateEvent(roomId, EventType.RoomGuestAccess, { guest_access: opts.allowJoin ? "can_join" : "forbidden", }, ""); - let readPromise = Promise.resolve(); + let readPromise: Promise = Promise.resolve(); if (opts.allowRead) { - readPromise = this.sendStateEvent(roomId, "m.room.history_visibility", { + readPromise = this.sendStateEvent(roomId, EventType.RoomHistoryVisibility, { history_visibility: "world_readable", }, ""); } @@ -4724,7 +4960,7 @@ export class MatrixClient extends EventEmitter { clientSecret: string, sendAttempt: number, nextLink?: string, - ): Promise { // TODO: Types + ): Promise { return this.requestTokenFromEndpoint( "/register/email/requestToken", { @@ -4756,7 +4992,7 @@ export class MatrixClient extends EventEmitter { clientSecret: string, sendAttempt: number, nextLink?: string, - ): Promise { // TODO: Types + ): Promise { return this.requestTokenFromEndpoint( "/register/msisdn/requestToken", { @@ -4791,7 +5027,7 @@ export class MatrixClient extends EventEmitter { clientSecret: string, sendAttempt: number, nextLink: string, - ): Promise { // TODO: Types + ): Promise { return this.requestTokenFromEndpoint( "/account/3pid/email/requestToken", { @@ -4823,7 +5059,7 @@ export class MatrixClient extends EventEmitter { clientSecret: string, sendAttempt: number, nextLink: string, - ): Promise { // TODO: Types + ): Promise { return this.requestTokenFromEndpoint( "/account/3pid/msisdn/requestToken", { @@ -4860,7 +5096,7 @@ export class MatrixClient extends EventEmitter { clientSecret: string, sendAttempt: number, nextLink: string, - ): Promise { // TODO: Types + ): Promise { return this.requestTokenFromEndpoint( "/account/password/email/requestToken", { @@ -4891,7 +5127,7 @@ export class MatrixClient extends EventEmitter { clientSecret: string, sendAttempt: number, nextLink: string, - ): Promise { // TODO: Types + ): Promise { return this.requestTokenFromEndpoint( "/account/password/msisdn/requestToken", { @@ -4912,7 +5148,10 @@ export class MatrixClient extends EventEmitter { * @param {object} params Parameters for the POST request * @return {Promise} Resolves: As requestEmailToken */ - private async requestTokenFromEndpoint(endpoint: string, params: any): Promise { + private async requestTokenFromEndpoint( + endpoint: string, + params: Record, + ): Promise { const postParams = Object.assign({}, params); // If the HS supports separate add and bind, then requestToken endpoints @@ -4936,10 +5175,7 @@ export class MatrixClient extends EventEmitter { } } - return this.http.request( - undefined, "POST", endpoint, undefined, - postParams, - ); + return this.http.request(undefined, "POST", endpoint, undefined, postParams); } /** @@ -4974,7 +5210,7 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: result object * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public setRoomMutePushRule(scope: string, roomId: string, mute: string): any { // TODO: Types + public setRoomMutePushRule(scope: string, roomId: string, mute: string): Promise | void { let deferred; let hasDontNotifyRule; @@ -5041,8 +5277,8 @@ export class MatrixClient extends EventEmitter { } } - public searchMessageText(opts: ISearchOpts, callback?: Callback): Promise { // TODO: Types - const roomEvents: any = { + public searchMessageText(opts: ISearchOpts, callback?: Callback): Promise { + const roomEvents: ISearchRequestBody["search_categories"]["room_events"] = { search_term: opts.query, }; @@ -5079,7 +5315,7 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: result object * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public searchRoomEvents(opts: IEventSearchOpts): Promise { // TODO: Types + public searchRoomEvents(opts: IEventSearchOpts): Promise { // TODO: support groups const body = { @@ -5087,7 +5323,7 @@ export class MatrixClient extends EventEmitter { room_events: { search_term: opts.term, filter: opts.filter, - order_by: "recent", + order_by: SearchOrderBy.Recent, event_context: { before_limit: 1, after_limit: 1, @@ -5097,7 +5333,7 @@ export class MatrixClient extends EventEmitter { }, }; - const searchResults = { + const searchResults: ISearchResults = { _query: body, results: [], highlights: [], @@ -5113,7 +5349,7 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: updated result object * @return {Error} Rejects: with an error response. */ - public backPaginateRoomEventsSearch(searchResults: any): Promise { // TODO: Types + public backPaginateRoomEventsSearch(searchResults: T): Promise { // TODO: we should implement a backoff (as per scrollback()) to deal more // nicely with HTTP errors. @@ -5123,7 +5359,7 @@ export class MatrixClient extends EventEmitter { if (searchResults.pendingRequest) { // already a request in progress - return the existing promise - return searchResults.pendingRequest; + return searchResults.pendingRequest as Promise; } const searchOpts = { @@ -5150,8 +5386,9 @@ export class MatrixClient extends EventEmitter { * @return {Object} searchResults * @private */ - public processRoomEventsSearch(searchResults: any, response: any): any { // XXX: Intended private, used in code - const roomEvents = response.search_categories.room_events; // eslint-disable-line camelcase + // XXX: Intended private, used in code + public processRoomEventsSearch(searchResults: T, response: ISearchResponse): T { + const roomEvents = response.search_categories.room_events; searchResults.count = roomEvents.count; searchResults.next_batch = roomEvents.next_batch; @@ -5212,7 +5449,7 @@ export class MatrixClient extends EventEmitter { * @return {Filter} Resolves to a Filter object. * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public createFilter(content: any): Promise { // TODO: Types + public createFilter(content: IFilterDefinition): Promise { const path = utils.encodeUri("/user/$userId/filter", { $userId: this.credentials.userId, }); @@ -5232,7 +5469,7 @@ export class MatrixClient extends EventEmitter { * @param {string} filterId The filter ID to retrieve * @param {boolean} allowCached True to allow cached filters to be returned. * Default: True. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: a Filter object * @return {module:http-api.MatrixError} Rejects: with an error response. */ public getFilter(userId: string, filterId: string, allowCached: boolean): Promise { @@ -5322,7 +5559,7 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: Token object * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public getOpenIdToken(): Promise { // TODO: Types + public getOpenIdToken(): Promise { const path = utils.encodeUri("/user/$userId/openid/request_token", { $userId: this.credentials.userId, }); @@ -5332,7 +5569,7 @@ export class MatrixClient extends EventEmitter { ); } - private startCallEventHandler = () => { + private startCallEventHandler = (): void => { if (this.isInitialSyncComplete()) { this.callEventHandler.start(); this.off("sync", this.startCallEventHandler); @@ -5341,10 +5578,10 @@ export class MatrixClient extends EventEmitter { /** * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: ITurnServerResponse object * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public turnServer(callback?: Callback): Promise { // TODO: Types + public turnServer(callback?: Callback): Promise { return this.http.authedRequest(callback, "GET", "/voip/turnServer"); } @@ -5352,7 +5589,7 @@ export class MatrixClient extends EventEmitter { * Get the TURN servers for this home server. * @return {Array} The servers or an empty list. */ - public getTurnServers(): any[] { // TODO: Types + public getTurnServers(): ITurnServer[] { return this.turnServers || []; } @@ -5383,7 +5620,7 @@ export class MatrixClient extends EventEmitter { if (res.uris) { logger.log("Got TURN URIs: " + res.uris + " refresh in " + res.ttl + " secs"); // map the response to a format that can be fed to RTCPeerConnection - const servers = { + const servers: ITurnServer = { urls: res.uris, username: res.username, credential: res.password, @@ -5454,14 +5691,12 @@ export class MatrixClient extends EventEmitter { * @param {string} userId the User ID to look up. * @return {object} the whois response - see Synapse docs for information. */ - public whoisSynapseUser(userId: string): Promise { + public whoisSynapseUser(userId: string): Promise { const path = utils.encodeUri( "/_synapse/admin/v1/whois/$userId", { $userId: userId }, ); - return this.http.authedRequest( - undefined, 'GET', path, undefined, undefined, { prefix: '' }, - ); + return this.http.authedRequest(undefined, 'GET', path, undefined, undefined, { prefix: '' }); } /** @@ -5470,7 +5705,7 @@ export class MatrixClient extends EventEmitter { * @param {string} userId the User ID to deactivate. * @return {object} the deactivate response - see Synapse docs for information. */ - public deactivateSynapseUser(userId: string): Promise { + public deactivateSynapseUser(userId: string): Promise { const path = utils.encodeUri( "/_synapse/admin/v1/deactivate/$userId", { $userId: userId }, @@ -5480,21 +5715,19 @@ export class MatrixClient extends EventEmitter { ); } - private async fetchClientWellKnown() { + private async fetchClientWellKnown(): Promise { // `getRawClientConfig` does not throw or reject on network errors, instead // it absorbs errors and returns `{}`. - this.clientWellKnownPromise = AutoDiscovery.getRawClientConfig( - this.getDomain(), - ); + this.clientWellKnownPromise = AutoDiscovery.getRawClientConfig(this.getDomain()); this.clientWellKnown = await this.clientWellKnownPromise; this.emit("WellKnown.client", this.clientWellKnown); } - public getClientWellKnown(): any { + public getClientWellKnown(): IClientWellKnown { return this.clientWellKnown; } - public waitForClientWellKnown(): Promise { + public waitForClientWellKnown(): Promise { return this.clientWellKnownPromise; } @@ -5505,7 +5738,7 @@ export class MatrixClient extends EventEmitter { * @param {object} opts the complete set of client options * @return {Promise} for store operation */ - public storeClientOptions() { // XXX: Intended private, used in code + public storeClientOptions(): Promise { // XXX: Intended private, used in code const primTypes = ["boolean", "string", "number"]; const serializableOpts = Object.entries(this.clientOpts) .filter(([key, value]) => { @@ -5543,7 +5776,7 @@ export class MatrixClient extends EventEmitter { * unstable APIs it supports * @return {Promise} The server /versions response */ - public getVersions(): Promise { // TODO: Types + public getVersions(): Promise { if (this.serverVersionsPromise) { return this.serverVersionsPromise; } @@ -5743,14 +5976,14 @@ export class MatrixClient extends EventEmitter { originalEvent = mapper(result.original_event); } let events = result.chunk.map(mapper); - if (fetchedEventType === "m.room.encrypted") { + if (fetchedEventType === EventType.RoomMessageEncrypted) { const allEvents = originalEvent ? events.concat(originalEvent) : events; await Promise.all(allEvents.map(e => { return new Promise(resolve => e.once("Event.decrypted", resolve)); })); events = events.filter(e => e.getType() === eventType); } - if (originalEvent && relationType === "m.replace") { + if (originalEvent && relationType === RelationType.Replace) { events = events.filter(e => e.getSender() === originalEvent.getSender()); } return { @@ -5765,7 +5998,7 @@ export class MatrixClient extends EventEmitter { * triggering a user interaction. * @return {object} */ - public getCrossSigningCacheCallbacks(): any { // TODO: Types + public getCrossSigningCacheCallbacks(): ICacheCallbacks { // XXX: Private member access return this.crypto?.crossSigningInfo.getCacheCallbacks(); } @@ -6129,9 +6362,7 @@ export class MatrixClient extends EventEmitter { */ public deactivateAccount(auth?: any, erase?: boolean): Promise<{}> { if (typeof (erase) === 'function') { - throw new Error( - 'deactivateAccount no longer accepts a callback parameter', - ); + throw new Error('deactivateAccount no longer accepts a callback parameter'); } const body: any = {}; @@ -6142,9 +6373,7 @@ export class MatrixClient extends EventEmitter { body.erase = erase; } - return this.http.authedRequest( - undefined, "POST", '/account/deactivate', undefined, body, - ); + return this.http.authedRequest(undefined, "POST", '/account/deactivate', undefined, body); } /** @@ -6175,14 +6404,13 @@ export class MatrixClient extends EventEmitter { * @param {string} options.name The name to give this room. * @param {string} options.topic The topic to give this room. * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: {room_id: {string}, - * room_alias: {string(opt)}} + * @return {Promise} Resolves: {room_id: {string}} * @return {module:http-api.MatrixError} Rejects: with an error response. */ public async createRoom( options: ICreateRoomOpts, callback?: Callback, - ): Promise<{ room_id: string, room_alias?: string }> { // eslint-disable-line camelcase + ): Promise<{ room_id: string }> { // eslint-disable-line camelcase // some valid options include: room_alias_name, visibility, invite // inject the id_access_token if inviting 3rd party addresses @@ -6202,9 +6430,7 @@ export class MatrixClient extends EventEmitter { } } - return this.http.authedRequest( - callback, "POST", "/createRoom", undefined, options, - ); + return this.http.authedRequest(callback, "POST", "/createRoom", undefined, options); } /** @@ -6249,7 +6475,7 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: TODO * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public roomState(roomId: string, callback?: Callback): Promise { // TODO: Types + public roomState(roomId: string, callback?: Callback): Promise { const path = utils.encodeUri("/rooms/$roomId/state", { $roomId: roomId }); return this.http.authedRequest(callback, "GET", path); } @@ -6263,7 +6489,11 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves to an object containing the event. * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public fetchRoomEvent(roomId: string, eventId: string, callback?: Callback): Promise { // TODO: Types + public fetchRoomEvent( + roomId: string, + eventId: string, + callback?: Callback, + ): Promise { const path = utils.encodeUri( "/rooms/$roomId/event/$eventId", { $roomId: roomId, @@ -6288,7 +6518,7 @@ export class MatrixClient extends EventEmitter { excludeMembership?: string[], atEventId?: string, callback?: Callback, - ): Promise<{ [userId: string]: any }> { + ): Promise<{ [userId: string]: IStateEventWithRoomId }> { const queryParams: any = {}; if (includeMembership) { queryParams.membership = includeMembership; @@ -6333,7 +6563,12 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: TODO * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public getStateEvent(roomId: string, eventType: string, stateKey: string, callback?: Callback): Promise { + public getStateEvent( + roomId: string, + eventType: string, + stateKey: string, + callback?: Callback, + ): Promise { const pathParams = { $roomId: roomId, $eventType: eventType, @@ -6363,7 +6598,7 @@ export class MatrixClient extends EventEmitter { content: any, stateKey = "", callback?: Callback, - ): Promise { // TODO: Types + ): Promise { const pathParams = { $roomId: roomId, $eventType: eventType, @@ -6373,9 +6608,7 @@ export class MatrixClient extends EventEmitter { if (stateKey !== undefined) { path = utils.encodeUri(path + "/$stateKey", pathParams); } - return this.http.authedRequest( - callback, "PUT", path, undefined, content, - ); + return this.http.authedRequest(callback, "PUT", path, undefined, content); } /** @@ -6385,7 +6618,7 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: TODO * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public roomInitialSync(roomId: string, limit: number, callback?: Callback): Promise { // TODO: Types + public roomInitialSync(roomId: string, limit: number, callback?: Callback): Promise { if (utils.isFunction(limit)) { callback = limit as any as Callback; // legacy limit = undefined; @@ -6432,9 +6665,7 @@ export class MatrixClient extends EventEmitter { "m.hidden": Boolean(opts ? opts.hidden : false), }; - return this.http.authedRequest( - undefined, "POST", path, undefined, content, - ); + return this.http.authedRequest(undefined, "POST", path, undefined, content); } /** @@ -6453,7 +6684,7 @@ export class MatrixClient extends EventEmitter { * and their profile data. * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public getJoinedRoomMembers(roomId: string): Promise { // TODO: Types + public getJoinedRoomMembers(roomId: string): Promise { const path = utils.encodeUri("/rooms/$roomId/joined_members", { $roomId: roomId, }); @@ -6473,7 +6704,7 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: TODO * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public publicRooms(options: IRoomDirectoryOptions, callback?: Callback): Promise { // TODO: Types + public publicRooms(options: IRoomDirectoryOptions, callback?: Callback): Promise { if (typeof (options) == 'function') { callback = options; options = {}; @@ -6491,9 +6722,7 @@ export class MatrixClient extends EventEmitter { if (Object.keys(options).length === 0 && Object.keys(queryParams).length === 0) { return this.http.authedRequest(callback, "GET", "/publicRooms"); } else { - return this.http.authedRequest( - callback, "POST", "/publicRooms", queryParams, options, - ); + return this.http.authedRequest(callback, "POST", "/publicRooms", queryParams, options); } } @@ -6502,19 +6731,17 @@ export class MatrixClient extends EventEmitter { * @param {string} alias The room alias to create. * @param {string} roomId The room ID to link the alias to. * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO. + * @return {Promise} Resolves: an empty object {} * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public createAlias(alias: string, roomId: string, callback?: Callback): Promise { // TODO: Types + public createAlias(alias: string, roomId: string, callback?: Callback): Promise<{}> { const path = utils.encodeUri("/directory/room/$alias", { $alias: alias, }); const data = { room_id: roomId, }; - return this.http.authedRequest( - callback, "PUT", path, undefined, data, - ); + return this.http.authedRequest(callback, "PUT", path, undefined, data); } /** @@ -6522,16 +6749,14 @@ export class MatrixClient extends EventEmitter { * and you must have sufficient access to do this operation. * @param {string} alias The room alias to delete. * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO. + * @return {Promise} Resolves: an empty object. * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public deleteAlias(alias: string, callback?: Callback): Promise { // TODO: Types + public deleteAlias(alias: string, callback?: Callback): Promise<{}> { const path = utils.encodeUri("/directory/room/$alias", { $alias: alias, }); - return this.http.authedRequest( - callback, "DELETE", path, undefined, undefined, - ); + return this.http.authedRequest(callback, "DELETE", path, undefined, undefined); } /** @@ -6544,8 +6769,7 @@ export class MatrixClient extends EventEmitter { const path = utils.encodeUri("/rooms/$roomId/aliases", { $roomId: roomId }); const prefix = PREFIX_UNSTABLE + "/org.matrix.msc2432"; - return this.http.authedRequest(callback, "GET", path, - null, null, { prefix }); + return this.http.authedRequest(callback, "GET", path, null, null, { prefix }); } /** @@ -6563,18 +6787,16 @@ export class MatrixClient extends EventEmitter { const path = utils.encodeUri("/directory/room/$alias", { $alias: alias, }); - return this.http.authedRequest( - callback, "GET", path, - ); + return this.http.authedRequest(callback, "GET", path); } /** * @param {string} roomAlias * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: Object with room_id and servers. * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public resolveRoomAlias(roomAlias: string, callback?: Callback): Promise { // TODO: Types + public resolveRoomAlias(roomAlias: string, callback?: Callback): Promise<{ room_id: string, servers: string[] }> { // TODO: deprecate this or getRoomIdForAlias const path = utils.encodeUri("/directory/room/$alias", { $alias: roomAlias }); return this.http.request(callback, "GET", path); @@ -6587,7 +6809,7 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: TODO * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public getRoomDirectoryVisibility(roomId: string, callback?: Callback): Promise { // TODO: Types + public getRoomDirectoryVisibility(roomId: string, callback?: Callback): Promise<{ visibility: Visibility }> { const path = utils.encodeUri("/directory/list/room/$roomId", { $roomId: roomId, }); @@ -6604,17 +6826,11 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: result object * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public setRoomDirectoryVisibility( - roomId: string, - visibility: "public" | "private", - callback?: Callback, - ): Promise { // TODO: Types + public setRoomDirectoryVisibility(roomId: string, visibility: Visibility, callback?: Callback): Promise<{}> { const path = utils.encodeUri("/directory/list/room/$roomId", { $roomId: roomId, }); - return this.http.authedRequest( - callback, "PUT", path, undefined, { "visibility": visibility }, - ); + return this.http.authedRequest(callback, "PUT", path, undefined, { visibility }); } /** @@ -6653,7 +6869,7 @@ export class MatrixClient extends EventEmitter { * apply a limit if unspecified. * @return {Promise} Resolves: an array of results. */ - public searchUserDirectory(opts: { term: string, limit?: number }): Promise { // TODO: Types + public searchUserDirectory(opts: { term: string, limit?: number }): Promise { const body: any = { search_term: opts.term, }; @@ -6662,9 +6878,7 @@ export class MatrixClient extends EventEmitter { body.limit = opts.limit; } - return this.http.authedRequest( - undefined, "POST", "/user_directory/search", undefined, body, - ); + return this.http.authedRequest(undefined, "POST", "/user_directory/search", undefined, body); } /** @@ -6710,7 +6924,7 @@ export class MatrixClient extends EventEmitter { public uploadContent( file: File | String | Buffer | ReadStream | Blob, opts?: IUploadOpts, - ): Promise { // TODO: Advanced types + ): IAbortablePromise { // TODO: Advanced types return this.http.uploadContent(file, opts); } @@ -6719,7 +6933,7 @@ export class MatrixClient extends EventEmitter { * @param {Promise} promise The promise returned from uploadContent * @return {boolean} true if canceled, otherwise false */ - public cancelUpload(promise: Promise): boolean { // TODO: Advanced types + public cancelUpload(promise: IAbortablePromise): boolean { return this.http.cancelUpload(promise); } @@ -6743,7 +6957,11 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: TODO * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public getProfileInfo(userId: string, info?: string, callback?: Callback): Promise { // TODO: Types + public getProfileInfo( + userId: string, + info?: string, + callback?: Callback, + ): Promise<{ avatar_url?: string, displayname?: string }> { if (utils.isFunction(info)) { callback = info as any as Callback; // legacy info = undefined; @@ -6762,11 +6980,9 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: TODO * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public getThreePids(callback?: Callback): Promise { // TODO: Types + public getThreePids(callback?: Callback): Promise<{ threepids: IThreepid[] }> { const path = "/account/3pid"; - return this.http.authedRequest( - callback, "GET", path, undefined, undefined, - ); + return this.http.authedRequest(callback, "GET", path, undefined, undefined); } /** @@ -6805,13 +7021,10 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: on success * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public async addThreePidOnly(data: any): Promise { // TODO: Types + public async addThreePidOnly(data: IAddThreePidOnlyBody): Promise<{}> { const path = "/account/3pid/add"; - const prefix = await this.isVersionSupported("r0.6.0") ? - PREFIX_R0 : PREFIX_UNSTABLE; - return this.http.authedRequest( - undefined, "POST", path, null, data, { prefix }, - ); + const prefix = await this.isVersionSupported("r0.6.0") ? PREFIX_R0 : PREFIX_UNSTABLE; + return this.http.authedRequest(undefined, "POST", path, null, data, { prefix }); } /** @@ -6828,7 +7041,7 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: on success * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public async bindThreePid(data: any): Promise { // TODO: Types + public async bindThreePid(data: IBindThreePidBody): Promise<{}> { const path = "/account/3pid/bind"; const prefix = await this.isVersionSupported("r0.6.0") ? PREFIX_R0 : PREFIX_UNSTABLE; @@ -6848,18 +7061,19 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: on success * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public async unbindThreePid(medium: string, address: string): Promise { + public async unbindThreePid( + medium: string, + address: string, + // eslint-disable-next-line camelcase + ): Promise<{ id_server_unbind_result: IdServerUnbindResult }> { const path = "/account/3pid/unbind"; const data = { medium, address, id_server: this.getIdentityServerUrl(true), }; - const prefix = await this.isVersionSupported("r0.6.0") ? - PREFIX_R0 : PREFIX_UNSTABLE; - return this.http.authedRequest( - undefined, "POST", path, null, data, { prefix }, - ); + const prefix = await this.isVersionSupported("r0.6.0") ? PREFIX_R0 : PREFIX_UNSTABLE; + return this.http.authedRequest(undefined, "POST", path, null, data, { prefix }); } /** @@ -6870,13 +7084,13 @@ export class MatrixClient extends EventEmitter { * (generally the empty JSON object) * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public deleteThreePid(medium: string, address: string): Promise { + public deleteThreePid( + medium: string, + address: string, + // eslint-disable-next-line camelcase + ): Promise<{ id_server_unbind_result: IdServerUnbindResult }> { const path = "/account/3pid/delete"; - const data = { - 'medium': medium, - 'address': address, - }; - return this.http.authedRequest(undefined, "POST", path, null, data); + return this.http.authedRequest(undefined, "POST", path, null, { medium, address }); } /** @@ -6904,10 +7118,8 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: result object * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public getDevices(): Promise { // TODO: Types - return this.http.authedRequest( - undefined, 'GET', "/devices", undefined, undefined, - ); + public getDevices(): Promise<{ devices: IMyDevice[] }> { + return this.http.authedRequest(undefined, 'GET', "/devices", undefined, undefined); } /** @@ -6916,13 +7128,11 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: result object * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public getDevice(deviceId: string): Promise { // TODO: Types + public getDevice(deviceId: string): Promise { const path = utils.encodeUri("/devices/$device_id", { $device_id: deviceId, }); - return this.http.authedRequest( - undefined, 'GET', path, undefined, undefined, - ); + return this.http.authedRequest(undefined, 'GET', path, undefined, undefined); } /** @@ -6933,7 +7143,8 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: result object * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public setDeviceDetails(deviceId: string, body: any): Promise { // TODO: Types + // eslint-disable-next-line camelcase + public setDeviceDetails(deviceId: string, body: { display_name: string }): Promise<{}> { const path = utils.encodeUri("/devices/$device_id", { $device_id: deviceId, }); @@ -6989,11 +7200,9 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: Array of objects representing pushers * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public getPushers(callback?: Callback): Promise { // TODO: Types + public getPushers(callback?: Callback): Promise<{ pushers: IPusher[] }> { const path = "/pushers"; - return this.http.authedRequest( - callback, "GET", path, undefined, undefined, - ); + return this.http.authedRequest(callback, "GET", path, undefined, undefined); } /** @@ -7004,11 +7213,9 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: Empty json object on success * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public setPusher(pusher: any, callback?: Callback): Promise { // TODO: Types + public setPusher(pusher: IPusher, callback?: Callback): Promise<{}> { const path = "/pushers/set"; - return this.http.authedRequest( - callback, "POST", path, null, pusher, - ); + return this.http.authedRequest(callback, "POST", path, null, pusher); } /** @@ -7028,18 +7235,16 @@ export class MatrixClient extends EventEmitter { * @param {string} ruleId * @param {Object} body * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: an empty object {} * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public addPushRule(scope: string, kind: string, ruleId: string, body: any, callback?: Callback): Promise { // TODO: Types + public addPushRule(scope: string, kind: string, ruleId: string, body: any, callback?: Callback): Promise<{}> { // NB. Scope not uri encoded because devices need the '/' const path = utils.encodeUri("/pushrules/" + scope + "/$kind/$ruleId", { $kind: kind, $ruleId: ruleId, }); - return this.http.authedRequest( - callback, "PUT", path, undefined, body, - ); + return this.http.authedRequest(callback, "PUT", path, undefined, body); } /** @@ -7047,10 +7252,10 @@ export class MatrixClient extends EventEmitter { * @param {string} kind * @param {string} ruleId * @param {module:client.callback} callback Optional. - * @return {Promise} Resolves: TODO + * @return {Promise} Resolves: an empty object {} * @return {module:http-api.MatrixError} Rejects: with an error response. */ - public deletePushRule(scope: string, kind: string, ruleId: string, callback?: Callback): Promise { // TODO: Types + public deletePushRule(scope: string, kind: string, ruleId: string, callback?: Callback): Promise<{}> { // NB. Scope not uri encoded because devices need the '/' const path = utils.encodeUri("/pushrules/" + scope + "/$kind/$ruleId", { $kind: kind, @@ -7075,7 +7280,7 @@ export class MatrixClient extends EventEmitter { ruleId: string, enabled: boolean, callback?: Callback, - ): Promise { // TODO: Types + ): Promise<{}> { const path = utils.encodeUri("/pushrules/" + scope + "/$kind/$ruleId/enabled", { $kind: kind, $ruleId: ruleId, @@ -7101,7 +7306,7 @@ export class MatrixClient extends EventEmitter { ruleId: string, actions: string[], callback?: Callback, - ): Promise { // TODO: Types + ): Promise<{}> { const path = utils.encodeUri("/pushrules/" + scope + "/$kind/$ruleId/actions", { $kind: kind, $ruleId: ruleId, @@ -7121,16 +7326,14 @@ export class MatrixClient extends EventEmitter { * @return {module:http-api.MatrixError} Rejects: with an error response. */ public search( - opts: { body: any, next_batch?: string }, // eslint-disable-line camelcase + opts: { body: ISearchRequestBody, next_batch?: string }, // eslint-disable-line camelcase callback?: Callback, - ): Promise { // TODO: Types + ): Promise { const queryParams: any = {}; if (opts.next_batch) { queryParams.next_batch = opts.next_batch; } - return this.http.authedRequest( - callback, "POST", "/search", queryParams, opts.body, - ); + return this.http.authedRequest(callback, "POST", "/search", queryParams, opts.body); } /** @@ -7146,7 +7349,11 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: result object. Rejects: with * an error response ({@link module:http-api.MatrixError}). */ - public uploadKeysRequest(content: any, opts?: any, callback?: Callback): Promise { + public uploadKeysRequest( + content: IUploadKeysRequest, + opts?: void, + callback?: Callback, + ): Promise { return this.http.authedRequest(callback, "POST", "/keys/upload", undefined, content); } @@ -7172,12 +7379,10 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: result object. Rejects: with * an error response ({@link module:http-api.MatrixError}). */ - public downloadKeysForUsers(userIds: string[], opts: { token?: string }): Promise { // TODO: Types + public downloadKeysForUsers(userIds: string[], opts: { token?: string }): Promise { if (utils.isFunction(opts)) { // opts used to be 'callback'. - throw new Error( - 'downloadKeysForUsers no longer accepts a callback parameter', - ); + throw new Error('downloadKeysForUsers no longer accepts a callback parameter'); } opts = opts || {}; @@ -7207,7 +7412,11 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: result object. Rejects: with * an error response ({@link module:http-api.MatrixError}). */ - public claimOneTimeKeys(devices: string[], keyAlgorithm = "signed_curve25519", timeout?: number): Promise { // TODO: Types + public claimOneTimeKeys( + devices: string[], + keyAlgorithm = "signed_curve25519", + timeout?: number, + ): Promise { const queries = {}; if (keyAlgorithm === undefined) { @@ -7239,7 +7448,7 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: result object. Rejects: with * an error response ({@link module:http-api.MatrixError}). */ - public getKeyChanges(oldToken: string, newToken: string): Promise { // TODO: Types + public getKeyChanges(oldToken: string, newToken: string): Promise<{ changed: string[], left: string[] }> { const qps = { from: oldToken, to: newToken, @@ -7689,15 +7898,13 @@ export class MatrixClient extends EventEmitter { * this HS * @return {Promise} Resolves to the result object */ - public getThirdpartyProtocols(): Promise { // TODO: Types + public getThirdpartyProtocols(): Promise<{ [protocol: string]: IProtocol }> { return this.http.authedRequest( undefined, "GET", "/thirdparty/protocols", undefined, undefined, ).then((response) => { // sanity check if (!response || typeof (response) !== 'object') { - throw new Error( - `/thirdparty/protocols did not return an object: ${response}`, - ); + throw new Error(`/thirdparty/protocols did not return an object: ${response}`); } return response; }); @@ -7711,7 +7918,10 @@ export class MatrixClient extends EventEmitter { * response to getThirdpartyProtocols() * @return {Promise} Resolves to the result object */ - public getThirdpartyLocation(protocol: string, params: any): Promise { // TODO: Types + public getThirdpartyLocation( + protocol: string, + params: { searchFields?: string[] }, + ): Promise { const path = utils.encodeUri("/thirdparty/location/$protocol", { $protocol: protocol, }); @@ -7727,7 +7937,7 @@ export class MatrixClient extends EventEmitter { * response to getThirdpartyProtocols() * @return {Promise} Resolves to the result object */ - public getThirdpartyUser(protocol: string, params: any): Promise { // TODO: Types + public getThirdpartyUser(protocol: string, params: any): Promise { // TODO: Types const path = utils.encodeUri("/thirdparty/user/$protocol", { $protocol: protocol, }); @@ -7737,9 +7947,7 @@ export class MatrixClient extends EventEmitter { public getTerms(serviceType: SERVICE_TYPES, baseUrl: string): Promise { // TODO: Types const url = this.termsUrlForService(serviceType, baseUrl); - return this.http.requestOtherUrl( - undefined, 'GET', url, - ); + return this.http.requestOtherUrl(undefined, 'GET', url); } public agreeToTerms( @@ -7752,9 +7960,7 @@ export class MatrixClient extends EventEmitter { const headers = { Authorization: "Bearer " + accessToken, }; - return this.http.requestOtherUrl( - undefined, 'POST', url, null, { user_accepts: termsUrls }, { headers }, - ); + return this.http.requestOtherUrl(undefined, 'POST', url, null, { user_accepts: termsUrls }, { headers }); } /** @@ -7765,7 +7971,7 @@ export class MatrixClient extends EventEmitter { * @param {string} reason The reason the content is being reported. May be blank. * @returns {Promise} Resolves to an empty object if successful */ - public reportEvent(roomId: string, eventId: string, score: number, reason: string): Promise { // TODO: Types + public reportEvent(roomId: string, eventId: string, score: number, reason: string): Promise<{}> { const path = utils.encodeUri("/rooms/$roomId/report/$eventId", { $roomId: roomId, $eventId: eventId, @@ -7791,7 +7997,10 @@ export class MatrixClient extends EventEmitter { autoJoinOnly?: boolean, limit?: number, batch?: string, - ): Promise { // TODO: Types + ): Promise<{ + rooms: ISpaceSummaryRoom[]; + events: ISpaceSummaryEvent[]; + }> { const path = utils.encodeUri("/rooms/$roomId/spaces", { $roomId: roomId, }); diff --git a/src/content-helpers.ts b/src/content-helpers.ts index 061073c5e..a75b2fd87 100644 --- a/src/content-helpers.ts +++ b/src/content-helpers.ts @@ -17,6 +17,8 @@ limitations under the License. /** @module ContentHelpers */ +import { MsgType } from "./@types/event"; + /** * Generates the content for a HTML Message event * @param {string} body the plaintext body of the message @@ -25,7 +27,7 @@ limitations under the License. */ export function makeHtmlMessage(body: string, htmlBody: string) { return { - msgtype: "m.text", + msgtype: MsgType.Text, format: "org.matrix.custom.html", body: body, formatted_body: htmlBody, @@ -40,7 +42,7 @@ export function makeHtmlMessage(body: string, htmlBody: string) { */ export function makeHtmlNotice(body: string, htmlBody: string) { return { - msgtype: "m.notice", + msgtype: MsgType.Notice, format: "org.matrix.custom.html", body: body, formatted_body: htmlBody, @@ -55,7 +57,7 @@ export function makeHtmlNotice(body: string, htmlBody: string) { */ export function makeHtmlEmote(body: string, htmlBody: string) { return { - msgtype: "m.emote", + msgtype: MsgType.Emote, format: "org.matrix.custom.html", body: body, formatted_body: htmlBody, @@ -69,7 +71,7 @@ export function makeHtmlEmote(body: string, htmlBody: string) { */ export function makeTextMessage(body: string) { return { - msgtype: "m.text", + msgtype: MsgType.Text, body: body, }; } @@ -81,7 +83,7 @@ export function makeTextMessage(body: string) { */ export function makeNotice(body: string) { return { - msgtype: "m.notice", + msgtype: MsgType.Notice, body: body, }; } @@ -93,7 +95,7 @@ export function makeNotice(body: string) { */ export function makeEmoteMessage(body: string) { return { - msgtype: "m.emote", + msgtype: MsgType.Emote, body: body, }; } diff --git a/src/crypto/EncryptionSetup.ts b/src/crypto/EncryptionSetup.ts index 278a17ed4..8b538afa2 100644 --- a/src/crypto/EncryptionSetup.ts +++ b/src/crypto/EncryptionSetup.ts @@ -109,8 +109,8 @@ export class EncryptionSetupBuilder { * @param {Object} content * @return {Promise} */ - public setAccountData(type: string, content: object): Promise { - return this.accountDataClientAdapter.setAccountData(type, content); + public async setAccountData(type: string, content: object): Promise { + await this.accountDataClientAdapter.setAccountData(type, content); } /** @@ -284,7 +284,7 @@ class AccountDataClientAdapter extends EventEmitter { * @param {Object} content * @return {Promise} */ - public setAccountData(type: string, content: any): Promise { + public setAccountData(type: string, content: any): Promise<{}> { const lastEvent = this.values.get(type); this.values.set(type, content); // ensure accountData is emitted on the next tick, @@ -293,6 +293,7 @@ class AccountDataClientAdapter extends EventEmitter { return Promise.resolve().then(() => { const event = new MatrixEvent({ type, content }); this.emit("accountData", event, lastEvent); + return {}; }); } } diff --git a/src/crypto/RoomList.ts b/src/crypto/RoomList.ts index ab653f456..b2835362f 100644 --- a/src/crypto/RoomList.ts +++ b/src/crypto/RoomList.ts @@ -24,10 +24,10 @@ import { IndexedDBCryptoStore } from './store/indexeddb-crypto-store'; import { CryptoStore } from "../client"; /* eslint-disable camelcase */ -interface IRoomEncryption { +export interface IRoomEncryption { algorithm: string; - rotation_period_ms: number; - rotation_period_msgs: number; + rotation_period_ms?: number; + rotation_period_msgs?: number; } /* eslint-enable camelcase */ diff --git a/src/crypto/SecretStorage.ts b/src/crypto/SecretStorage.ts index 017851194..fb5665fa6 100644 --- a/src/crypto/SecretStorage.ts +++ b/src/crypto/SecretStorage.ts @@ -37,9 +37,9 @@ export interface ISecretRequest { export interface IAccountDataClient extends EventEmitter { // Subset of MatrixClient (which also uses any for the event content) - getAccountDataFromServer: (eventType: string) => Promise; + getAccountDataFromServer: (eventType: string) => Promise>; getAccountData: (eventType: string) => MatrixEvent; - setAccountData: (eventType: string, content: any) => Promise; + setAccountData: (eventType: string, content: any) => Promise<{}>; } interface ISecretRequestInternal { @@ -174,7 +174,7 @@ export class SecretStorage { * the form [keyId, keyInfo]. Otherwise, null is returned. * XXX: why is this an array when addKey returns an object? */ - public async getKey(keyId: string): Promise { + public async getKey(keyId: string): Promise { if (!keyId) { keyId = await this.getDefaultKeyId(); } @@ -184,7 +184,7 @@ export class SecretStorage { const keyInfo = await this.accountDataAdapter.getAccountDataFromServer( "m.secret_storage.key." + keyId, - ); + ) as ISecretStorageKeyInfo; return keyInfo ? [keyId, keyInfo] : null; } @@ -248,7 +248,7 @@ export class SecretStorage { // get key information from key storage const keyInfo = await this.accountDataAdapter.getAccountDataFromServer( "m.secret_storage.key." + keyId, - ); + ) as ISecretStorageKeyInfo; if (!keyInfo) { throw new Error("Unknown key: " + keyId); } diff --git a/src/crypto/algorithms/base.ts b/src/crypto/algorithms/base.ts index 7f687774b..89fa7034d 100644 --- a/src/crypto/algorithms/base.ts +++ b/src/crypto/algorithms/base.ts @@ -26,6 +26,7 @@ import { OlmDevice } from "../OlmDevice"; import { MatrixEvent, RoomMember } from "../.."; import { Crypto, IEventDecryptionResult, IMegolmSessionData, IncomingRoomKeyRequest } from ".."; import { DeviceInfo } from "../deviceinfo"; +import { IRoomEncryption } from "../RoomList"; /** * map of registered encryption algorithm classes. A map from string to {@link @@ -52,7 +53,7 @@ interface IParams { olmDevice: OlmDevice; baseApis: MatrixClient; roomId: string; - config: object; + config: IRoomEncryption & object; } /** diff --git a/src/crypto/backup.ts b/src/crypto/backup.ts index abe30798e..fbbdec933 100644 --- a/src/crypto/backup.ts +++ b/src/crypto/backup.ts @@ -31,7 +31,7 @@ import { IndexedDBCryptoStore } from './store/indexeddb-crypto-store'; import { encodeRecoveryKey } from './recoverykey'; import { encryptAES, decryptAES, calculateKeyCheck } from './aes'; import { getCrypto } from '../utils'; -import { ICurve25519AuthData, IAes256AuthData, IKeyBackupInfo } from "./keybackup"; +import { ICurve25519AuthData, IAes256AuthData, IKeyBackupInfo, IKeyBackupSession } from "./keybackup"; import { UnstableValue } from "../NamespacedValue"; const KEY_BACKUP_KEYS_PER_REQUEST = 200; @@ -85,12 +85,22 @@ interface BackupAlgorithmClass { interface BackupAlgorithm { untrusted: boolean; encryptSession(data: Record): Promise; - decryptSessions(ciphertexts: Record): Promise[]>; + decryptSessions(ciphertexts: Record): Promise[]>; authData: AuthData; keyMatches(key: ArrayLike): Promise; free(): void; } +export interface IKeyBackup { + rooms: { + [roomId: string]: { + sessions: { + [sessionId: string]: IKeyBackupSession; + }; + }; + } +} + /** * Manages the key backup. */ @@ -464,11 +474,11 @@ export class BackupManager { let remaining = await this.baseApis.crypto.cryptoStore.countSessionsNeedingBackup(); this.baseApis.crypto.emit("crypto.keyBackupSessionsRemaining", remaining); - const data = {}; + const rooms: IKeyBackup["rooms"] = {}; for (const session of sessions) { const roomId = session.sessionData.room_id; - if (data[roomId] === undefined) { - data[roomId] = { sessions: {} }; + if (rooms[roomId] === undefined) { + rooms[roomId] = { sessions: {} }; } const sessionData = await this.baseApis.crypto.olmDevice.exportInboundGroupSession( @@ -487,7 +497,7 @@ export class BackupManager { ); const verified = this.baseApis.crypto.checkDeviceInfoTrust(userId, device).isVerified(); - data[roomId]['sessions'][session.sessionId] = { + rooms[roomId]['sessions'][session.sessionId] = { first_message_index: sessionData.first_known_index, forwarded_count: forwardedCount, is_verified: verified, @@ -495,10 +505,7 @@ export class BackupManager { }; } - await this.baseApis.sendKeyBackup( - undefined, undefined, this.backupInfo.version, - { rooms: data }, - ); + await this.baseApis.sendKeyBackup(undefined, undefined, this.backupInfo.version, { rooms }); await this.baseApis.crypto.cryptoStore.unmarkSessionsNeedingBackup(sessions); remaining = await this.baseApis.crypto.cryptoStore.countSessionsNeedingBackup(); @@ -636,7 +643,9 @@ export class Curve25519 implements BackupAlgorithm { return this.publicKey.encrypt(JSON.stringify(plainText)); } - public async decryptSessions(sessions: Record>): Promise[]> { + public async decryptSessions( + sessions: Record, + ): Promise[]> { const privKey = await this.getKey(); const decryption = new global.Olm.PkDecryption(); try { @@ -766,14 +775,12 @@ export class Aes256 implements BackupAlgorithm { return await encryptAES(JSON.stringify(plainText), this.key, data.session_id); } - async decryptSessions(sessions: Record): Promise[]> { + async decryptSessions(sessions: Record): Promise[]> { const keys = []; for (const [sessionId, sessionData] of Object.entries(sessions)) { try { - const decrypted = JSON.parse(await decryptAES( - sessionData.session_data, this.key, sessionId, - )); + const decrypted = JSON.parse(await decryptAES(sessionData.session_data, this.key, sessionId)); decrypted.session_id = sessionId; keys.push(decrypted); } catch (e) { diff --git a/src/crypto/dehydration.ts b/src/crypto/dehydration.ts index 0267e457e..80791b901 100644 --- a/src/crypto/dehydration.ts +++ b/src/crypto/dehydration.ts @@ -36,7 +36,7 @@ export interface IDehydratedDeviceKeyInfo { passphrase?: string; } -interface DeviceKeys { +export interface IDeviceKeys { algorithms: Array; device_id: string; // eslint-disable-line camelcase user_id: string; // eslint-disable-line camelcase @@ -44,7 +44,7 @@ interface DeviceKeys { signatures?: Signatures; } -export interface OneTimeKey { +export interface IOneTimeKey { key: string; fallback?: boolean; signatures?: Signatures; @@ -222,7 +222,7 @@ export class DehydrationManager { // send the keys to the server const deviceId = dehydrateResult.device_id; logger.log("Preparing device keys", deviceId); - const deviceKeys: DeviceKeys = { + const deviceKeys: IDeviceKeys = { algorithms: this.crypto.supportedAlgorithms, device_id: deviceId, user_id: this.crypto.userId, @@ -244,7 +244,7 @@ export class DehydrationManager { logger.log("Preparing one-time keys"); const oneTimeKeys = {}; for (const [keyId, key] of Object.entries(otks.curve25519)) { - const k: OneTimeKey = { key }; + const k: IOneTimeKey = { key }; const signature = account.sign(anotherjson.stringify(k)); k.signatures = { [this.crypto.userId]: { @@ -257,7 +257,7 @@ export class DehydrationManager { logger.log("Preparing fallback keys"); const fallbackKeys = {}; for (const [keyId, key] of Object.entries(fallbacks.curve25519)) { - const k: OneTimeKey = { key, fallback: true }; + const k: IOneTimeKey = { key, fallback: true }; const signature = account.sign(anotherjson.stringify(k)); k.signatures = { [this.crypto.userId]: { diff --git a/src/crypto/index.ts b/src/crypto/index.ts index a33a36f98..39a976964 100644 --- a/src/crypto/index.ts +++ b/src/crypto/index.ts @@ -44,7 +44,7 @@ import { IAddSecretStorageKeyOpts, ISecretStorageKeyInfo } from "./api"; import { OutgoingRoomKeyRequestManager } from './OutgoingRoomKeyRequestManager'; import { IndexedDBCryptoStore } from './store/indexeddb-crypto-store'; import { ReciprocateQRCode, SCAN_QR_CODE_METHOD, SHOW_QR_CODE_METHOD } from './verification/QRCode'; -import { SAS } from './verification/SAS'; +import { SAS as SASVerification } from './verification/SAS'; import { keyFromPassphrase } from './key_passphrase'; import { decodeRecoveryKey, encodeRecoveryKey } from './recoverykey'; import { VerificationRequest } from "./verification/request/VerificationRequest"; @@ -53,7 +53,7 @@ import { ToDeviceChannel, ToDeviceRequests } from "./verification/request/ToDevi import { IllegalMethod } from "./verification/IllegalMethod"; import { KeySignatureUploadError } from "../errors"; import { decryptAES, encryptAES, calculateKeyCheck } from './aes'; -import { DehydrationManager } from './dehydration'; +import { DehydrationManager, IDeviceKeys, IOneTimeKey } from './dehydration'; import { BackupManager } from "./backup"; import { IStore } from "../store"; import { Room } from "../models/room"; @@ -61,7 +61,7 @@ import { RoomMember } from "../models/room-member"; import { MatrixEvent } from "../models/event"; import { MatrixClient, IKeysUploadResponse, SessionStore, CryptoStore, ISignedKey } from "../client"; import type { EncryptionAlgorithm, DecryptionAlgorithm } from "./algorithms/base"; -import type { RoomList } from "./RoomList"; +import type { IRoomEncryption, RoomList } from "./RoomList"; import { IRecoveryKey, IEncryptedEventInfo } from "./api"; import { IKeyBackupInfo } from "./keybackup"; import { ISyncStateData } from "../sync"; @@ -70,7 +70,7 @@ const DeviceVerification = DeviceInfo.DeviceVerification; const defaultVerificationMethods = { [ReciprocateQRCode.NAME]: ReciprocateQRCode, - [SAS.NAME]: SAS, + [SASVerification.NAME]: SASVerification, // These two can't be used for actual verification, but we do // need to be able to define them here for the verification flows @@ -82,10 +82,13 @@ const defaultVerificationMethods = { /** * verification method names */ -export const verificationMethods = { - RECIPROCATE_QR_CODE: ReciprocateQRCode.NAME, - SAS: SAS.NAME, -}; +// legacy export identifier +export enum verificationMethods { + RECIPROCATE_QR_CODE = ReciprocateQRCode.NAME, + SAS = SASVerification.NAME, +} + +export type VerificationMethod = verificationMethods; export function isCryptoAvailable(): boolean { return Boolean(global.Olm); @@ -142,6 +145,10 @@ interface IDeviceVerificationUpgrade { crossSigningInfo: CrossSigningInfo; } +export interface ICheckOwnCrossSigningTrustOpts { + allowPrivateKeyRequests?: boolean; +} + /** * @typedef {Object} module:crypto~OlmSessionResult * @property {module:crypto/deviceinfo} device device info @@ -1418,7 +1425,7 @@ export class Crypto extends EventEmitter { */ async checkOwnCrossSigningTrust({ allowPrivateKeyRequests = false, - } = {}) { + }: ICheckOwnCrossSigningTrustOpts = {}): Promise { const userId = this.userId; // Before proceeding, ensure our cross-signing public keys have been @@ -1772,7 +1779,7 @@ export class Crypto extends EventEmitter { return this.signObject(deviceKeys).then(() => { return this.baseApis.uploadKeysRequest({ - device_keys: deviceKeys, + device_keys: deviceKeys as Required, }); }); } @@ -1904,9 +1911,9 @@ export class Crypto extends EventEmitter { private async uploadOneTimeKeys() { const promises = []; - const fallbackJson = {}; + const fallbackJson: Record = {}; if (this.getNeedsNewFallback()) { - const fallbackKeys = await this.olmDevice.getFallbackKey(); + const fallbackKeys = await this.olmDevice.getFallbackKey() as Record>; for (const [keyId, key] of Object.entries(fallbackKeys.curve25519)) { const k = { key, fallback: true }; fallbackJson["signed_curve25519:" + keyId] = k; @@ -2252,7 +2259,7 @@ export class Crypto extends EventEmitter { public async legacyDeviceVerification( userId: string, deviceId: string, - method: string, + method: VerificationMethod, ): VerificationRequest { const transactionId = ToDeviceChannel.makeTransactionId(); const channel = new ToDeviceChannel( @@ -2465,7 +2472,7 @@ export class Crypto extends EventEmitter { */ public async setRoomEncryption( roomId: string, - config: any, // TODO types + config: IRoomEncryption, inhibitDeviceQuery?: boolean, ): Promise { // ignore crypto events with no algorithm defined @@ -2522,8 +2529,8 @@ export class Crypto extends EventEmitter { crypto: this, olmDevice: this.olmDevice, baseApis: this.baseApis, - roomId: roomId, - config: config, + roomId, + config, }); this.roomEncryptors[roomId] = alg; @@ -2878,7 +2885,7 @@ export class Crypto extends EventEmitter { */ public async onCryptoEvent(event: MatrixEvent): Promise { const roomId = event.getRoomId(); - const content = event.getContent(); + const content = event.getContent(); try { // inhibit the device list refresh for now - it will happen once we've diff --git a/src/crypto/keybackup.ts b/src/crypto/keybackup.ts index 13c5621b9..4d8981ff8 100644 --- a/src/crypto/keybackup.ts +++ b/src/crypto/keybackup.ts @@ -24,6 +24,7 @@ export interface IKeyBackupSession { ciphertext: string; ephemeral: string; mac: string; + iv: string; }; } diff --git a/src/crypto/olmlib.ts b/src/crypto/olmlib.ts index 7a5e3a26c..d00c249ea 100644 --- a/src/crypto/olmlib.ts +++ b/src/crypto/olmlib.ts @@ -28,7 +28,7 @@ import OlmDevice from "./OlmDevice"; import { DeviceInfo } from "./deviceinfo"; import { logger } from '../logger'; import * as utils from "../utils"; -import { OneTimeKey } from "./dehydration"; +import { IOneTimeKey } from "./dehydration"; import { MatrixClient } from "../client"; enum Algorithm { @@ -407,7 +407,7 @@ export async function ensureOlmSessionsForDevices( async function _verifyKeyAndStartSession( olmDevice: OlmDevice, - oneTimeKey: OneTimeKey, + oneTimeKey: IOneTimeKey, userId: string, deviceInfo: DeviceInfo, ): Promise { @@ -465,7 +465,7 @@ export interface IObject { */ export async function verifySignature( olmDevice: OlmDevice, - obj: OneTimeKey | IObject, + obj: IOneTimeKey | IObject, signingUserId: string, signingDeviceId: string, signingKey: string, diff --git a/src/filter.ts b/src/filter.ts index 859aeb0c7..618c727c3 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -39,7 +39,7 @@ function setProp(obj: object, keyNesting: string, val: any) { } /* eslint-disable camelcase */ -interface IFilterDefinition { +export interface IFilterDefinition { event_fields?: string[]; event_format?: "client" | "federation"; presence?: IFilterComponent; @@ -47,7 +47,7 @@ interface IFilterDefinition { room?: IRoomFilter; } -interface IRoomEventFilter extends IFilterComponent { +export interface IRoomEventFilter extends IFilterComponent { lazy_load_members?: boolean; include_redundant_members?: boolean; } diff --git a/src/models/MSC3089Branch.ts b/src/models/MSC3089Branch.ts index 87acee0f8..7bb8c356e 100644 --- a/src/models/MSC3089Branch.ts +++ b/src/models/MSC3089Branch.ts @@ -70,8 +70,8 @@ export class MSC3089Branch { * @param {string} name The new name for this file. * @returns {Promise} Resolves when complete. */ - public setName(name: string): Promise { - return this.client.sendStateEvent(this.roomId, UNSTABLE_MSC3089_BRANCH.name, { + public async setName(name: string): Promise { + await this.client.sendStateEvent(this.roomId, UNSTABLE_MSC3089_BRANCH.name, { ...this.indexEvent.getContent(), name: name, }, this.id); diff --git a/src/models/MSC3089TreeSpace.ts b/src/models/MSC3089TreeSpace.ts index 77966d9fb..20585eab0 100644 --- a/src/models/MSC3089TreeSpace.ts +++ b/src/models/MSC3089TreeSpace.ts @@ -111,8 +111,8 @@ export class MSC3089TreeSpace { * @param {string} name The new name for the space. * @returns {Promise} Resolves when complete. */ - public setName(name: string): Promise { - return this.client.sendStateEvent(this.roomId, EventType.RoomName, { name }, ""); + public async setName(name: string): Promise { + await this.client.sendStateEvent(this.roomId, EventType.RoomName, { name }, ""); } /** @@ -190,7 +190,7 @@ export class MSC3089TreeSpace { } pls['users'] = users; - return this.client.sendStateEvent(this.roomId, EventType.RoomPowerLevels, pls, ""); + await this.client.sendStateEvent(this.roomId, EventType.RoomPowerLevels, pls, ""); } /** diff --git a/src/models/room.ts b/src/models/room.ts index 9970ae595..716da1214 100644 --- a/src/models/room.ts +++ b/src/models/room.ts @@ -31,7 +31,7 @@ import { IRoomSummary, RoomSummary } from "./room-summary"; import { logger } from '../logger'; import { ReEmitter } from '../ReEmitter'; import { EventType, RoomCreateTypeField, RoomType } from "../@types/event"; -import { IRoomVersionsCapability, MatrixClient, RoomVersionStability } from "../client"; +import { IRoomVersionsCapability, MatrixClient, PendingEventOrdering, RoomVersionStability } from "../client"; import { ResizeMethod } from "../@types/partials"; import { Filter } from "../filter"; import { RoomState } from "./room-state"; @@ -64,7 +64,7 @@ function synthesizeReceipt(userId: string, event: MatrixEvent, receiptType: stri interface IOpts { storageToken?: string; - pendingEventOrdering?: "chronological" | "detached"; + pendingEventOrdering?: PendingEventOrdering; timelineSupport?: boolean; unstableClientRelationAggregation?: boolean; lazyLoadMembers?: boolean; @@ -218,7 +218,7 @@ export class Room extends EventEmitter { this.setMaxListeners(100); this.reEmitter = new ReEmitter(this); - opts.pendingEventOrdering = opts.pendingEventOrdering || "chronological"; + opts.pendingEventOrdering = opts.pendingEventOrdering || PendingEventOrdering.Chronological; if (["chronological", "detached"].indexOf(opts.pendingEventOrdering) === -1) { throw new Error( "opts.pendingEventOrdering MUST be either 'chronological' or " + @@ -665,8 +665,7 @@ export class Room extends EventEmitter { private async loadMembers(): Promise<{ memberEvents: MatrixEvent[], fromServer: boolean }> { // were the members loaded from the server? let fromServer = false; - let rawMembersEvents = - await this.client.store.getOutOfBandMembers(this.roomId); + let rawMembersEvents = await this.client.store.getOutOfBandMembers(this.roomId); if (rawMembersEvents === null) { fromServer = true; rawMembersEvents = await this.loadMembersFromServer(); diff --git a/src/models/search-result.js b/src/models/search-result.js deleted file mode 100644 index fcf8bb43f..000000000 --- a/src/models/search-result.js +++ /dev/null @@ -1,60 +0,0 @@ -/* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -/** - * @module models/search-result - */ - -import { EventContext } from "./event-context"; - -/** - * Construct a new SearchResult - * - * @param {number} rank where this SearchResult ranks in the results - * @param {event-context.EventContext} eventContext the matching event and its - * context - * - * @constructor - */ -export function SearchResult(rank, eventContext) { - this.rank = rank; - this.context = eventContext; -} - -/** - * Create a SearchResponse from the response to /search - * @static - * @param {Object} jsonObj - * @param {function} eventMapper - * @return {SearchResult} - */ - -SearchResult.fromJson = function(jsonObj, eventMapper) { - const jsonContext = jsonObj.context || {}; - const events_before = jsonContext.events_before || []; - const events_after = jsonContext.events_after || []; - - const context = new EventContext(eventMapper(jsonObj.result)); - - context.setPaginateToken(jsonContext.start, true); - context.addEvents(events_before.map(eventMapper), true); - context.addEvents(events_after.map(eventMapper), false); - context.setPaginateToken(jsonContext.end, false); - - return new SearchResult(jsonObj.rank, context); -}; - diff --git a/src/models/search-result.ts b/src/models/search-result.ts new file mode 100644 index 000000000..83271fba2 --- /dev/null +++ b/src/models/search-result.ts @@ -0,0 +1,60 @@ +/* +Copyright 2015 - 2021 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/** + * @module models/search-result + */ + +import { EventContext } from "./event-context"; +import { EventMapper } from "../event-mapper"; +import { IResultContext, ISearchResult } from "../@types/search"; + +export class SearchResult { + /** + * Create a SearchResponse from the response to /search + * @static + * @param {Object} jsonObj + * @param {function} eventMapper + * @return {SearchResult} + */ + + static fromJson(jsonObj: ISearchResult, eventMapper: EventMapper): SearchResult { + const jsonContext = jsonObj.context || {} as IResultContext; + const eventsBefore = jsonContext.events_before || []; + const eventsAfter = jsonContext.events_after || []; + + const context = new EventContext(eventMapper(jsonObj.result)); + + context.setPaginateToken(jsonContext.start, true); + context.addEvents(eventsBefore.map(eventMapper), true); + context.addEvents(eventsAfter.map(eventMapper), false); + context.setPaginateToken(jsonContext.end, false); + + return new SearchResult(jsonObj.rank, context); + } + + /** + * Construct a new SearchResult + * + * @param {number} rank where this SearchResult ranks in the results + * @param {event-context.EventContext} context the matching event and its + * context + * + * @constructor + */ + constructor(public readonly rank: number, public readonly context: EventContext) {} +} + diff --git a/src/store/index.ts b/src/store/index.ts index 80cdc8a1a..e835d3791 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -22,6 +22,7 @@ import { MatrixEvent } from "../models/event"; import { Filter } from "../filter"; import { RoomSummary } from "../models/room-summary"; import { IMinimalEvent, IGroups, IRooms } from "../sync-accumulator"; +import { IStartClientOpts } from "../client"; export interface ISavedSync { nextBatch: string; @@ -35,6 +36,8 @@ export interface ISavedSync { * @constructor */ export interface IStore { + readonly accountData: Record; // type : content + /** @return {Promise} whether or not the database was newly created in this session. */ isNewlyCreated(): Promise; @@ -194,7 +197,7 @@ export interface IStore { /** * Save does nothing as there is no backing data store. */ - save(force: boolean): void; + save(force?: boolean): void; /** * Startup does nothing. @@ -222,13 +225,13 @@ export interface IStore { */ deleteAllData(): Promise; - getOutOfBandMembers(roomId: string): Promise; + getOutOfBandMembers(roomId: string): Promise; - setOutOfBandMembers(roomId: string, membershipEvents: MatrixEvent[]): Promise; + setOutOfBandMembers(roomId: string, membershipEvents: object[]): Promise; clearOutOfBandMembers(roomId: string): Promise; - getClientOptions(): Promise; + getClientOptions(): Promise; storeClientOptions(options: object): Promise; } diff --git a/src/store/memory.ts b/src/store/memory.ts index 452eb0c9a..94090f9c5 100644 --- a/src/store/memory.ts +++ b/src/store/memory.ts @@ -59,7 +59,7 @@ export class MemoryStore implements IStore { // filterId: Filter // } private filters: Record> = {}; - private accountData: Record = {}; // type : content + public accountData: Record = {}; // type : content private readonly localStorage: Storage; private oobMembers: Record = {}; // roomId: [member events] private clientOptions = {}; diff --git a/src/store/stub.ts b/src/store/stub.ts index 1a136d1d1..97ed0165e 100644 --- a/src/store/stub.ts +++ b/src/store/stub.ts @@ -33,9 +33,10 @@ import { RoomSummary } from "../models/room-summary"; * @constructor */ export class StubStore implements IStore { + public readonly accountData = {}; // stub private fromToken: string = null; - /** @return {Promise} whether or not the database was newly created in this session. */ + /** @return {Promise} whether or not the database was newly created in this session. */ public isNewlyCreated(): Promise { return Promise.resolve(true); } diff --git a/src/sync.ts b/src/sync.ts index 831c7b084..465c31983 100644 --- a/src/sync.ts +++ b/src/sync.ts @@ -318,7 +318,7 @@ export class SyncApi { this._peekRoom = this.createRoom(roomId); return this.client.roomInitialSync(roomId, 20).then((response) => { // make sure things are init'd - response.messages = response.messages || {}; + response.messages = response.messages || { chunk: [] }; response.messages.chunk = response.messages.chunk || []; response.state = response.state || []; From 3bff5430f69ad187f195810ccd446cdcd2e896b2 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Sat, 10 Jul 2021 15:55:19 +0100 Subject: [PATCH 2/6] delint --- src/@types/search.ts | 6 +++--- src/client.ts | 6 ++++-- src/crypto/backup.ts | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/@types/search.ts b/src/@types/search.ts index f1af733cd..0aa8a34a4 100644 --- a/src/@types/search.ts +++ b/src/@types/search.ts @@ -64,9 +64,9 @@ export interface IResultRoomEvents { next_batch?: string; order: number; results: string[]; - } + }; }; - } + }; next_batch?: string; } @@ -98,7 +98,7 @@ export interface ISearchRequestBody { group_by: { key: GroupKey; }[]; - } + }; }; }; } diff --git a/src/client.ts b/src/client.ts index 516a3b0dd..0c2a83f39 100644 --- a/src/client.ts +++ b/src/client.ts @@ -546,7 +546,7 @@ interface IOpenIDToken { interface IRoomInitialSyncResponse { room_id: string; - membership: "invite" | "join" | "leave" | "ban", + membership: "invite" | "join" | "leave" | "ban"; messages?: { start?: string; end?: string; @@ -633,7 +633,7 @@ interface IDownloadKeyResult { device_display_name: string; }; }; - } + }; }; } @@ -6796,6 +6796,7 @@ export class MatrixClient extends EventEmitter { * @return {Promise} Resolves: Object with room_id and servers. * @return {module:http-api.MatrixError} Rejects: with an error response. */ + // eslint-disable-next-line camelcase public resolveRoomAlias(roomAlias: string, callback?: Callback): Promise<{ room_id: string, servers: string[] }> { // TODO: deprecate this or getRoomIdForAlias const path = utils.encodeUri("/directory/room/$alias", { $alias: roomAlias }); @@ -6961,6 +6962,7 @@ export class MatrixClient extends EventEmitter { userId: string, info?: string, callback?: Callback, + // eslint-disable-next-line camelcase ): Promise<{ avatar_url?: string, displayname?: string }> { if (utils.isFunction(info)) { callback = info as any as Callback; // legacy diff --git a/src/crypto/backup.ts b/src/crypto/backup.ts index fbbdec933..82f90a6c7 100644 --- a/src/crypto/backup.ts +++ b/src/crypto/backup.ts @@ -98,7 +98,7 @@ export interface IKeyBackup { [sessionId: string]: IKeyBackupSession; }; }; - } + }; } /** From e3f7c848d7f725a09d2542740c6e24d82d4cd322 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 12 Jul 2021 08:44:34 +0100 Subject: [PATCH 3/6] iterate types based on PR review --- src/client.ts | 7 +++---- src/crypto/EncryptionSetup.ts | 16 ++++++++++++++++ src/filter.ts | 2 +- src/models/room.ts | 7 ++++--- src/store/index.ts | 6 +++--- src/store/indexeddb.ts | 6 +++--- src/store/memory.ts | 9 +++++---- src/store/stub.ts | 6 +++--- 8 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/client.ts b/src/client.ts index 0c2a83f39..7cc9c601a 100644 --- a/src/client.ts +++ b/src/client.ts @@ -490,6 +490,7 @@ interface IKeyBackupPath { } interface IMediaConfig { + [key: string]: any; // extensible "m.upload.size"?: number; } @@ -4374,14 +4375,12 @@ export class MatrixClient extends EventEmitter { // eslint-disable-next-line camelcase public setProfileInfo(info: "avatar_url", data: { avatar_url: string }, callback?: Callback): Promise<{}>; public setProfileInfo(info: "displayname", data: { displayname: string }, callback?: Callback): Promise<{}>; - public setProfileInfo(info: string, data: object, callback?: Callback): Promise<{}> { + public setProfileInfo(info: "avatar_url" | "displayname", data: object, callback?: Callback): Promise<{}> { const path = utils.encodeUri("/profile/$userId/$info", { $userId: this.credentials.userId, $info: info, }); - return this.http.authedRequest( - callback, "PUT", path, undefined, data, - ); + return this.http.authedRequest(callback, "PUT", path, undefined, data); } /** diff --git a/src/crypto/EncryptionSetup.ts b/src/crypto/EncryptionSetup.ts index 8b538afa2..faa4bd2e8 100644 --- a/src/crypto/EncryptionSetup.ts +++ b/src/crypto/EncryptionSetup.ts @@ -1,3 +1,19 @@ +/* +Copyright 2021 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + import { logger } from "../logger"; import { MatrixEvent } from "../models/event"; import { EventEmitter } from "events"; diff --git a/src/filter.ts b/src/filter.ts index 618c727c3..8cbd2a67e 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -86,7 +86,7 @@ export class Filter { * @param {Object} jsonObj * @return {Filter} */ - static fromJson(userId: string, filterId: string, jsonObj: IFilterDefinition): Filter { + public static fromJson(userId: string, filterId: string, jsonObj: IFilterDefinition): Filter { const filter = new Filter(userId, filterId); filter.setDefinition(jsonObj); return filter; diff --git a/src/models/room.ts b/src/models/room.ts index 716da1214..65abbd099 100644 --- a/src/models/room.ts +++ b/src/models/room.ts @@ -25,7 +25,7 @@ import { EventTimeline } from "./event-timeline"; import { getHttpUriForMxc } from "../content-repo"; import * as utils from "../utils"; import { normalize } from "../utils"; -import { EventStatus, MatrixEvent } from "./event"; +import { EventStatus, IEvent, MatrixEvent } from "./event"; import { RoomMember } from "./room-member"; import { IRoomSummary, RoomSummary } from "./room-summary"; import { logger } from '../logger'; @@ -35,6 +35,7 @@ import { IRoomVersionsCapability, MatrixClient, PendingEventOrdering, RoomVersio import { ResizeMethod } from "../@types/partials"; import { Filter } from "../filter"; import { RoomState } from "./room-state"; +import { IMinimalEvent } from "../sync-accumulator"; // These constants are used as sane defaults when the homeserver doesn't support // the m.room_versions capability. In practice, KNOWN_SAFE_ROOM_VERSION should be @@ -649,7 +650,7 @@ export class Room extends EventEmitter { } } - private async loadMembersFromServer(): Promise { + private async loadMembersFromServer(): Promise { const lastSyncToken = this.client.store.getSyncToken(); const queryString = utils.encodeParams({ not_membership: "leave", @@ -712,7 +713,7 @@ export class Room extends EventEmitter { if (fromServer) { const oobMembers = this.currentState.getMembers() .filter((m) => m.isOutOfBand()) - .map((m) => m.events.member.event); + .map((m) => m.events.member.event as IEvent); logger.log(`LL: telling store to write ${oobMembers.length}` + ` members for room ${this.roomId}`); const store = this.client.store; diff --git a/src/store/index.ts b/src/store/index.ts index e835d3791..01650f86a 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -18,7 +18,7 @@ import { EventType } from "../@types/event"; import { Group } from "../models/group"; import { Room } from "../models/room"; import { User } from "../models/user"; -import { MatrixEvent } from "../models/event"; +import { IEvent, MatrixEvent } from "../models/event"; import { Filter } from "../filter"; import { RoomSummary } from "../models/room-summary"; import { IMinimalEvent, IGroups, IRooms } from "../sync-accumulator"; @@ -225,9 +225,9 @@ export interface IStore { */ deleteAllData(): Promise; - getOutOfBandMembers(roomId: string): Promise; + getOutOfBandMembers(roomId: string): Promise; - setOutOfBandMembers(roomId: string, membershipEvents: object[]): Promise; + setOutOfBandMembers(roomId: string, membershipEvents: IEvent[]): Promise; clearOutOfBandMembers(roomId: string): Promise; diff --git a/src/store/indexeddb.ts b/src/store/indexeddb.ts index f7f9fb8b9..aed61dae2 100644 --- a/src/store/indexeddb.ts +++ b/src/store/indexeddb.ts @@ -22,7 +22,7 @@ import { MemoryStore, IOpts as IBaseOpts } from "./memory"; import { LocalIndexedDBStoreBackend } from "./indexeddb-local-backend.js"; import { RemoteIndexedDBStoreBackend } from "./indexeddb-remote-backend.js"; import { User } from "../models/user"; -import { MatrixEvent } from "../models/event"; +import { IEvent, MatrixEvent } from "../models/event"; import { logger } from '../logger'; import { ISavedSync } from "./index"; @@ -247,7 +247,7 @@ export class IndexedDBStore extends MemoryStore { * @returns {event[]} the events, potentially an empty array if OOB loading didn't yield any new members * @returns {null} in case the members for this room haven't been stored yet */ - public getOutOfBandMembers = this.degradable((roomId: string): Promise => { + public getOutOfBandMembers = this.degradable((roomId: string): Promise => { return this.backend.getOutOfBandMembers(roomId); }, "getOutOfBandMembers"); @@ -259,7 +259,7 @@ export class IndexedDBStore extends MemoryStore { * @param {event[]} membershipEvents the membership events to store * @returns {Promise} when all members have been stored */ - public setOutOfBandMembers = this.degradable((roomId: string, membershipEvents: MatrixEvent[]): Promise => { + public setOutOfBandMembers = this.degradable((roomId: string, membershipEvents: IEvent[]): Promise => { super.setOutOfBandMembers(roomId, membershipEvents); return this.backend.setOutOfBandMembers(roomId, membershipEvents); }, "setOutOfBandMembers"); diff --git a/src/store/memory.ts b/src/store/memory.ts index 94090f9c5..00dceb711 100644 --- a/src/store/memory.ts +++ b/src/store/memory.ts @@ -23,12 +23,13 @@ import { EventType } from "../@types/event"; import { Group } from "../models/group"; import { Room } from "../models/room"; import { User } from "../models/user"; -import { MatrixEvent } from "../models/event"; +import { IEvent, MatrixEvent } from "../models/event"; import { RoomState } from "../models/room-state"; import { RoomMember } from "../models/room-member"; import { Filter } from "../filter"; import { ISavedSync, IStore } from "./index"; import { RoomSummary } from "../models/room-summary"; +import { IMinimalEvent } from "../sync-accumulator"; function isValidFilterId(filterId: string): boolean { const isValidStr = typeof filterId === "string" && @@ -61,7 +62,7 @@ export class MemoryStore implements IStore { private filters: Record> = {}; public accountData: Record = {}; // type : content private readonly localStorage: Storage; - private oobMembers: Record = {}; // roomId: [member events] + private oobMembers: Record = {}; // roomId: [member events] private clientOptions = {}; constructor(opts: IOpts = {}) { @@ -415,7 +416,7 @@ export class MemoryStore implements IStore { * @returns {event[]} the events, potentially an empty array if OOB loading didn't yield any new members * @returns {null} in case the members for this room haven't been stored yet */ - public getOutOfBandMembers(roomId: string): Promise { + public getOutOfBandMembers(roomId: string): Promise { return Promise.resolve(this.oobMembers[roomId] || null); } @@ -427,7 +428,7 @@ export class MemoryStore implements IStore { * @param {event[]} membershipEvents the membership events to store * @returns {Promise} when all members have been stored */ - public setOutOfBandMembers(roomId: string, membershipEvents: MatrixEvent[]): Promise { + public setOutOfBandMembers(roomId: string, membershipEvents: IEvent[]): Promise { this.oobMembers[roomId] = membershipEvents; return Promise.resolve(); } diff --git a/src/store/stub.ts b/src/store/stub.ts index 97ed0165e..f7adb9b3e 100644 --- a/src/store/stub.ts +++ b/src/store/stub.ts @@ -23,7 +23,7 @@ import { EventType } from "../@types/event"; import { Group } from "../models/group"; import { Room } from "../models/room"; import { User } from "../models/user"; -import { MatrixEvent } from "../models/event"; +import { IEvent, MatrixEvent } from "../models/event"; import { Filter } from "../filter"; import { ISavedSync, IStore } from "./index"; import { RoomSummary } from "../models/room-summary"; @@ -265,11 +265,11 @@ export class StubStore implements IStore { return Promise.resolve(); } - public getOutOfBandMembers(): Promise { + public getOutOfBandMembers(): Promise { return Promise.resolve(null); } - public setOutOfBandMembers(roomId: string, membershipEvents: MatrixEvent[]): Promise { + public setOutOfBandMembers(roomId: string, membershipEvents: IEvent[]): Promise { return Promise.resolve(); } From b33429317cf2d25d37aca06c7c7b57e7aac5114b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 12 Jul 2021 09:02:47 +0100 Subject: [PATCH 4/6] Fix setTimeout/setInterval typing --- src/@types/global.d.ts | 6 ++++++ src/client.ts | 4 ++-- src/crypto/DeviceList.ts | 2 +- src/crypto/OutgoingRoomKeyRequestManager.ts | 4 ++-- src/sync.ts | 2 +- src/webrtc/call.ts | 2 +- 6 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index dc39bd730..d09d92f25 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -20,6 +20,12 @@ import "@matrix-org/olm"; export {}; declare global { + // use `number` as the return type in all cases for global.set{Interval,Timeout}, + // so we don't accidentally use the methods on NodeJS.Timeout - they only exist in a subset of environments. + // The overload for clear{Interval,Timeout} is resolved as expected. + function setInterval(handler: TimerHandler, timeout: number, ...arguments: any[]): number; + function setTimeout(handler: TimerHandler, timeout: number, ...arguments: any[]): number; + namespace NodeJS { interface Global { localStorage: Storage; diff --git a/src/client.ts b/src/client.ts index 7cc9c601a..c01b2b230 100644 --- a/src/client.ts +++ b/src/client.ts @@ -731,7 +731,7 @@ export class MatrixClient extends EventEmitter { protected syncLeftRoomsPromise: Promise; protected syncedLeftRooms = false; protected clientOpts: IStoredClientOpts; - protected clientWellKnownIntervalID: NodeJS.Timeout; + protected clientWellKnownIntervalID: number; protected canResetTimelineCallback: ResetTimelineCallback; // The pushprocessor caches useful things, so keep one and re-use it @@ -749,7 +749,7 @@ export class MatrixClient extends EventEmitter { protected clientWellKnownPromise: Promise; protected turnServers: ITurnServer[] = []; protected turnServersExpiry = 0; - protected checkTurnServersIntervalID: NodeJS.Timeout; + protected checkTurnServersIntervalID: number; protected exportedOlmDeviceToImport: IOlmDevice; protected txnCtr = 0; diff --git a/src/crypto/DeviceList.ts b/src/crypto/DeviceList.ts index 71d3d364d..890f3bfa3 100644 --- a/src/crypto/DeviceList.ts +++ b/src/crypto/DeviceList.ts @@ -102,7 +102,7 @@ export class DeviceList extends EventEmitter { // The time the save is scheduled for private savePromiseTime: number = null; // The timer used to delay the save - private saveTimer: NodeJS.Timeout = null; + private saveTimer: number = null; // True if we have fetched data from the server or loaded a non-empty // set of device data from the store private hasFetched: boolean = null; diff --git a/src/crypto/OutgoingRoomKeyRequestManager.ts b/src/crypto/OutgoingRoomKeyRequestManager.ts index a684b2c71..d01245722 100644 --- a/src/crypto/OutgoingRoomKeyRequestManager.ts +++ b/src/crypto/OutgoingRoomKeyRequestManager.ts @@ -78,7 +78,7 @@ export enum RoomKeyRequestState { export class OutgoingRoomKeyRequestManager { // handle for the delayed call to sendOutgoingRoomKeyRequests. Non-null // if the callback has been set, or if it is still running. - private sendOutgoingRoomKeyRequestsTimer: NodeJS.Timeout = null; + private sendOutgoingRoomKeyRequestsTimer: number = null; // sanity check to ensure that we don't end up with two concurrent runs // of sendOutgoingRoomKeyRequests @@ -366,7 +366,7 @@ export class OutgoingRoomKeyRequestManager { }); }; - this.sendOutgoingRoomKeyRequestsTimer = global.setTimeout( + this.sendOutgoingRoomKeyRequestsTimer = setTimeout( startSendingOutgoingRoomKeyRequests, SEND_KEY_REQUESTS_DELAY_MS, ); diff --git a/src/sync.ts b/src/sync.ts index 465c31983..3ac4489a5 100644 --- a/src/sync.ts +++ b/src/sync.ts @@ -135,7 +135,7 @@ export class SyncApi { private syncStateData: ISyncStateData = null; // additional data (eg. error object for failed sync) private catchingUp = false; private running = false; - private keepAliveTimer: NodeJS.Timeout = null; + private keepAliveTimer: number = null; private connectionReturnedDefer: IDeferred = null; private notifEvents: MatrixEvent[] = []; // accumulator of sync events in the current sync response private failedSyncCount = 0; // Number of consecutive failed /sync requests diff --git a/src/webrtc/call.ts b/src/webrtc/call.ts index ed1af7b90..05e64da2c 100644 --- a/src/webrtc/call.ts +++ b/src/webrtc/call.ts @@ -288,7 +288,7 @@ export class MatrixCall extends EventEmitter { // yet, null if we have but they didn't send a party ID. private opponentPartyId: string; private opponentCaps: CallCapabilities; - private inviteTimeout: NodeJS.Timeout; // in the browser it's 'number' + private inviteTimeout: number; // The logic of when & if a call is on hold is nontrivial and explained in is*OnHold // This flag represents whether we want the other party to be on hold From c0e16ac98c45134966f45ffdda1328e24f075c6d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 12 Jul 2021 09:10:27 +0100 Subject: [PATCH 5/6] Update some more --- src/@types/spaces.ts | 4 ++-- src/client.ts | 10 +++++----- src/event-mapper.ts | 6 +++--- src/sync.ts | 3 +-- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/@types/spaces.ts b/src/@types/spaces.ts index 17e677d44..da35bc0b9 100644 --- a/src/@types/spaces.ts +++ b/src/@types/spaces.ts @@ -14,12 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { IPublicRoomsChunk } from "../client"; +import { IPublicRoomsChunkRoom } from "../client"; // Types relating to Rooms of type `m.space` and related APIs /* eslint-disable camelcase */ -export interface ISpaceSummaryRoom extends IPublicRoomsChunk { +export interface ISpaceSummaryRoom extends IPublicRoomsChunkRoom { num_refs: number; room_type: string; } diff --git a/src/client.ts b/src/client.ts index c01b2b230..321836fb6 100644 --- a/src/client.ts +++ b/src/client.ts @@ -21,7 +21,7 @@ limitations under the License. import { EventEmitter } from "events"; import { ISyncStateData, SyncApi } from "./sync"; -import { EventStatus, IContent, IDecryptOptions, MatrixEvent } from "./models/event"; +import { EventStatus, IContent, IDecryptOptions, IEvent, MatrixEvent } from "./models/event"; import { StubStore } from "./store/stub"; import { createNewMatrixCall, MatrixCall } from "./webrtc/call"; import { Filter, IFilterDefinition } from "./filter"; @@ -556,7 +556,7 @@ interface IRoomInitialSyncResponse { state?: IStateEventWithRoomId[]; visibility: Visibility; account_data?: IMinimalEvent[]; - presence: any; // undocumented + presence: Partial; // legacy and undocumented, api is deprecated so this won't get attention } interface IJoinedMembersResponse { @@ -568,7 +568,7 @@ interface IJoinedMembersResponse { }; } -export interface IPublicRoomsChunk { +export interface IPublicRoomsChunkRoom { room_id: string; name?: string; avatar_url?: string; @@ -581,7 +581,7 @@ export interface IPublicRoomsChunk { } interface IPublicRoomsResponse { - chunk: IPublicRoomsChunk[]; + chunk: IPublicRoomsChunkRoom[]; next_batch?: string; prev_batch?: string; total_room_count_estimate?: number; @@ -657,7 +657,7 @@ export interface IInstance { icon?: string; fields: object; network_id: string; - // XXX: this is undocumented but we rely on it. + // XXX: this is undocumented but we rely on it: https://github.com/matrix-org/matrix-doc/issues/3203 instance_id: string; } diff --git a/src/event-mapper.ts b/src/event-mapper.ts index 84b8b306f..e0a5e421b 100644 --- a/src/event-mapper.ts +++ b/src/event-mapper.ts @@ -15,9 +15,9 @@ limitations under the License. */ import { MatrixClient } from "./client"; -import { MatrixEvent } from "./models/event"; +import { IEvent, MatrixEvent } from "./models/event"; -export type EventMapper = (obj: any) => MatrixEvent; +export type EventMapper = (obj: Partial) => MatrixEvent; export interface MapperOpts { preventReEmit?: boolean; @@ -28,7 +28,7 @@ export function eventMapperFor(client: MatrixClient, options: MapperOpts): Event const preventReEmit = Boolean(options.preventReEmit); const decrypt = options.decrypt !== false; - function mapper(plainOldJsObject) { + function mapper(plainOldJsObject: Partial) { const event = new MatrixEvent(plainOldJsObject); if (event.isEncrypted()) { if (!preventReEmit) { diff --git a/src/sync.ts b/src/sync.ts index 3ac4489a5..863329808 100644 --- a/src/sync.ts +++ b/src/sync.ts @@ -329,8 +329,7 @@ export class SyncApi { const stateEvents = response.state.map(client.getEventMapper()); const messages = response.messages.chunk.map(client.getEventMapper()); - // XXX: copypasted from /sync until we kill off this - // minging v1 API stuff) + // XXX: copypasted from /sync until we kill off this minging v1 API stuff) // handle presence events (User objects) if (response.presence && Array.isArray(response.presence)) { response.presence.map(client.getEventMapper()).forEach( From e38595e735b233071cecdc0e677b4db5764928b0 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 12 Jul 2021 09:14:44 +0100 Subject: [PATCH 6/6] remove unused import --- src/models/room.ts | 1 - src/store/memory.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/models/room.ts b/src/models/room.ts index 65abbd099..089e60fa8 100644 --- a/src/models/room.ts +++ b/src/models/room.ts @@ -35,7 +35,6 @@ import { IRoomVersionsCapability, MatrixClient, PendingEventOrdering, RoomVersio import { ResizeMethod } from "../@types/partials"; import { Filter } from "../filter"; import { RoomState } from "./room-state"; -import { IMinimalEvent } from "../sync-accumulator"; // These constants are used as sane defaults when the homeserver doesn't support // the m.room_versions capability. In practice, KNOWN_SAFE_ROOM_VERSION should be diff --git a/src/store/memory.ts b/src/store/memory.ts index 00dceb711..424989b67 100644 --- a/src/store/memory.ts +++ b/src/store/memory.ts @@ -29,7 +29,6 @@ import { RoomMember } from "../models/room-member"; import { Filter } from "../filter"; import { ISavedSync, IStore } from "./index"; import { RoomSummary } from "../models/room-summary"; -import { IMinimalEvent } from "../sync-accumulator"; function isValidFilterId(filterId: string): boolean { const isValidStr = typeof filterId === "string" &&