matrix-js-sdk 42.4.0 → 43.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/lib/@types/PushRules.d.ts +3 -1
- package/lib/@types/PushRules.d.ts.map +1 -1
- package/lib/@types/PushRules.js +2 -0
- package/lib/@types/PushRules.js.map +1 -1
- package/lib/@types/event.d.ts +1 -0
- package/lib/@types/event.d.ts.map +1 -1
- package/lib/@types/event.js.map +1 -1
- package/lib/@types/matrix-sdk-crypto-wasm.d.js.map +1 -1
- package/lib/client.d.ts +88 -37
- package/lib/client.d.ts.map +1 -1
- package/lib/client.js +72 -47
- package/lib/client.js.map +1 -1
- package/lib/crypto-api/index.d.ts +3 -3
- package/lib/crypto-api/index.d.ts.map +1 -1
- package/lib/crypto-api/index.js.map +1 -1
- package/lib/embedded.d.ts +17 -1
- package/lib/embedded.d.ts.map +1 -1
- package/lib/embedded.js +46 -1
- package/lib/embedded.js.map +1 -1
- package/lib/errors.d.ts +1 -1
- package/lib/errors.d.ts.map +1 -1
- package/lib/errors.js +2 -2
- package/lib/errors.js.map +1 -1
- package/lib/http-api/fetch.d.ts +7 -1
- package/lib/http-api/fetch.d.ts.map +1 -1
- package/lib/http-api/fetch.js +14 -5
- package/lib/http-api/fetch.js.map +1 -1
- package/lib/http-api/interface.d.ts +34 -8
- package/lib/http-api/interface.d.ts.map +1 -1
- package/lib/http-api/interface.js +10 -3
- package/lib/http-api/interface.js.map +1 -1
- package/lib/http-api/refresh.d.ts +30 -11
- package/lib/http-api/refresh.d.ts.map +1 -1
- package/lib/http-api/refresh.js +72 -5
- package/lib/http-api/refresh.js.map +1 -1
- package/lib/matrix.d.ts +1 -1
- package/lib/matrix.d.ts.map +1 -1
- package/lib/matrix.js.map +1 -1
- package/lib/matrixrtc/CallMembership.d.ts.map +1 -1
- package/lib/matrixrtc/CallMembership.js +20 -6
- package/lib/matrixrtc/CallMembership.js.map +1 -1
- package/lib/matrixrtc/IMembershipManager.d.ts +5 -0
- package/lib/matrixrtc/IMembershipManager.d.ts.map +1 -1
- package/lib/matrixrtc/IMembershipManager.js.map +1 -1
- package/lib/matrixrtc/MatrixRTCSession.d.ts +27 -1
- package/lib/matrixrtc/MatrixRTCSession.d.ts.map +1 -1
- package/lib/matrixrtc/MatrixRTCSession.js +58 -36
- package/lib/matrixrtc/MatrixRTCSession.js.map +1 -1
- package/lib/matrixrtc/MatrixRTCSessionManager.js +1 -1
- package/lib/matrixrtc/MatrixRTCSessionManager.js.map +1 -1
- package/lib/matrixrtc/MembershipManager.d.ts +76 -0
- package/lib/matrixrtc/MembershipManager.d.ts.map +1 -1
- package/lib/matrixrtc/MembershipManager.js +264 -47
- package/lib/matrixrtc/MembershipManager.js.map +1 -1
- package/lib/matrixrtc/index.d.ts +1 -1
- package/lib/matrixrtc/index.d.ts.map +1 -1
- package/lib/matrixrtc/index.js +1 -1
- package/lib/matrixrtc/index.js.map +1 -1
- package/lib/matrixrtc/membershipData/common.d.ts +8 -0
- package/lib/matrixrtc/membershipData/common.d.ts.map +1 -1
- package/lib/matrixrtc/membershipData/common.js +10 -0
- package/lib/matrixrtc/membershipData/common.js.map +1 -1
- package/lib/matrixrtc/membershipData/index.d.ts +1 -1
- package/lib/matrixrtc/membershipData/index.d.ts.map +1 -1
- package/lib/matrixrtc/membershipData/index.js +1 -1
- package/lib/matrixrtc/membershipData/index.js.map +1 -1
- package/lib/matrixrtc/types.d.ts +104 -11
- package/lib/matrixrtc/types.d.ts.map +1 -1
- package/lib/matrixrtc/types.js +263 -19
- package/lib/matrixrtc/types.js.map +1 -1
- package/lib/matrixrtc/utils.d.ts +22 -1
- package/lib/matrixrtc/utils.d.ts.map +1 -1
- package/lib/matrixrtc/utils.js +38 -0
- package/lib/matrixrtc/utils.js.map +1 -1
- package/lib/models/room-sticky-events.d.ts +7 -0
- package/lib/models/room-sticky-events.d.ts.map +1 -1
- package/lib/models/room-sticky-events.js +27 -0
- package/lib/models/room-sticky-events.js.map +1 -1
- package/lib/models/thread.d.ts.map +1 -1
- package/lib/models/thread.js +8 -0
- package/lib/models/thread.js.map +1 -1
- package/lib/oauth/authorize.js +1 -1
- package/lib/oauth/authorize.js.map +1 -1
- package/lib/oauth/index.d.ts +3 -5
- package/lib/oauth/index.d.ts.map +1 -1
- package/lib/oauth/index.js +7 -7
- package/lib/oauth/index.js.map +1 -1
- package/lib/oauth/tokenRefresher.d.ts +10 -4
- package/lib/oauth/tokenRefresher.d.ts.map +1 -1
- package/lib/oauth/tokenRefresher.js +34 -24
- package/lib/oauth/tokenRefresher.js.map +1 -1
- package/lib/receipt-accumulator.d.ts +10 -11
- package/lib/receipt-accumulator.d.ts.map +1 -1
- package/lib/receipt-accumulator.js +26 -25
- package/lib/receipt-accumulator.js.map +1 -1
- package/lib/room-summary-api.d.ts +71 -0
- package/lib/room-summary-api.d.ts.map +1 -0
- package/lib/room-summary-api.js +82 -0
- package/lib/room-summary-api.js.map +1 -0
- package/lib/rust-crypto/backup.d.ts +7 -0
- package/lib/rust-crypto/backup.d.ts.map +1 -1
- package/lib/rust-crypto/backup.js +41 -25
- package/lib/rust-crypto/backup.js.map +1 -1
- package/lib/sync.d.ts.map +1 -1
- package/lib/sync.js +6 -1
- package/lib/sync.js.map +1 -1
- package/package.json +6 -6
- package/src/@types/PushRules.ts +2 -0
- package/src/@types/event.ts +1 -1
- package/src/@types/matrix-sdk-crypto-wasm.d.ts +15 -12
- package/src/client.ts +105 -62
- package/src/crypto-api/index.ts +3 -3
- package/src/embedded.ts +61 -2
- package/src/errors.ts +2 -1
- package/src/http-api/fetch.ts +14 -5
- package/src/http-api/interface.ts +36 -8
- package/src/http-api/refresh.ts +101 -17
- package/src/matrix.ts +1 -1
- package/src/matrixrtc/CallMembership.ts +22 -4
- package/src/matrixrtc/IMembershipManager.ts +6 -0
- package/src/matrixrtc/MatrixRTCSession.ts +72 -49
- package/src/matrixrtc/MatrixRTCSessionManager.ts +1 -1
- package/src/matrixrtc/MembershipManager.ts +284 -61
- package/src/matrixrtc/index.ts +7 -1
- package/src/matrixrtc/membershipData/common.ts +12 -0
- package/src/matrixrtc/membershipData/index.ts +1 -1
- package/src/matrixrtc/types.ts +351 -23
- package/src/matrixrtc/utils.ts +54 -1
- package/src/models/room-sticky-events.ts +26 -1
- package/src/models/thread.ts +9 -0
- package/src/oauth/authorize.ts +1 -1
- package/src/oauth/index.ts +7 -8
- package/src/oauth/tokenRefresher.ts +15 -5
- package/src/receipt-accumulator.ts +30 -26
- package/src/room-summary-api.ts +114 -0
- package/src/rust-crypto/backup.ts +46 -33
- package/src/sync.ts +9 -1
|
@@ -14,7 +14,8 @@ See the License for the specific language governing permissions and
|
|
|
14
14
|
limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import { HTTPError, TokenRefreshLogoutError } from "../http-api/errors.ts";
|
|
18
|
+
import { type AccessTokens } from "../http-api/interface.ts";
|
|
18
19
|
import { OAuth2HTTPError } from "./error.ts";
|
|
19
20
|
import { type OAuth2 } from "./index.ts";
|
|
20
21
|
|
|
@@ -26,16 +27,15 @@ export class TokenRefresher {
|
|
|
26
27
|
|
|
27
28
|
public constructor(
|
|
28
29
|
private readonly auth: OAuth2,
|
|
29
|
-
private readonly onRefresh: (tokens: AccessTokens) => Promise<void>,
|
|
30
|
+
private readonly onRefresh: (tokens: AccessTokens) => void | Promise<void>,
|
|
30
31
|
) {}
|
|
31
32
|
|
|
32
33
|
/**
|
|
33
34
|
* Attempt token refresh using given refresh token
|
|
34
35
|
* @param refreshToken - refresh token to use in request with token issuer
|
|
35
|
-
* @returns tokens - Promise that resolves with new access and refresh tokens
|
|
36
36
|
* @throws when token refresh fails
|
|
37
37
|
*/
|
|
38
|
-
public
|
|
38
|
+
public async refreshTokens(refreshToken: string): Promise<AccessTokens> {
|
|
39
39
|
if (!this.inflightRefreshRequest) {
|
|
40
40
|
this.inflightRefreshRequest = this.getNewTokens(refreshToken);
|
|
41
41
|
}
|
|
@@ -52,7 +52,17 @@ export class TokenRefresher {
|
|
|
52
52
|
} finally {
|
|
53
53
|
this.inflightRefreshRequest = undefined;
|
|
54
54
|
}
|
|
55
|
-
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Revoke a token with the OP, e.g. as part of logging out.
|
|
59
|
+
* @param token - the token to revoke
|
|
60
|
+
* @param type - the type of token, acts as a hint to the OP
|
|
61
|
+
* @throws when revocation fails
|
|
62
|
+
*/
|
|
63
|
+
public async revokeToken(token: string, type?: "access_token" | "refresh_token"): Promise<void> {
|
|
64
|
+
await this.auth.revokeToken(token, type);
|
|
65
|
+
}
|
|
56
66
|
|
|
57
67
|
private shouldLogoutOnError(error: HTTPError): boolean {
|
|
58
68
|
// Treat as logout as per https://spec.matrix.org/v1.18/client-server-api/#refresh-token-grant
|
|
@@ -37,50 +37,57 @@ interface AccumulatedReceipt {
|
|
|
37
37
|
* the most recently received receipt in each thread.
|
|
38
38
|
*/
|
|
39
39
|
export class ReceiptAccumulator {
|
|
40
|
-
/** user_id -\> most-recently-received unthreaded receipt */
|
|
41
|
-
private unthreadedReadReceipts:
|
|
42
|
-
|
|
43
|
-
/** thread_id -\> user_id -\> most-recently-received receipt for this thread */
|
|
44
|
-
private threadedReadReceipts: MapWithDefault<string, Map<string, AccumulatedReceipt>> = new MapWithDefault(
|
|
40
|
+
/** user_id -\> receipt_type -\> most-recently-received unthreaded receipt */
|
|
41
|
+
private unthreadedReadReceipts: MapWithDefault<string, Map<ReceiptType, AccumulatedReceipt>> = new MapWithDefault(
|
|
45
42
|
() => new Map(),
|
|
46
43
|
);
|
|
47
44
|
|
|
45
|
+
/** thread_id -\> user_id -\> receipt_type -\> most-recently-received receipt for this thread */
|
|
46
|
+
private threadedReadReceipts: MapWithDefault<string, MapWithDefault<string, Map<ReceiptType, AccumulatedReceipt>>> =
|
|
47
|
+
new MapWithDefault(() => new MapWithDefault(() => new Map()));
|
|
48
|
+
|
|
48
49
|
/**
|
|
49
50
|
* Provide an unthreaded receipt for this user. Overwrites any other
|
|
50
|
-
* unthreaded receipt we have for this user.
|
|
51
|
+
* unthreaded receipt of this type we have for this user.
|
|
51
52
|
*/
|
|
52
53
|
private setUnthreaded(userId: string, receipt: AccumulatedReceipt): void {
|
|
53
|
-
this.unthreadedReadReceipts.set(
|
|
54
|
+
this.unthreadedReadReceipts.getOrCreate(userId).set(receipt.type, receipt);
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
/**
|
|
57
58
|
* Provide a receipt for this user in this thread. Overwrites any other
|
|
58
|
-
* receipt we have for this user in this thread.
|
|
59
|
+
* receipt of this type we have for this user in this thread.
|
|
59
60
|
*/
|
|
60
61
|
private setThreaded(threadId: string, userId: string, receipt: AccumulatedReceipt): void {
|
|
61
|
-
this.threadedReadReceipts.getOrCreate(threadId).set(
|
|
62
|
+
this.threadedReadReceipts.getOrCreate(threadId).getOrCreate(userId).set(receipt.type, receipt);
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
/**
|
|
65
|
-
* @
|
|
66
|
-
* most recently-received unthreaded receipts for each user.
|
|
67
|
-
* @yields pairs of [userId, AccumulatedReceipt]
|
|
66
|
+
* @yields pairs of [userId, AccumulatedReceipt] for every receipt type.
|
|
68
67
|
*/
|
|
69
|
-
private
|
|
70
|
-
|
|
68
|
+
private *allForUsers(
|
|
69
|
+
receiptsByUser: ReadonlyMap<string, ReadonlyMap<ReceiptType, AccumulatedReceipt>>,
|
|
70
|
+
): IterableIterator<[string, AccumulatedReceipt]> {
|
|
71
|
+
for (const [userId, receiptsForUser] of receiptsByUser) {
|
|
72
|
+
for (const receipt of receiptsForUser.values()) {
|
|
73
|
+
yield [userId, receipt];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
71
76
|
}
|
|
72
77
|
|
|
73
78
|
/**
|
|
74
|
-
* @
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
79
|
+
* @yields all unthreaded receipts of each type for each user.
|
|
80
|
+
*/
|
|
81
|
+
private *allUnthreaded(): IterableIterator<[string, AccumulatedReceipt]> {
|
|
82
|
+
yield* this.allForUsers(this.unthreadedReadReceipts);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @yields all threaded receipts of each type for each user, in all threads.
|
|
78
87
|
*/
|
|
79
88
|
private *allThreaded(): IterableIterator<[string, AccumulatedReceipt]> {
|
|
80
89
|
for (const receiptsForThread of this.threadedReadReceipts.values()) {
|
|
81
|
-
|
|
82
|
-
yield e;
|
|
83
|
-
}
|
|
90
|
+
yield* this.allForUsers(receiptsForThread);
|
|
84
91
|
}
|
|
85
92
|
}
|
|
86
93
|
|
|
@@ -101,11 +108,8 @@ export class ReceiptAccumulator {
|
|
|
101
108
|
// but they are keyed in the event as:
|
|
102
109
|
// content:{ $event_id: { $receipt_type: { $user_id: {json} }}}
|
|
103
110
|
// so store them in the former so we can accumulate receipt deltas
|
|
104
|
-
// quickly and efficiently (we expect a lot of them).
|
|
105
|
-
//
|
|
106
|
-
// moment (m.read) and nested JSON objects are slower and more
|
|
107
|
-
// of a hassle to work with. We'll inflate this back out when
|
|
108
|
-
// getJSON() is called.
|
|
111
|
+
// quickly and efficiently (we expect a lot of them). We'll inflate
|
|
112
|
+
// this back out when getJSON() is called.
|
|
109
113
|
Object.keys(e.content).forEach((eventId) => {
|
|
110
114
|
Object.entries<ReceiptContent>(e.content[eventId]).forEach(([key, value]) => {
|
|
111
115
|
if (!isSupportedReceiptType(key)) return;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2026 The Matrix.org Foundation C.I.C.
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The room summary endpoint, which describes a room the user may not be in.
|
|
19
|
+
* https://spec.matrix.org/latest/client-server-api/#room-summaries
|
|
20
|
+
*
|
|
21
|
+
* Not to be confused with {@link RoomSummary} in `./models/room-summary.ts`, which holds the
|
|
22
|
+
* heroes and member counts that `/sync` sends for rooms we are in.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { type IPublicRoomsChunkRoom, type IServerVersions } from "./client.ts";
|
|
26
|
+
import { ClientPrefix, type IHttpOpts, type MatrixHttpApi, Method } from "./http-api/index.ts";
|
|
27
|
+
import { type JoinRule } from "./@types/partials.ts";
|
|
28
|
+
import { type Membership } from "./@types/membership.ts";
|
|
29
|
+
import * as utils from "./utils.ts";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The summary of a room, as returned by the room summary endpoint.
|
|
33
|
+
* https://spec.matrix.org/latest/client-server-api/#get_matrixclientv1room_summaryroomidoralias
|
|
34
|
+
*
|
|
35
|
+
* Also covers the initial version of MSC3266 implemented in older versions of Synapse, which
|
|
36
|
+
* prefixed some of the fields. See {@link MatrixClient.getRoomSummary}.
|
|
37
|
+
*/
|
|
38
|
+
export interface RoomSummary extends Omit<IPublicRoomsChunkRoom, "canonical_alias" | "aliases" | "join_rule"> {
|
|
39
|
+
/**
|
|
40
|
+
* The current membership of this user in the room.
|
|
41
|
+
*/
|
|
42
|
+
"membership"?: Membership;
|
|
43
|
+
/**
|
|
44
|
+
* The join rule of the room. Unlike the join rule of a public rooms chunk, this may be any
|
|
45
|
+
* join rule, since the room does not have to be published to the room directory.
|
|
46
|
+
*/
|
|
47
|
+
"join_rule"?: JoinRule;
|
|
48
|
+
/**
|
|
49
|
+
* The canonical alias of the room, if any.
|
|
50
|
+
*/
|
|
51
|
+
"canonical_alias"?: string;
|
|
52
|
+
/**
|
|
53
|
+
* Version of the room.
|
|
54
|
+
*/
|
|
55
|
+
"room_version"?: string;
|
|
56
|
+
/**
|
|
57
|
+
* The encryption algorithm used for this room, if the room is encrypted.
|
|
58
|
+
*/
|
|
59
|
+
"encryption"?: string;
|
|
60
|
+
/**
|
|
61
|
+
* For restricted rooms, the room IDs which are specified by the join rule.
|
|
62
|
+
*/
|
|
63
|
+
"allowed_room_ids"?: string[];
|
|
64
|
+
/**
|
|
65
|
+
* Version of the room.
|
|
66
|
+
* @deprecated Only returned by the unstable endpoint; use {@link RoomSummary.room_version}.
|
|
67
|
+
*/
|
|
68
|
+
"im.nheko.summary.room_version"?: string;
|
|
69
|
+
/**
|
|
70
|
+
* The encryption algorithm used for this room, if the room is encrypted.
|
|
71
|
+
* @deprecated Only returned by the unstable endpoint; use {@link RoomSummary.encryption}.
|
|
72
|
+
*/
|
|
73
|
+
"im.nheko.summary.encryption"?: string;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** The endpoint stabilised in Matrix 1.15. */
|
|
77
|
+
const STABLE_ENDPOINT = { path: "/room_summary/$roomid", prefix: ClientPrefix.V1 };
|
|
78
|
+
|
|
79
|
+
/** The endpoint used by the initial version of MSC3266, as implemented in older versions of Synapse. */
|
|
80
|
+
const UNSTABLE_ENDPOINT = { path: "/summary/$roomid", prefix: "/_matrix/client/unstable/im.nheko.summary" };
|
|
81
|
+
|
|
82
|
+
/** The spec version which stabilised MSC3266 as `/_matrix/client/v1/room_summary/{roomIdOrAlias}`. */
|
|
83
|
+
const STABLE_VERSION = "v1.15";
|
|
84
|
+
|
|
85
|
+
/** The unstable feature flag advertised for MSC3266. */
|
|
86
|
+
const UNSTABLE_FEATURE = "org.matrix.msc3266";
|
|
87
|
+
|
|
88
|
+
/** Whether the server advertises the stable room summary endpoint in its `/versions` response. */
|
|
89
|
+
function supportsStableEndpoint(versions: IServerVersions): boolean {
|
|
90
|
+
return versions.versions?.includes(STABLE_VERSION) || !!versions.unstable_features?.[UNSTABLE_FEATURE];
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Fetch the summary of a room.
|
|
95
|
+
*
|
|
96
|
+
* Uses the path from the initial version of MSC3266, as implemented in older versions of Synapse,
|
|
97
|
+
* unless the server advertises support for the spec version which stabilised it.
|
|
98
|
+
*
|
|
99
|
+
* @param http - The HTTP API to make the request with.
|
|
100
|
+
* @param versions - The server's `/versions` response, used to pick the endpoint.
|
|
101
|
+
* @param roomIdOrAlias - The ID or alias of the room to get the summary of.
|
|
102
|
+
* @param via - The servers to attempt to request the summary from, when the local server cannot
|
|
103
|
+
* generate it (for instance, because it has no local user in the room).
|
|
104
|
+
*/
|
|
105
|
+
export function fetchRoomSummary(
|
|
106
|
+
http: MatrixHttpApi<IHttpOpts & { onlyData: true }>,
|
|
107
|
+
versions: IServerVersions,
|
|
108
|
+
roomIdOrAlias: string,
|
|
109
|
+
via?: string[],
|
|
110
|
+
): Promise<RoomSummary> {
|
|
111
|
+
const endpoint = supportsStableEndpoint(versions) ? STABLE_ENDPOINT : UNSTABLE_ENDPOINT;
|
|
112
|
+
const path = utils.encodeUri(endpoint.path, { $roomid: roomIdOrAlias });
|
|
113
|
+
return http.authedRequest<RoomSummary>(Method.Get, path, { via }, undefined, { prefix: endpoint.prefix });
|
|
114
|
+
}
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
import { type Logger } from "../logger.ts";
|
|
32
32
|
import { ClientPrefix, type IHttpOpts, MatrixError, type MatrixHttpApi, Method } from "../http-api/index.ts";
|
|
33
33
|
import { TypedEventEmitter } from "../models/typed-event-emitter.ts";
|
|
34
|
-
import { encodeUri
|
|
34
|
+
import { encodeUri } from "../utils.ts";
|
|
35
35
|
import { type OutgoingRequestProcessor } from "./OutgoingRequestProcessor.ts";
|
|
36
36
|
import { sleep } from "../utils.ts";
|
|
37
37
|
import { type BackupDecryptor } from "../common-crypto/CryptoBackend.ts";
|
|
@@ -61,6 +61,14 @@ interface KeyBackupCreationInfo {
|
|
|
61
61
|
* @internal
|
|
62
62
|
*/
|
|
63
63
|
export class RustBackupManager extends TypedEventEmitter<RustBackupCryptoEvents, RustBackupCryptoEventMap> {
|
|
64
|
+
/**
|
|
65
|
+
* When the backup upload loop starts, we delay the first request by a random amount to avoid backup
|
|
66
|
+
* requests from different clients hitting the server all at the same time when a new key is sent.
|
|
67
|
+
*
|
|
68
|
+
* This defines the maximum delay. It can be reduced in tests to make the test run faster.
|
|
69
|
+
*/
|
|
70
|
+
public static readonly maxBackupLoopStartDelayMillis: number = 10000;
|
|
71
|
+
|
|
64
72
|
/** Have we checked if there is a backup on the server which we can use */
|
|
65
73
|
private checkedForBackup = false;
|
|
66
74
|
|
|
@@ -418,19 +426,21 @@ export class RustBackupManager extends TypedEventEmitter<RustBackupCryptoEvents,
|
|
|
418
426
|
this.emit(CryptoEvent.KeyBackupStatus, false);
|
|
419
427
|
}
|
|
420
428
|
|
|
421
|
-
private async backupKeysLoop(
|
|
429
|
+
private async backupKeysLoop(): Promise<void> {
|
|
430
|
+
const logger = this.logger.getChild("[backupKeysLoop]");
|
|
431
|
+
|
|
422
432
|
if (this.backupKeysLoopRunning) {
|
|
423
|
-
|
|
433
|
+
logger.debug(`Backup loop already running`);
|
|
424
434
|
return;
|
|
425
435
|
}
|
|
426
436
|
this.backupKeysLoopRunning = true;
|
|
427
437
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
438
|
+
// Wait between 0 and `maxBackupLoopStartDelayMillis` milliseconds, to avoid backup requests from different
|
|
439
|
+
// clients hitting the server all at the same time when a new key is sent.
|
|
440
|
+
const delay = Math.random() * RustBackupManager.maxBackupLoopStartDelayMillis;
|
|
441
|
+
logger.debug(
|
|
442
|
+
`Starting keys upload loop for backup version ${this.activeBackupVersion}, but delaying startup by ${delay}ms`,
|
|
443
|
+
);
|
|
434
444
|
await sleep(delay);
|
|
435
445
|
|
|
436
446
|
try {
|
|
@@ -444,25 +454,28 @@ export class RustBackupManager extends TypedEventEmitter<RustBackupCryptoEvents,
|
|
|
444
454
|
|
|
445
455
|
while (!this.stopped) {
|
|
446
456
|
// Get a batch of room keys to upload
|
|
447
|
-
let request
|
|
457
|
+
let request;
|
|
448
458
|
try {
|
|
449
|
-
request = await
|
|
450
|
-
|
|
451
|
-
"
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
459
|
+
request = await this.olmMachine.backupRoomKeys();
|
|
460
|
+
if (request) {
|
|
461
|
+
logger.debug("Got keys to back up from crypto-sdk");
|
|
462
|
+
} else {
|
|
463
|
+
logger.debug(`No more keys to back up: ending loop for version ${this.activeBackupVersion}.`);
|
|
464
|
+
this.emit(CryptoEvent.KeyBackupSessionsRemaining, 0);
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
456
467
|
} catch (err) {
|
|
457
|
-
|
|
468
|
+
logger.error("Failed to get keys to backup from rust crypto-sdk: ending backup loop", err);
|
|
469
|
+
return;
|
|
458
470
|
}
|
|
459
471
|
|
|
460
|
-
if (
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
472
|
+
if (this.stopped) {
|
|
473
|
+
logger.debug(`Client stopping: ending loop for version ${this.activeBackupVersion}.`);
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (!this.activeBackupVersion) {
|
|
478
|
+
logger.debug(`Backup no longer active: ending loop.`);
|
|
466
479
|
return;
|
|
467
480
|
}
|
|
468
481
|
|
|
@@ -485,7 +498,7 @@ export class RustBackupManager extends TypedEventEmitter<RustBackupCryptoEvents,
|
|
|
485
498
|
const keyCount = await this.olmMachine.roomKeyCounts();
|
|
486
499
|
remainingToUploadCount = keyCount.total - keyCount.backedUp;
|
|
487
500
|
} catch (err) {
|
|
488
|
-
|
|
501
|
+
logger.error("Failed to get key counts from rust crypto-sdk", err);
|
|
489
502
|
}
|
|
490
503
|
}
|
|
491
504
|
|
|
@@ -501,15 +514,15 @@ export class RustBackupManager extends TypedEventEmitter<RustBackupCryptoEvents,
|
|
|
501
514
|
}
|
|
502
515
|
} catch (err) {
|
|
503
516
|
numFailures++;
|
|
504
|
-
|
|
517
|
+
logger.error("Error processing backup request for rust crypto-sdk", err);
|
|
505
518
|
if (err instanceof MatrixError) {
|
|
506
519
|
const errCode = err.data.errcode;
|
|
507
520
|
if (errCode == "M_NOT_FOUND" || errCode == "M_WRONG_ROOM_KEYS_VERSION") {
|
|
508
|
-
|
|
521
|
+
logger.debug(`Failed to upload keys to current version: ${errCode}.`);
|
|
509
522
|
try {
|
|
510
523
|
await this.disableKeyBackup();
|
|
511
524
|
} catch (error) {
|
|
512
|
-
|
|
525
|
+
logger.error("An error occurred while disabling key backup:", error);
|
|
513
526
|
}
|
|
514
527
|
this.emit(CryptoEvent.KeyBackupFailed, err.data.errcode!);
|
|
515
528
|
// There was an active backup and we are out of sync with the server
|
|
@@ -522,21 +535,21 @@ export class RustBackupManager extends TypedEventEmitter<RustBackupCryptoEvents,
|
|
|
522
535
|
try {
|
|
523
536
|
const waitTime = err.getRetryAfterMs();
|
|
524
537
|
if (waitTime && waitTime > 0) {
|
|
538
|
+
logger.debug(`Sleeping ${waitTime}ms after ratelimit`);
|
|
525
539
|
await sleep(waitTime);
|
|
526
540
|
continue;
|
|
527
541
|
}
|
|
528
542
|
} catch (error) {
|
|
529
|
-
|
|
530
|
-
"Backup: An error occurred while retrieving a rate-limit retry delay",
|
|
531
|
-
error,
|
|
532
|
-
);
|
|
543
|
+
logger.warn("An error occurred while retrieving a rate-limit retry delay", error);
|
|
533
544
|
} // else go to the normal backoff
|
|
534
545
|
}
|
|
535
546
|
}
|
|
536
547
|
|
|
537
548
|
// Some other errors (mx, network, or CORS or invalid urls?) anyhow backoff
|
|
538
549
|
// exponential backoff if we have failures
|
|
539
|
-
|
|
550
|
+
const waitTime = 1000 * Math.pow(2, Math.min(numFailures - 1, 4));
|
|
551
|
+
logger.debug(`Sleeping ${waitTime}ms after failure #${numFailures}`);
|
|
552
|
+
await sleep(waitTime);
|
|
540
553
|
}
|
|
541
554
|
isFirstIteration = false;
|
|
542
555
|
}
|
package/src/sync.ts
CHANGED
|
@@ -1486,7 +1486,15 @@ export class SyncApi {
|
|
|
1486
1486
|
// this fires a couple of times for some events. (eg state events are in the timeline and the state)
|
|
1487
1487
|
// should this get a sync section as an additional event emission param (e, syncSection))?
|
|
1488
1488
|
stateEvents.forEach(emitEvent);
|
|
1489
|
-
|
|
1489
|
+
stateAfterEvents.forEach(emitEvent);
|
|
1490
|
+
// have to filter out all state events from the timeline when MSC4222 is enabled, in this case:
|
|
1491
|
+
// all state events are emitted by "state_after"
|
|
1492
|
+
// all state events in the timeline are either duplicates or are outdated and should be ignored
|
|
1493
|
+
const filteredTimelineEvents =
|
|
1494
|
+
"org.matrix.msc4222.state_after" in joinObj
|
|
1495
|
+
? timelineEvents.filter((timelineEvent) => !timelineEvent.isState())
|
|
1496
|
+
: timelineEvents;
|
|
1497
|
+
filteredTimelineEvents.forEach(emitEvent);
|
|
1490
1498
|
ephemeralEvents.forEach(emitEvent);
|
|
1491
1499
|
accountDataEvents.forEach(emitEvent);
|
|
1492
1500
|
stickyEvents
|