@openclaw/matrix 2026.3.13 → 2026.5.10-beta.1

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.
Files changed (206) hide show
  1. package/dist/account-config-D2W-V1eQ.js +96 -0
  2. package/dist/account-selection-BWwIruri.js +158 -0
  3. package/dist/accounts-Bm90Rzvp.js +130 -0
  4. package/dist/active-client-uhlxdhEy.js +20 -0
  5. package/dist/allowlist-sTzpCn5d.js +68 -0
  6. package/dist/api.js +12 -0
  7. package/dist/approval-handler.runtime-DWTQfd4m.js +370 -0
  8. package/dist/approval-ids-DoC2z7tR.js +7 -0
  9. package/dist/approval-reaction-auth-DbcA1gGd.js +27 -0
  10. package/dist/approval-reactions-o2_tuH8D.js +162 -0
  11. package/dist/async-lock-uQfhfQIY.js +19 -0
  12. package/dist/auth-presence.js +26 -0
  13. package/dist/backup-health-Cabu_WQC.js +60 -0
  14. package/dist/channel-H_6lMgwf.js +1116 -0
  15. package/dist/channel-plugin-api.js +2 -0
  16. package/dist/channel.runtime-BnO9f0pR.js +246 -0
  17. package/dist/cli-CYZ9yVcB.js +1340 -0
  18. package/dist/cli-metadata-DPIHnoa6.js +22 -0
  19. package/dist/cli-metadata.js +2 -0
  20. package/dist/client-DkcXnm0X.js +25 -0
  21. package/dist/client-_hckQNGW.js +31 -0
  22. package/dist/client-bootstrap-Rb8oHvhH.js +114 -0
  23. package/dist/config--5-S2Akv.js +452 -0
  24. package/dist/config-paths-nsVaysCu.js +19 -0
  25. package/dist/config-schema-nPLpEgHl.js +200 -0
  26. package/dist/config-secret-input.runtime-DiKFehsE.js +2 -0
  27. package/dist/config-update-wZX-HLMn.js +143 -0
  28. package/dist/contract-api.js +9 -0
  29. package/dist/create-client-DCnqDaqd.js +64 -0
  30. package/dist/credentials-DV6fWXhC.js +56 -0
  31. package/dist/credentials-read-cmHgousK.js +112 -0
  32. package/dist/credentials-write.runtime-zniTq-Gr.js +17 -0
  33. package/dist/crypto-node.runtime-pihzdpY7.js +12 -0
  34. package/dist/crypto-runtime-ZI0zAtn3.js +1214 -0
  35. package/dist/deps-C6WqKY7m.js +235 -0
  36. package/dist/device-health-UVYpbA_W.js +16 -0
  37. package/dist/direct-management-DMMMgtTB.js +249 -0
  38. package/dist/direct-room-XkutHjES.js +76 -0
  39. package/dist/directory-live-DmOtMhyr.js +150 -0
  40. package/dist/doctor-C4__7c-U.js +153 -0
  41. package/dist/doctor-contract-D4-64QuJ.js +246 -0
  42. package/dist/doctor-contract-api.js +2 -0
  43. package/dist/draft-stream-BE2QevQQ.js +144 -0
  44. package/dist/encryption-guidance-BPi3A_m3.js +15 -0
  45. package/dist/env-auth-BJqGI8M6.js +63 -0
  46. package/dist/env-vars-C7uQCTKn.js +63 -0
  47. package/dist/errors-CTcpEDq-.js +17 -0
  48. package/dist/exec-approval-resolver-Bza9Dhlm.js +15 -0
  49. package/dist/exec-approvals-Crnh543m.js +196 -0
  50. package/dist/helper-api.js +4 -0
  51. package/dist/http-client-C7AeVJay.js +319 -0
  52. package/dist/index.js +46 -0
  53. package/dist/legacy-crypto-inspector-poDWldgy.js +41 -0
  54. package/dist/legacy-crypto-restore-Biw-w2ng.js +85 -0
  55. package/dist/logger-CnZRVrux.js +78 -0
  56. package/dist/logging-DZHSPP5N.js +99 -0
  57. package/dist/matrix-migration.runtime-WY6ffcrf.js +525 -0
  58. package/dist/media-text-DU6nWZuj.js +146 -0
  59. package/dist/messages-BpihMh82.js +140 -0
  60. package/dist/migration-snapshot-backup-DaCHTp8C.js +69 -0
  61. package/dist/migration-snapshot.runtime-CKHE3xF9.js +2 -0
  62. package/dist/monitor-BaRCKyLd.js +4175 -0
  63. package/dist/plugin-entry.handlers.runtime.js +51 -0
  64. package/dist/probe.runtime-BvAzYAIe.js +3 -0
  65. package/dist/profile-BlHu0wDX.js +111 -0
  66. package/dist/profile-update-DjeBNgIV.js +69 -0
  67. package/dist/reaction-common-ejrL19w-.js +71 -0
  68. package/dist/reaction-events-CiARZfjk.js +121 -0
  69. package/dist/record-shared-CHWJCTWf.js +2 -0
  70. package/dist/recovery-key-store-BTJ6jz5v.js +294 -0
  71. package/dist/resolve-targets-YtJnw1Tb.js +140 -0
  72. package/dist/resolver.runtime-D9piiGEl.js +5 -0
  73. package/dist/rolldown-runtime-DUslC3ob.js +14 -0
  74. package/dist/route-D6rg-iXN.js +161 -0
  75. package/dist/runtime-C6X4h_SJ.js +6 -0
  76. package/dist/runtime-Dog86njy.js +8 -0
  77. package/dist/runtime-api-DTKcXOhp.js +24 -0
  78. package/dist/runtime-api.js +25 -0
  79. package/dist/runtime-heavy-api.js +3 -0
  80. package/dist/runtime-setter-api.js +2 -0
  81. package/dist/sdk-B2vZA27-.js +1416 -0
  82. package/dist/secret-contract-DcrJWCQI.js +120 -0
  83. package/dist/secret-contract-api.js +2 -0
  84. package/dist/send-Bo0DU1ca.js +1200 -0
  85. package/dist/session-store-metadata-DQXjgNLt.js +77 -0
  86. package/dist/setup-bootstrap-ImenBsMt.js +62 -0
  87. package/dist/setup-core-CfZy05oW.js +116 -0
  88. package/dist/setup-dm-policy-2-r1FrQh.js +194 -0
  89. package/dist/setup-entry.js +19 -0
  90. package/dist/setup-plugin-api.js +44 -0
  91. package/dist/setup-surface-CqT_o61M.js +540 -0
  92. package/dist/shared-CpMoYKm1.js +195 -0
  93. package/dist/startup-abort-56edvmbM.js +32 -0
  94. package/dist/startup-verification-Demyp0bP.js +132 -0
  95. package/dist/storage-paths-BJLdnCjV.js +52 -0
  96. package/dist/storage-tC3ujLiW.js +281 -0
  97. package/dist/subagent-hooks-DQbyqq9V.js +149 -0
  98. package/dist/subagent-hooks-api.js +23 -0
  99. package/dist/sync-state-C_beeevA.js +12 -0
  100. package/dist/target-ids-80nQ2gql.js +77 -0
  101. package/dist/test-api.js +4 -0
  102. package/dist/thread-binding-api-Cq_E-E1K.js +17 -0
  103. package/dist/thread-binding-api.js +2 -0
  104. package/dist/thread-bindings-B9mesxXk.js +352 -0
  105. package/dist/thread-bindings-runtime.js +2 -0
  106. package/dist/thread-bindings-shared-DK-d-oYX.js +97 -0
  107. package/dist/timeout-abort-signal-CtaIaP1v.js +2 -0
  108. package/dist/tool-actions.runtime-ThYhfHtZ.js +532 -0
  109. package/dist/url-validation-DiK9j7jz.js +36 -0
  110. package/dist/verification-CZ2rDeHL.js +345 -0
  111. package/openclaw.plugin.json +796 -1
  112. package/package.json +82 -16
  113. package/CHANGELOG.md +0 -104
  114. package/index.ts +0 -22
  115. package/src/actions.ts +0 -195
  116. package/src/channel.directory.test.ts +0 -135
  117. package/src/channel.ts +0 -461
  118. package/src/config-schema.test.ts +0 -26
  119. package/src/config-schema.ts +0 -62
  120. package/src/directory-live.test.ts +0 -85
  121. package/src/directory-live.ts +0 -209
  122. package/src/group-mentions.ts +0 -52
  123. package/src/matrix/accounts.test.ts +0 -131
  124. package/src/matrix/accounts.ts +0 -114
  125. package/src/matrix/actions/client.ts +0 -47
  126. package/src/matrix/actions/limits.test.ts +0 -15
  127. package/src/matrix/actions/limits.ts +0 -6
  128. package/src/matrix/actions/messages.ts +0 -126
  129. package/src/matrix/actions/pins.test.ts +0 -74
  130. package/src/matrix/actions/pins.ts +0 -84
  131. package/src/matrix/actions/reactions.test.ts +0 -109
  132. package/src/matrix/actions/reactions.ts +0 -102
  133. package/src/matrix/actions/room.ts +0 -85
  134. package/src/matrix/actions/summary.ts +0 -75
  135. package/src/matrix/actions/types.ts +0 -85
  136. package/src/matrix/actions.ts +0 -15
  137. package/src/matrix/active-client.ts +0 -32
  138. package/src/matrix/client/config.ts +0 -245
  139. package/src/matrix/client/create-client.ts +0 -125
  140. package/src/matrix/client/logging.ts +0 -46
  141. package/src/matrix/client/runtime.ts +0 -4
  142. package/src/matrix/client/shared.test.ts +0 -85
  143. package/src/matrix/client/shared.ts +0 -210
  144. package/src/matrix/client/startup.test.ts +0 -49
  145. package/src/matrix/client/startup.ts +0 -29
  146. package/src/matrix/client/storage.ts +0 -131
  147. package/src/matrix/client/types.ts +0 -34
  148. package/src/matrix/client-bootstrap.ts +0 -47
  149. package/src/matrix/client.test.ts +0 -56
  150. package/src/matrix/client.ts +0 -14
  151. package/src/matrix/credentials.ts +0 -125
  152. package/src/matrix/deps.test.ts +0 -74
  153. package/src/matrix/deps.ts +0 -126
  154. package/src/matrix/format.test.ts +0 -33
  155. package/src/matrix/format.ts +0 -22
  156. package/src/matrix/index.ts +0 -11
  157. package/src/matrix/monitor/access-policy.ts +0 -126
  158. package/src/matrix/monitor/allowlist.test.ts +0 -45
  159. package/src/matrix/monitor/allowlist.ts +0 -94
  160. package/src/matrix/monitor/auto-join.ts +0 -72
  161. package/src/matrix/monitor/direct.test.ts +0 -396
  162. package/src/matrix/monitor/direct.ts +0 -152
  163. package/src/matrix/monitor/events.test.ts +0 -186
  164. package/src/matrix/monitor/events.ts +0 -168
  165. package/src/matrix/monitor/handler.body-for-agent.test.ts +0 -196
  166. package/src/matrix/monitor/handler.ts +0 -768
  167. package/src/matrix/monitor/inbound-body.test.ts +0 -73
  168. package/src/matrix/monitor/inbound-body.ts +0 -28
  169. package/src/matrix/monitor/index.test.ts +0 -18
  170. package/src/matrix/monitor/index.ts +0 -414
  171. package/src/matrix/monitor/location.ts +0 -100
  172. package/src/matrix/monitor/media.test.ts +0 -86
  173. package/src/matrix/monitor/media.ts +0 -118
  174. package/src/matrix/monitor/mentions.test.ts +0 -154
  175. package/src/matrix/monitor/mentions.ts +0 -62
  176. package/src/matrix/monitor/replies.test.ts +0 -184
  177. package/src/matrix/monitor/replies.ts +0 -124
  178. package/src/matrix/monitor/room-info.ts +0 -55
  179. package/src/matrix/monitor/rooms.test.ts +0 -124
  180. package/src/matrix/monitor/rooms.ts +0 -47
  181. package/src/matrix/monitor/threads.ts +0 -68
  182. package/src/matrix/monitor/types.ts +0 -39
  183. package/src/matrix/poll-types.test.ts +0 -21
  184. package/src/matrix/poll-types.ts +0 -167
  185. package/src/matrix/probe.ts +0 -69
  186. package/src/matrix/sdk-runtime.ts +0 -18
  187. package/src/matrix/send/client.ts +0 -99
  188. package/src/matrix/send/formatting.ts +0 -93
  189. package/src/matrix/send/media.ts +0 -230
  190. package/src/matrix/send/targets.test.ts +0 -98
  191. package/src/matrix/send/targets.ts +0 -150
  192. package/src/matrix/send/types.ts +0 -110
  193. package/src/matrix/send-queue.test.ts +0 -145
  194. package/src/matrix/send-queue.ts +0 -28
  195. package/src/matrix/send.test.ts +0 -319
  196. package/src/matrix/send.ts +0 -267
  197. package/src/onboarding.ts +0 -462
  198. package/src/outbound.test.ts +0 -159
  199. package/src/outbound.ts +0 -58
  200. package/src/resolve-targets.test.ts +0 -68
  201. package/src/resolve-targets.ts +0 -125
  202. package/src/runtime.ts +0 -6
  203. package/src/secret-input.ts +0 -13
  204. package/src/test-mocks.ts +0 -53
  205. package/src/tool-actions.ts +0 -164
  206. package/src/types.ts +0 -118
@@ -0,0 +1,1416 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-DUslC3ob.js";
2
+ import { t as isRecord } from "./record-shared-CHWJCTWf.js";
3
+ import { n as formatMatrixErrorReason, r as isMatrixNotFoundError, t as formatMatrixErrorMessage } from "./errors-CTcpEDq-.js";
4
+ import { t as claimCurrentTokenStorageState } from "./storage-tC3ujLiW.js";
5
+ import { n as resolveMatrixRoomKeyBackupReadinessError } from "./backup-health-Cabu_WQC.js";
6
+ import { t as createAsyncLock } from "./async-lock-uQfhfQIY.js";
7
+ import { n as LogService, r as noop, t as ConsoleLogger } from "./logger-CnZRVrux.js";
8
+ import { t as createMatrixJsSdkClientLogger } from "./logging-DZHSPP5N.js";
9
+ import { a as matrixEventToRaw, n as createMatrixGuardedFetch, o as parseMxc, t as MatrixAuthedHttpClient } from "./http-client-C7AeVJay.js";
10
+ import { n as isRepairableSecretStorageAccessError, r as MATRIX_IDB_PERSIST_INTERVAL_MS, t as MatrixRecoveryKeyStore } from "./recovery-key-store-BTJ6jz5v.js";
11
+ import { i as throwIfMatrixStartupAborted, n as createMatrixStartupAbortError } from "./startup-abort-56edvmbM.js";
12
+ import { n as isMatrixReadySyncState, r as isMatrixTerminalSyncState } from "./sync-state-C_beeevA.js";
13
+ import { normalizeNullableString } from "openclaw/plugin-sdk/string-coerce-runtime";
14
+ import { readFileSync } from "node:fs";
15
+ import path from "node:path";
16
+ import { writeJsonFileAtomically } from "openclaw/plugin-sdk/json-store";
17
+ import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
18
+ import { EventEmitter } from "node:events";
19
+ import { Category, ClientEvent, Filter, MatrixEventEvent, MemoryStore, Preset, SyncAccumulator, createClient } from "matrix-js-sdk/lib/matrix.js";
20
+ import { VerificationMethod } from "matrix-js-sdk/lib/types.js";
21
+ import fs$1 from "node:fs/promises";
22
+ //#region extensions/matrix/src/matrix/client/file-sync-store.ts
23
+ const STORE_VERSION = 1;
24
+ const PERSIST_DEBOUNCE_MS = 250;
25
+ function normalizeRoomsData(value) {
26
+ if (!isRecord(value)) return null;
27
+ return {
28
+ [Category.Join]: isRecord(value[Category.Join]) ? value[Category.Join] : {},
29
+ [Category.Invite]: isRecord(value[Category.Invite]) ? value[Category.Invite] : {},
30
+ [Category.Leave]: isRecord(value[Category.Leave]) ? value[Category.Leave] : {},
31
+ [Category.Knock]: isRecord(value[Category.Knock]) ? value[Category.Knock] : {}
32
+ };
33
+ }
34
+ function toPersistedSyncData(value) {
35
+ if (!isRecord(value)) return null;
36
+ if (typeof value.nextBatch === "string" && value.nextBatch.trim()) {
37
+ const roomsData = normalizeRoomsData(value.roomsData);
38
+ if (!Array.isArray(value.accountData) || !roomsData) return null;
39
+ return {
40
+ nextBatch: value.nextBatch,
41
+ accountData: value.accountData,
42
+ roomsData
43
+ };
44
+ }
45
+ if (typeof value.next_batch === "string" && value.next_batch.trim()) {
46
+ const roomsData = normalizeRoomsData(value.rooms);
47
+ if (!roomsData) return null;
48
+ return {
49
+ nextBatch: value.next_batch,
50
+ accountData: isRecord(value.account_data) && Array.isArray(value.account_data.events) ? value.account_data.events : [],
51
+ roomsData
52
+ };
53
+ }
54
+ return null;
55
+ }
56
+ function readPersistedStore(raw) {
57
+ try {
58
+ const parsed = JSON.parse(raw);
59
+ const savedSync = toPersistedSyncData(parsed.savedSync);
60
+ if (parsed.version === STORE_VERSION) return {
61
+ version: STORE_VERSION,
62
+ savedSync,
63
+ clientOptions: isRecord(parsed.clientOptions) ? parsed.clientOptions : void 0,
64
+ cleanShutdown: parsed.cleanShutdown === true
65
+ };
66
+ return {
67
+ version: STORE_VERSION,
68
+ savedSync: toPersistedSyncData(parsed),
69
+ cleanShutdown: false
70
+ };
71
+ } catch {
72
+ return null;
73
+ }
74
+ }
75
+ function cloneJson(value) {
76
+ return structuredClone(value);
77
+ }
78
+ function syncDataToSyncResponse(syncData) {
79
+ return {
80
+ next_batch: syncData.nextBatch,
81
+ rooms: syncData.roomsData,
82
+ account_data: { events: syncData.accountData }
83
+ };
84
+ }
85
+ var FileBackedMatrixSyncStore = class extends MemoryStore {
86
+ constructor(storagePath) {
87
+ super();
88
+ this.storagePath = storagePath;
89
+ this.persistLock = createAsyncLock();
90
+ this.accumulator = new SyncAccumulator();
91
+ this.savedSync = null;
92
+ this.cleanShutdown = false;
93
+ this.dirty = false;
94
+ this.persistTimer = null;
95
+ this.persistPromise = null;
96
+ let restoredSavedSync = null;
97
+ let restoredClientOptions;
98
+ let restoredCleanShutdown = false;
99
+ try {
100
+ const persisted = readPersistedStore(readFileSync(this.storagePath, "utf8"));
101
+ restoredSavedSync = persisted?.savedSync ?? null;
102
+ restoredClientOptions = persisted?.clientOptions;
103
+ restoredCleanShutdown = persisted?.cleanShutdown === true;
104
+ } catch {}
105
+ this.savedSync = restoredSavedSync;
106
+ this.savedClientOptions = restoredClientOptions;
107
+ this.hadSavedSyncOnLoad = restoredSavedSync !== null;
108
+ this.hadCleanShutdownOnLoad = this.hadSavedSyncOnLoad && restoredCleanShutdown;
109
+ this.cleanShutdown = this.hadCleanShutdownOnLoad;
110
+ if (this.savedSync) {
111
+ this.accumulator.accumulate(syncDataToSyncResponse(this.savedSync), true);
112
+ super.setSyncToken(this.savedSync.nextBatch);
113
+ }
114
+ if (this.savedClientOptions) super.storeClientOptions(this.savedClientOptions);
115
+ }
116
+ hasSavedSync() {
117
+ return this.hadSavedSyncOnLoad;
118
+ }
119
+ hasSavedSyncFromCleanShutdown() {
120
+ return this.hadCleanShutdownOnLoad;
121
+ }
122
+ getSavedSync() {
123
+ return Promise.resolve(this.savedSync ? cloneJson(this.savedSync) : null);
124
+ }
125
+ getSavedSyncToken() {
126
+ return Promise.resolve(this.savedSync?.nextBatch ?? null);
127
+ }
128
+ setSyncData(syncData) {
129
+ this.accumulator.accumulate(syncData);
130
+ this.savedSync = this.accumulator.getJSON();
131
+ this.markDirtyAndSchedulePersist();
132
+ return Promise.resolve();
133
+ }
134
+ getClientOptions() {
135
+ return Promise.resolve(this.savedClientOptions ? cloneJson(this.savedClientOptions) : void 0);
136
+ }
137
+ storeClientOptions(options) {
138
+ this.savedClientOptions = cloneJson(options);
139
+ super.storeClientOptions(options);
140
+ this.markDirtyAndSchedulePersist();
141
+ return Promise.resolve();
142
+ }
143
+ save(force = false) {
144
+ if (force) return this.flush();
145
+ return Promise.resolve();
146
+ }
147
+ wantsSave() {
148
+ return false;
149
+ }
150
+ async deleteAllData() {
151
+ if (this.persistTimer) {
152
+ clearTimeout(this.persistTimer);
153
+ this.persistTimer = null;
154
+ }
155
+ this.dirty = false;
156
+ await this.persistPromise?.catch(() => void 0);
157
+ await super.deleteAllData();
158
+ this.savedSync = null;
159
+ this.savedClientOptions = void 0;
160
+ this.cleanShutdown = false;
161
+ await fs$1.rm(this.storagePath, { force: true }).catch(() => void 0);
162
+ }
163
+ markCleanShutdown() {
164
+ this.cleanShutdown = true;
165
+ this.dirty = true;
166
+ }
167
+ async flush() {
168
+ if (this.persistTimer) {
169
+ clearTimeout(this.persistTimer);
170
+ this.persistTimer = null;
171
+ }
172
+ while (this.dirty || this.persistPromise) {
173
+ if (this.dirty && !this.persistPromise) this.persistPromise = this.persist().finally(() => {
174
+ this.persistPromise = null;
175
+ });
176
+ await this.persistPromise;
177
+ }
178
+ }
179
+ markDirtyAndSchedulePersist() {
180
+ this.cleanShutdown = false;
181
+ this.dirty = true;
182
+ if (this.persistTimer) return;
183
+ this.persistTimer = setTimeout(() => {
184
+ this.persistTimer = null;
185
+ this.flush().catch((err) => {
186
+ LogService.warn("MatrixFileSyncStore", "Failed to persist Matrix sync store:", err);
187
+ });
188
+ }, PERSIST_DEBOUNCE_MS);
189
+ this.persistTimer.unref?.();
190
+ }
191
+ async persist() {
192
+ this.dirty = false;
193
+ const payload = {
194
+ version: STORE_VERSION,
195
+ savedSync: this.savedSync ? cloneJson(this.savedSync) : null,
196
+ cleanShutdown: this.cleanShutdown,
197
+ ...this.savedClientOptions ? { clientOptions: cloneJson(this.savedClientOptions) } : {}
198
+ };
199
+ try {
200
+ await this.persistLock(async () => {
201
+ await writeJsonFileAtomically(this.storagePath, payload);
202
+ claimCurrentTokenStorageState({ rootDir: path.dirname(this.storagePath) });
203
+ });
204
+ } catch (err) {
205
+ this.dirty = true;
206
+ throw err;
207
+ }
208
+ }
209
+ };
210
+ //#endregion
211
+ //#region extensions/matrix/src/matrix/sdk.ts
212
+ var sdk_exports = /* @__PURE__ */ __exportAll({
213
+ ConsoleLogger: () => ConsoleLogger,
214
+ LogService: () => LogService,
215
+ MatrixClient: () => MatrixClient
216
+ });
217
+ const MATRIX_STATUS_DIAGNOSTIC_TIMEOUT_MS = 1e4;
218
+ function unresolvedMatrixRoomKeyBackupStatus() {
219
+ return {
220
+ serverVersion: null,
221
+ activeVersion: null,
222
+ trusted: null,
223
+ matchesDecryptionKey: null,
224
+ decryptionKeyCached: null,
225
+ keyLoadAttempted: false,
226
+ keyLoadError: null
227
+ };
228
+ }
229
+ function unresolvedMatrixDeviceVerificationStatus(params) {
230
+ return {
231
+ encryptionEnabled: true,
232
+ userId: params.userId,
233
+ deviceId: params.deviceId,
234
+ verified: false,
235
+ localVerified: false,
236
+ crossSigningVerified: false,
237
+ signedByOwner: false
238
+ };
239
+ }
240
+ async function resolveMatrixDiagnostic(promise, timeoutMs) {
241
+ return (await resolveMatrixDiagnosticResult(promise, timeoutMs)).value;
242
+ }
243
+ async function resolveMatrixDiagnosticResult(promise, timeoutMs) {
244
+ let timeoutId;
245
+ try {
246
+ const guarded = promise.then((value) => ({
247
+ error: null,
248
+ timedOut: false,
249
+ value
250
+ })).catch((error) => ({
251
+ error,
252
+ timedOut: false,
253
+ value: null
254
+ }));
255
+ const timeout = new Promise((resolve) => {
256
+ timeoutId = setTimeout(() => resolve({
257
+ error: null,
258
+ timedOut: true,
259
+ value: null
260
+ }), timeoutMs);
261
+ timeoutId.unref?.();
262
+ });
263
+ return await Promise.race([guarded, timeout]);
264
+ } finally {
265
+ if (timeoutId) clearTimeout(timeoutId);
266
+ }
267
+ }
268
+ function isMatrixAccessTokenInvalidatedError(error) {
269
+ if (!error || typeof error !== "object") return false;
270
+ const err = error;
271
+ const errcode = err.body?.errcode ?? err.data?.errcode;
272
+ if (err.statusCode === 401 && errcode === "M_UNKNOWN_TOKEN") return true;
273
+ const reason = formatMatrixErrorReason(error);
274
+ return reason.includes("m_unknown_token") || reason.includes("unknown token") || reason.includes("access token") && (reason.includes("invalid") || reason.includes("unrecognized") || reason.includes("unknown"));
275
+ }
276
+ const MATRIX_INITIAL_CRYPTO_BOOTSTRAP_OPTIONS = { allowAutomaticCrossSigningReset: false };
277
+ const MATRIX_AUTOMATIC_REPAIR_BOOTSTRAP_OPTIONS = {
278
+ forceResetCrossSigning: true,
279
+ allowSecretStorageRecreateWithoutRecoveryKey: true,
280
+ strict: true
281
+ };
282
+ function createMatrixExplicitBootstrapOptions(params) {
283
+ return {
284
+ forceResetCrossSigning: params?.forceResetCrossSigning === true,
285
+ allowAutomaticCrossSigningReset: params?.allowAutomaticCrossSigningReset !== false,
286
+ allowSecretStorageRecreateWithoutRecoveryKey: true,
287
+ strict: params?.strict !== false
288
+ };
289
+ }
290
+ let loadedMatrixCryptoRuntime = null;
291
+ let matrixCryptoRuntimePromise = null;
292
+ async function loadMatrixCryptoRuntime() {
293
+ matrixCryptoRuntimePromise ??= import("./crypto-runtime-ZI0zAtn3.js").then((runtime) => {
294
+ loadedMatrixCryptoRuntime = runtime;
295
+ return runtime;
296
+ });
297
+ return await matrixCryptoRuntimePromise;
298
+ }
299
+ const normalizeOptionalString$1 = normalizeNullableString;
300
+ function isUnsupportedAuthenticatedMediaEndpointError(err) {
301
+ const statusCode = err?.statusCode;
302
+ if (statusCode === 404 || statusCode === 405 || statusCode === 501) return true;
303
+ const message = formatMatrixErrorReason(err);
304
+ return message.includes("m_unrecognized") || message.includes("unrecognized request") || message.includes("method not allowed") || message.includes("not implemented");
305
+ }
306
+ var MatrixClient = class {
307
+ constructor(homeserver, accessToken, opts = {}) {
308
+ this.emitter = new EventEmitter();
309
+ this.bridgeRegistered = false;
310
+ this.started = false;
311
+ this.cryptoBootstrapped = false;
312
+ this.dmRoomIds = /* @__PURE__ */ new Set();
313
+ this.cryptoInitialized = false;
314
+ this.sendQueue = new KeyedAsyncQueue();
315
+ this.stopPersistPromise = null;
316
+ this.verificationSummaryListenerBound = false;
317
+ this.currentSyncState = null;
318
+ this.dms = {
319
+ update: async () => {
320
+ return await this.refreshDmCache();
321
+ },
322
+ isDm: (roomId) => this.dmRoomIds.has(roomId)
323
+ };
324
+ this.idbPersistTimer = null;
325
+ this.httpClient = new MatrixAuthedHttpClient({
326
+ homeserver,
327
+ accessToken,
328
+ ssrfPolicy: opts.ssrfPolicy,
329
+ dispatcherPolicy: opts.dispatcherPolicy
330
+ });
331
+ this.localTimeoutMs = Math.max(1, opts.localTimeoutMs ?? 6e4);
332
+ this.initialSyncLimit = opts.initialSyncLimit;
333
+ this.syncFilter = opts.syncFilter;
334
+ this.encryptionEnabled = opts.encryption === true;
335
+ this.password = opts.password;
336
+ this.syncStore = opts.storagePath ? new FileBackedMatrixSyncStore(opts.storagePath) : void 0;
337
+ this.idbSnapshotPath = opts.idbSnapshotPath;
338
+ this.cryptoDatabasePrefix = opts.cryptoDatabasePrefix;
339
+ this.selfUserId = opts.userId?.trim() || null;
340
+ this.autoBootstrapCrypto = opts.autoBootstrapCrypto !== false;
341
+ this.recoveryKeyStore = new MatrixRecoveryKeyStore(opts.recoveryKeyPath);
342
+ const cryptoCallbacks = this.encryptionEnabled ? this.recoveryKeyStore.buildCryptoCallbacks() : void 0;
343
+ this.client = createClient({
344
+ baseUrl: homeserver,
345
+ accessToken,
346
+ userId: opts.userId,
347
+ deviceId: opts.deviceId,
348
+ logger: createMatrixJsSdkClientLogger("MatrixClient"),
349
+ localTimeoutMs: this.localTimeoutMs,
350
+ fetchFn: createMatrixGuardedFetch({
351
+ ssrfPolicy: opts.ssrfPolicy,
352
+ dispatcherPolicy: opts.dispatcherPolicy
353
+ }),
354
+ store: this.syncStore,
355
+ cryptoCallbacks,
356
+ verificationMethods: [
357
+ VerificationMethod.Sas,
358
+ VerificationMethod.ShowQrCode,
359
+ VerificationMethod.ScanQrCode,
360
+ VerificationMethod.Reciprocate
361
+ ]
362
+ });
363
+ }
364
+ on(eventName, listener) {
365
+ this.emitter.on(eventName, listener);
366
+ return this;
367
+ }
368
+ off(eventName, listener) {
369
+ this.emitter.off(eventName, listener);
370
+ return this;
371
+ }
372
+ async ensureCryptoSupportInitialized() {
373
+ if (this.decryptBridge && (!this.encryptionEnabled || this.verificationManager && this.cryptoBootstrapper && this.crypto)) return;
374
+ const runtime = await loadMatrixCryptoRuntime();
375
+ this.decryptBridge ??= new runtime.MatrixDecryptBridge({
376
+ client: this.client,
377
+ toRaw: (event) => matrixEventToRaw(event, { contentMode: "original" }),
378
+ emitDecryptedEvent: (roomId, event) => {
379
+ this.emitter.emit("room.decrypted_event", roomId, event);
380
+ },
381
+ emitMessage: (roomId, event) => {
382
+ this.emitter.emit("room.message", roomId, event);
383
+ },
384
+ emitFailedDecryption: (roomId, event, error) => {
385
+ this.emitter.emit("room.failed_decryption", roomId, event, error);
386
+ }
387
+ });
388
+ if (!this.encryptionEnabled) return;
389
+ this.verificationManager ??= new runtime.MatrixVerificationManager({ trustOwnDeviceAfterSas: async (deviceId) => {
390
+ const crypto = this.client.getCrypto();
391
+ if (typeof crypto?.crossSignDevice !== "function") return;
392
+ await crypto.crossSignDevice(deviceId);
393
+ } });
394
+ this.cryptoBootstrapper ??= new runtime.MatrixCryptoBootstrapper({
395
+ getUserId: () => this.getUserId(),
396
+ getPassword: () => this.password,
397
+ getDeviceId: () => this.client.getDeviceId(),
398
+ verificationManager: this.verificationManager,
399
+ recoveryKeyStore: this.recoveryKeyStore,
400
+ decryptBridge: this.decryptBridge
401
+ });
402
+ if (!this.crypto) this.crypto = runtime.createMatrixCryptoFacade({
403
+ client: this.client,
404
+ verificationManager: this.verificationManager,
405
+ recoveryKeyStore: this.recoveryKeyStore,
406
+ getRoomStateEvent: (roomId, eventType, stateKey = "") => this.getRoomStateEvent(roomId, eventType, stateKey),
407
+ downloadContent: (mxcUrl) => this.downloadContent(mxcUrl)
408
+ });
409
+ if (!this.verificationSummaryListenerBound) {
410
+ this.verificationSummaryListenerBound = true;
411
+ this.verificationManager.onSummaryChanged((summary) => {
412
+ this.emitter.emit("verification.summary", summary);
413
+ });
414
+ }
415
+ }
416
+ async start(opts = {}) {
417
+ await this.startSyncSession({
418
+ bootstrapCrypto: true,
419
+ abortSignal: opts.abortSignal,
420
+ readyTimeoutMs: opts.readyTimeoutMs
421
+ });
422
+ }
423
+ async waitForInitialSyncReady(params = {}) {
424
+ const timeoutMs = params.timeoutMs ?? 3e4;
425
+ if (isMatrixReadySyncState(this.currentSyncState)) return;
426
+ if (isMatrixTerminalSyncState(this.currentSyncState)) throw new Error(`Matrix sync entered ${this.currentSyncState} during startup`);
427
+ await new Promise((resolve, reject) => {
428
+ let settled = false;
429
+ let timeoutId;
430
+ const abortSignal = params.abortSignal;
431
+ const cleanup = () => {
432
+ this.off("sync.state", onSyncState);
433
+ this.off("sync.unexpected_error", onUnexpectedError);
434
+ abortSignal?.removeEventListener("abort", onAbort);
435
+ if (timeoutId) {
436
+ clearTimeout(timeoutId);
437
+ timeoutId = void 0;
438
+ }
439
+ };
440
+ const settleResolve = () => {
441
+ if (settled) return;
442
+ settled = true;
443
+ cleanup();
444
+ resolve();
445
+ };
446
+ const settleReject = (error) => {
447
+ if (settled) return;
448
+ settled = true;
449
+ cleanup();
450
+ reject(error);
451
+ };
452
+ const onSyncState = (state, _prevState, error) => {
453
+ if (isMatrixReadySyncState(state)) {
454
+ settleResolve();
455
+ return;
456
+ }
457
+ if (isMatrixTerminalSyncState(state)) settleReject(new Error(error instanceof Error && error.message ? error.message : `Matrix sync entered ${state} during startup`));
458
+ };
459
+ const onUnexpectedError = (error) => {
460
+ settleReject(error);
461
+ };
462
+ const onAbort = () => {
463
+ settleReject(createMatrixStartupAbortError());
464
+ };
465
+ this.on("sync.state", onSyncState);
466
+ this.on("sync.unexpected_error", onUnexpectedError);
467
+ if (abortSignal?.aborted) {
468
+ onAbort();
469
+ return;
470
+ }
471
+ abortSignal?.addEventListener("abort", onAbort, { once: true });
472
+ timeoutId = setTimeout(() => {
473
+ settleReject(/* @__PURE__ */ new Error(`Matrix client did not reach a ready sync state within ${timeoutMs}ms`));
474
+ }, timeoutMs);
475
+ timeoutId.unref?.();
476
+ });
477
+ }
478
+ async startSyncSession(opts) {
479
+ if (this.started) return;
480
+ throwIfMatrixStartupAborted(opts.abortSignal);
481
+ await this.ensureCryptoSupportInitialized();
482
+ throwIfMatrixStartupAborted(opts.abortSignal);
483
+ this.registerBridge();
484
+ await this.initializeCryptoIfNeeded(opts.abortSignal);
485
+ await this.client.startClient({
486
+ initialSyncLimit: this.initialSyncLimit,
487
+ ...this.syncFilter ? { filter: Filter.fromJson(this.selfUserId, "", this.syncFilter) } : {}
488
+ });
489
+ await this.waitForInitialSyncReady({
490
+ abortSignal: opts.abortSignal,
491
+ timeoutMs: opts.readyTimeoutMs
492
+ });
493
+ throwIfMatrixStartupAborted(opts.abortSignal);
494
+ if (opts.bootstrapCrypto && this.autoBootstrapCrypto) await this.bootstrapCryptoIfNeeded(opts.abortSignal);
495
+ throwIfMatrixStartupAborted(opts.abortSignal);
496
+ this.started = true;
497
+ this.emitOutstandingInviteEvents();
498
+ await this.refreshDmCache().catch(noop);
499
+ }
500
+ async prepareForOneOff() {
501
+ if (!this.encryptionEnabled) return;
502
+ await this.ensureCryptoSupportInitialized();
503
+ await this.initializeCryptoIfNeeded();
504
+ if (!this.crypto) return;
505
+ try {
506
+ const joinedRooms = await this.getJoinedRooms();
507
+ await this.crypto.prepare(joinedRooms);
508
+ } catch {}
509
+ }
510
+ hasPersistedSyncState() {
511
+ return this.syncStore?.hasSavedSyncFromCleanShutdown() === true;
512
+ }
513
+ async ensureStartedForCryptoControlPlane() {
514
+ if (this.started) return;
515
+ await this.startSyncSession({ bootstrapCrypto: false });
516
+ }
517
+ stopSyncWithoutPersist() {
518
+ if (this.idbPersistTimer) {
519
+ clearInterval(this.idbPersistTimer);
520
+ this.idbPersistTimer = null;
521
+ }
522
+ this.currentSyncState = null;
523
+ this.client.stopClient();
524
+ this.started = false;
525
+ }
526
+ async drainPendingDecryptions(reason = "matrix client shutdown") {
527
+ await this.decryptBridge?.drainPendingDecryptions(reason);
528
+ }
529
+ stop() {
530
+ this.stopSyncWithoutPersist();
531
+ this.decryptBridge?.stop();
532
+ this.syncStore?.markCleanShutdown();
533
+ if (loadedMatrixCryptoRuntime) {
534
+ const { persistIdbToDisk } = loadedMatrixCryptoRuntime;
535
+ this.stopPersistPromise = Promise.all([persistIdbToDisk({
536
+ snapshotPath: this.idbSnapshotPath,
537
+ databasePrefix: this.cryptoDatabasePrefix
538
+ }).catch(noop), this.syncStore?.flush().catch(noop)]).then(() => void 0);
539
+ return;
540
+ }
541
+ this.stopPersistPromise = loadMatrixCryptoRuntime().then(async ({ persistIdbToDisk }) => {
542
+ await Promise.all([persistIdbToDisk({
543
+ snapshotPath: this.idbSnapshotPath,
544
+ databasePrefix: this.cryptoDatabasePrefix
545
+ }).catch(noop), this.syncStore?.flush().catch(noop)]);
546
+ }).catch(noop).then(() => void 0);
547
+ }
548
+ async stopAndPersist() {
549
+ this.stop();
550
+ await this.stopPersistPromise;
551
+ }
552
+ stopWithoutPersist() {
553
+ this.stopSyncWithoutPersist();
554
+ this.decryptBridge?.stop();
555
+ this.stopPersistPromise = Promise.resolve();
556
+ }
557
+ async bootstrapCryptoIfNeeded(abortSignal) {
558
+ if (!this.encryptionEnabled || !this.cryptoInitialized || this.cryptoBootstrapped) return;
559
+ throwIfMatrixStartupAborted(abortSignal);
560
+ await this.ensureCryptoSupportInitialized();
561
+ const crypto = this.client.getCrypto();
562
+ if (!crypto) return;
563
+ const cryptoBootstrapper = this.cryptoBootstrapper;
564
+ if (!cryptoBootstrapper) return;
565
+ const initial = await cryptoBootstrapper.bootstrap(crypto, MATRIX_INITIAL_CRYPTO_BOOTSTRAP_OPTIONS);
566
+ throwIfMatrixStartupAborted(abortSignal);
567
+ if (!initial.crossSigningPublished || initial.ownDeviceVerified === false) if ((await this.getOwnDeviceVerificationStatus()).signedByOwner) LogService.warn("MatrixClientLite", "Cross-signing/bootstrap is incomplete for an already owner-signed device; skipping automatic reset and preserving the current identity. Restore the recovery key or run an explicit verification bootstrap if repair is needed.");
568
+ else try {
569
+ const repaired = await cryptoBootstrapper.bootstrap(crypto, MATRIX_AUTOMATIC_REPAIR_BOOTSTRAP_OPTIONS);
570
+ throwIfMatrixStartupAborted(abortSignal);
571
+ if (repaired.crossSigningPublished && repaired.ownDeviceVerified !== false) LogService.info("MatrixClientLite", "Cross-signing/bootstrap recovered after forced reset");
572
+ } catch (err) {
573
+ LogService.warn("MatrixClientLite", "Failed to recover cross-signing/bootstrap with forced reset:", err);
574
+ }
575
+ this.cryptoBootstrapped = true;
576
+ }
577
+ async initializeCryptoIfNeeded(abortSignal) {
578
+ if (!this.encryptionEnabled || this.cryptoInitialized) return;
579
+ throwIfMatrixStartupAborted(abortSignal);
580
+ const { persistIdbToDisk, restoreIdbFromDisk } = await loadMatrixCryptoRuntime();
581
+ await restoreIdbFromDisk(this.idbSnapshotPath);
582
+ throwIfMatrixStartupAborted(abortSignal);
583
+ try {
584
+ await this.client.initRustCrypto({ cryptoDatabasePrefix: this.cryptoDatabasePrefix });
585
+ this.cryptoInitialized = true;
586
+ throwIfMatrixStartupAborted(abortSignal);
587
+ await persistIdbToDisk({
588
+ snapshotPath: this.idbSnapshotPath,
589
+ databasePrefix: this.cryptoDatabasePrefix
590
+ });
591
+ throwIfMatrixStartupAborted(abortSignal);
592
+ this.idbPersistTimer = setInterval(() => {
593
+ persistIdbToDisk({
594
+ snapshotPath: this.idbSnapshotPath,
595
+ databasePrefix: this.cryptoDatabasePrefix
596
+ }).catch(noop);
597
+ }, MATRIX_IDB_PERSIST_INTERVAL_MS);
598
+ this.idbPersistTimer.unref?.();
599
+ } catch (err) {
600
+ LogService.warn("MatrixClientLite", "Failed to initialize rust crypto:", err);
601
+ }
602
+ }
603
+ async getUserId() {
604
+ const fromClient = this.client.getUserId();
605
+ if (fromClient) {
606
+ this.selfUserId = fromClient;
607
+ return fromClient;
608
+ }
609
+ if (this.selfUserId) return this.selfUserId;
610
+ const resolved = (await this.doRequest("GET", "/_matrix/client/v3/account/whoami")).user_id?.trim();
611
+ if (!resolved) throw new Error("Matrix whoami did not return user_id");
612
+ this.selfUserId = resolved;
613
+ return resolved;
614
+ }
615
+ async getJoinedRooms() {
616
+ const joined = await this.doRequest("GET", "/_matrix/client/v3/joined_rooms");
617
+ return Array.isArray(joined.joined_rooms) ? joined.joined_rooms : [];
618
+ }
619
+ async getJoinedRoomMembers(roomId) {
620
+ const joined = (await this.client.getJoinedRoomMembers(roomId))?.joined;
621
+ if (!joined || typeof joined !== "object") return [];
622
+ return Object.keys(joined);
623
+ }
624
+ hasSyncedJoinedRoomMember(roomId, userId) {
625
+ return (this.client.getRoom?.(roomId))?.currentState?.getMember?.(userId)?.membership === "join";
626
+ }
627
+ async getRoomStateEvent(roomId, eventType, stateKey = "") {
628
+ return await this.client.getStateEvent(roomId, eventType, stateKey) ?? {};
629
+ }
630
+ async getAccountData(eventType) {
631
+ return this.client.getAccountData(eventType)?.getContent() ?? void 0;
632
+ }
633
+ async setAccountData(eventType, content) {
634
+ await this.client.setAccountData(eventType, content);
635
+ await this.refreshDmCache().catch(noop);
636
+ }
637
+ async resolveRoom(aliasOrRoomId) {
638
+ if (aliasOrRoomId.startsWith("!")) return aliasOrRoomId;
639
+ if (!aliasOrRoomId.startsWith("#")) return aliasOrRoomId;
640
+ try {
641
+ return (await this.client.getRoomIdForAlias(aliasOrRoomId)).room_id ?? null;
642
+ } catch {
643
+ return null;
644
+ }
645
+ }
646
+ async createDirectRoom(remoteUserId, opts = {}) {
647
+ const initialState = opts.encrypted ? [{
648
+ type: "m.room.encryption",
649
+ state_key: "",
650
+ content: { algorithm: "m.megolm.v1.aes-sha2" }
651
+ }] : void 0;
652
+ return (await this.client.createRoom({
653
+ invite: [remoteUserId],
654
+ is_direct: true,
655
+ preset: Preset.TrustedPrivateChat,
656
+ initial_state: initialState
657
+ })).room_id;
658
+ }
659
+ async sendMessage(roomId, content) {
660
+ return await this.runSerializedRoomSend(roomId, async () => {
661
+ return (await this.client.sendMessage(roomId, content)).event_id;
662
+ });
663
+ }
664
+ async sendEvent(roomId, eventType, content) {
665
+ return await this.runSerializedRoomSend(roomId, async () => {
666
+ return (await this.client.sendEvent(roomId, eventType, content)).event_id;
667
+ });
668
+ }
669
+ async runSerializedRoomSend(roomId, task) {
670
+ return await this.sendQueue.enqueue(roomId, task);
671
+ }
672
+ async sendStateEvent(roomId, eventType, stateKey, content) {
673
+ return (await this.client.sendStateEvent(roomId, eventType, content, stateKey)).event_id;
674
+ }
675
+ async redactEvent(roomId, eventId, reason) {
676
+ return (await this.client.redactEvent(roomId, eventId, void 0, reason?.trim() ? { reason } : void 0)).event_id;
677
+ }
678
+ async doRequest(method, endpoint, qs, body, opts) {
679
+ return await this.httpClient.requestJson({
680
+ method,
681
+ endpoint,
682
+ qs,
683
+ body,
684
+ timeoutMs: this.localTimeoutMs,
685
+ allowAbsoluteEndpoint: opts?.allowAbsoluteEndpoint
686
+ });
687
+ }
688
+ async getUserProfile(userId) {
689
+ return await this.client.getProfileInfo(userId);
690
+ }
691
+ async setDisplayName(displayName) {
692
+ await this.client.setDisplayName(displayName);
693
+ }
694
+ async setAvatarUrl(avatarUrl) {
695
+ await this.client.setAvatarUrl(avatarUrl);
696
+ }
697
+ async joinRoom(roomId) {
698
+ await this.client.joinRoom(roomId);
699
+ }
700
+ mxcToHttp(mxcUrl) {
701
+ return this.client.mxcUrlToHttp(mxcUrl, void 0, void 0, void 0, true, false, true);
702
+ }
703
+ async downloadContent(mxcUrl, opts = {}) {
704
+ const parsed = parseMxc(mxcUrl);
705
+ if (!parsed) throw new Error(`Invalid Matrix content URI: ${mxcUrl}`);
706
+ const encodedServer = encodeURIComponent(parsed.server);
707
+ const encodedMediaId = encodeURIComponent(parsed.mediaId);
708
+ const request = async (endpoint) => await this.httpClient.requestRaw({
709
+ method: "GET",
710
+ endpoint,
711
+ qs: { allow_remote: opts.allowRemote ?? true },
712
+ timeoutMs: this.localTimeoutMs,
713
+ maxBytes: opts.maxBytes,
714
+ readIdleTimeoutMs: opts.readIdleTimeoutMs
715
+ });
716
+ const authenticatedEndpoint = `/_matrix/client/v1/media/download/${encodedServer}/${encodedMediaId}`;
717
+ try {
718
+ return await request(authenticatedEndpoint);
719
+ } catch (err) {
720
+ if (!isUnsupportedAuthenticatedMediaEndpointError(err)) throw err;
721
+ }
722
+ return await request(`/_matrix/media/v3/download/${encodedServer}/${encodedMediaId}`);
723
+ }
724
+ async uploadContent(file, contentType, filename) {
725
+ return (await this.client.uploadContent(new Uint8Array(file), {
726
+ type: contentType || "application/octet-stream",
727
+ name: filename,
728
+ includeFilename: Boolean(filename)
729
+ })).content_uri;
730
+ }
731
+ async getEvent(roomId, eventId) {
732
+ const rawEvent = await this.client.fetchRoomEvent(roomId, eventId);
733
+ if (rawEvent.type !== "m.room.encrypted") return rawEvent;
734
+ const event = this.client.getEventMapper()(rawEvent);
735
+ let decryptedEvent;
736
+ const onDecrypted = (candidate) => {
737
+ decryptedEvent = candidate;
738
+ };
739
+ event.once(MatrixEventEvent.Decrypted, onDecrypted);
740
+ try {
741
+ await this.client.decryptEventIfNeeded(event);
742
+ } finally {
743
+ event.off(MatrixEventEvent.Decrypted, onDecrypted);
744
+ }
745
+ return matrixEventToRaw(decryptedEvent ?? event);
746
+ }
747
+ async getRelations(roomId, eventId, relationType, eventType, opts = {}) {
748
+ const result = await this.client.relations(roomId, eventId, relationType, eventType, opts);
749
+ return {
750
+ originalEvent: result.originalEvent ? matrixEventToRaw(result.originalEvent) : null,
751
+ events: result.events.map((event) => matrixEventToRaw(event)),
752
+ nextBatch: result.nextBatch ?? null,
753
+ prevBatch: result.prevBatch ?? null
754
+ };
755
+ }
756
+ async hydrateEvents(roomId, events) {
757
+ if (events.length === 0) return [];
758
+ const mapper = this.client.getEventMapper();
759
+ const mappedEvents = events.map((event) => mapper({
760
+ room_id: roomId,
761
+ ...event
762
+ }));
763
+ await Promise.all(mappedEvents.map((event) => this.client.decryptEventIfNeeded(event)));
764
+ return mappedEvents.map((event) => matrixEventToRaw(event));
765
+ }
766
+ async setTyping(roomId, typing, timeoutMs) {
767
+ await this.client.sendTyping(roomId, typing, timeoutMs);
768
+ }
769
+ async sendReadReceipt(roomId, eventId) {
770
+ await this.httpClient.requestJson({
771
+ method: "POST",
772
+ endpoint: `/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}/receipt/m.read/${encodeURIComponent(eventId)}`,
773
+ body: {},
774
+ timeoutMs: this.localTimeoutMs
775
+ });
776
+ }
777
+ async getRoomKeyBackupStatus() {
778
+ if (!this.encryptionEnabled) return {
779
+ serverVersion: null,
780
+ activeVersion: null,
781
+ trusted: null,
782
+ matchesDecryptionKey: null,
783
+ decryptionKeyCached: null,
784
+ keyLoadAttempted: false,
785
+ keyLoadError: null
786
+ };
787
+ const crypto = this.client.getCrypto();
788
+ const serverVersionFallback = await this.resolveRoomKeyBackupVersion();
789
+ if (!crypto) return {
790
+ serverVersion: serverVersionFallback,
791
+ activeVersion: null,
792
+ trusted: null,
793
+ matchesDecryptionKey: null,
794
+ decryptionKeyCached: null,
795
+ keyLoadAttempted: false,
796
+ keyLoadError: null
797
+ };
798
+ let { activeVersion, decryptionKeyCached } = await this.resolveRoomKeyBackupLocalState(crypto);
799
+ let { serverVersion, trusted, matchesDecryptionKey } = await this.resolveRoomKeyBackupTrustState(crypto, serverVersionFallback);
800
+ const shouldLoadBackupKey = Boolean(serverVersion) && (decryptionKeyCached === false || matchesDecryptionKey === false);
801
+ const shouldActivateBackup = Boolean(serverVersion) && !activeVersion;
802
+ let keyLoadAttempted = false;
803
+ let keyLoadError = null;
804
+ if (serverVersion && (shouldLoadBackupKey || shouldActivateBackup)) {
805
+ if (shouldLoadBackupKey) if (typeof crypto.loadSessionBackupPrivateKeyFromSecretStorage === "function") {
806
+ keyLoadAttempted = true;
807
+ try {
808
+ await crypto.loadSessionBackupPrivateKeyFromSecretStorage();
809
+ } catch (err) {
810
+ keyLoadError = formatMatrixErrorMessage(err);
811
+ }
812
+ } else keyLoadError = "Matrix crypto backend does not support loading backup keys from secret storage";
813
+ if (!keyLoadError) await this.enableTrustedRoomKeyBackupIfPossible(crypto);
814
+ ({activeVersion, decryptionKeyCached} = await this.resolveRoomKeyBackupLocalState(crypto));
815
+ ({serverVersion, trusted, matchesDecryptionKey} = await this.resolveRoomKeyBackupTrustState(crypto, serverVersion));
816
+ }
817
+ return {
818
+ serverVersion,
819
+ activeVersion,
820
+ trusted,
821
+ matchesDecryptionKey,
822
+ decryptionKeyCached,
823
+ keyLoadAttempted,
824
+ keyLoadError
825
+ };
826
+ }
827
+ async getDeviceVerificationStatus(userId, deviceId) {
828
+ const normalizedUserId = userId?.trim() || null;
829
+ const normalizedDeviceId = deviceId?.trim() || null;
830
+ if (!this.encryptionEnabled) return {
831
+ encryptionEnabled: false,
832
+ userId: normalizedUserId,
833
+ deviceId: normalizedDeviceId,
834
+ verified: false,
835
+ localVerified: false,
836
+ crossSigningVerified: false,
837
+ signedByOwner: false
838
+ };
839
+ const crypto = this.client.getCrypto();
840
+ let deviceStatus = null;
841
+ if (crypto && normalizedUserId && normalizedDeviceId && typeof crypto.getDeviceVerificationStatus === "function") deviceStatus = await crypto.getDeviceVerificationStatus(normalizedUserId, normalizedDeviceId).catch(() => null);
842
+ const { isMatrixDeviceVerifiedInCurrentClient } = await loadMatrixCryptoRuntime();
843
+ return {
844
+ encryptionEnabled: true,
845
+ userId: normalizedUserId,
846
+ deviceId: normalizedDeviceId,
847
+ verified: isMatrixDeviceVerifiedInCurrentClient(deviceStatus),
848
+ localVerified: deviceStatus?.localVerified === true,
849
+ crossSigningVerified: deviceStatus?.crossSigningVerified === true,
850
+ signedByOwner: deviceStatus?.signedByOwner === true
851
+ };
852
+ }
853
+ async getOwnDeviceVerificationStatus() {
854
+ const recoveryKey = this.recoveryKeyStore.getRecoveryKeySummary();
855
+ const userId = this.client.getUserId() ?? this.selfUserId ?? null;
856
+ const deviceId = this.client.getDeviceId()?.trim() || null;
857
+ const diagnosticTimeoutMs = Math.min(this.localTimeoutMs, MATRIX_STATUS_DIAGNOSTIC_TIMEOUT_MS);
858
+ const [backup, deviceVerification, ownDevices] = await Promise.all([
859
+ resolveMatrixDiagnostic(this.getRoomKeyBackupStatus(), diagnosticTimeoutMs),
860
+ resolveMatrixDiagnostic(this.getDeviceVerificationStatus(userId, deviceId), diagnosticTimeoutMs),
861
+ resolveMatrixDiagnosticResult(this.listOwnDevices(), diagnosticTimeoutMs)
862
+ ]);
863
+ const resolvedBackup = backup ?? unresolvedMatrixRoomKeyBackupStatus();
864
+ const resolvedDeviceVerification = deviceVerification ?? unresolvedMatrixDeviceVerificationStatus({
865
+ userId,
866
+ deviceId
867
+ });
868
+ const serverDeviceKnown = deviceId ? ownDevices.value ? ownDevices.value.some((device) => device.deviceId === deviceId) : isMatrixAccessTokenInvalidatedError(ownDevices.error) ? false : null : null;
869
+ return {
870
+ ...resolvedDeviceVerification,
871
+ verified: resolvedDeviceVerification.crossSigningVerified,
872
+ recoveryKeyStored: Boolean(recoveryKey),
873
+ recoveryKeyCreatedAt: recoveryKey?.createdAt ?? null,
874
+ recoveryKeyId: recoveryKey?.keyId ?? null,
875
+ backupVersion: resolvedBackup.serverVersion,
876
+ backup: resolvedBackup,
877
+ serverDeviceKnown
878
+ };
879
+ }
880
+ async getOwnDeviceIdentityVerificationStatus() {
881
+ const userId = this.client.getUserId() ?? this.selfUserId ?? null;
882
+ const deviceId = this.client.getDeviceId()?.trim() || null;
883
+ const deviceVerification = await this.getDeviceVerificationStatus(userId, deviceId);
884
+ return {
885
+ ...deviceVerification,
886
+ verified: deviceVerification.crossSigningVerified
887
+ };
888
+ }
889
+ async trustOwnIdentityAfterSelfVerification() {
890
+ if (!this.encryptionEnabled) return;
891
+ await this.ensureStartedForCryptoControlPlane();
892
+ await this.ensureCryptoSupportInitialized();
893
+ const crypto = this.client.getCrypto();
894
+ const ownIdentity = crypto && typeof crypto.getOwnIdentity === "function" ? await crypto.getOwnIdentity().catch(() => void 0) : void 0;
895
+ if (!ownIdentity) return;
896
+ try {
897
+ if (typeof ownIdentity.isVerified === "function" && ownIdentity.isVerified()) return;
898
+ if (typeof ownIdentity.verify !== "function") return;
899
+ await ownIdentity.verify();
900
+ } finally {
901
+ ownIdentity.free?.();
902
+ }
903
+ }
904
+ async verifyWithRecoveryKey(rawRecoveryKey) {
905
+ const fail = async (error, fields = {}) => {
906
+ const status = await this.getOwnDeviceVerificationStatus();
907
+ return {
908
+ success: false,
909
+ recoveryKeyAccepted: fields.recoveryKeyAccepted ?? false,
910
+ backupUsable: fields.backupUsable ?? false,
911
+ deviceOwnerVerified: fields.deviceOwnerVerified ?? status.verified,
912
+ error,
913
+ ...status
914
+ };
915
+ };
916
+ if (!this.encryptionEnabled) return await fail("Matrix encryption is disabled for this client");
917
+ await this.ensureStartedForCryptoControlPlane();
918
+ await this.ensureCryptoSupportInitialized();
919
+ const crypto = this.client.getCrypto();
920
+ if (!crypto) return await fail("Matrix crypto is not available (start client with encryption enabled)");
921
+ const backupUsableBeforeStagedRecovery = resolveMatrixRoomKeyBackupReadinessError(await this.getRoomKeyBackupStatus(), { requireServerBackup: true }) === null;
922
+ const trimmedRecoveryKey = rawRecoveryKey.trim();
923
+ if (!trimmedRecoveryKey) return await fail("Matrix recovery key is required");
924
+ let stagedKeyId = null;
925
+ try {
926
+ stagedKeyId = await this.resolveDefaultSecretStorageKeyId(crypto) ?? null;
927
+ this.recoveryKeyStore.stageEncodedRecoveryKey({
928
+ encodedPrivateKey: trimmedRecoveryKey,
929
+ keyId: stagedKeyId
930
+ });
931
+ } catch (err) {
932
+ return await fail(formatMatrixErrorMessage(err));
933
+ }
934
+ const storedRecoveryKeyMatches = this.recoveryKeyStore.getRecoveryKeySummary()?.encodedPrivateKey?.trim() === trimmedRecoveryKey;
935
+ if (backupUsableBeforeStagedRecovery && storedRecoveryKeyMatches) {
936
+ const status = await this.getOwnDeviceVerificationStatus();
937
+ const backupUsable = resolveMatrixRoomKeyBackupReadinessError(status.backup, { requireServerBackup: true }) === null;
938
+ const backupError = resolveMatrixRoomKeyBackupReadinessError(status.backup, { requireServerBackup: false });
939
+ const recoveryKeyAccepted = backupUsable;
940
+ if (!status.verified) {
941
+ if (recoveryKeyAccepted) this.recoveryKeyStore.commitStagedRecoveryKey({ keyId: stagedKeyId });
942
+ else this.recoveryKeyStore.discardStagedRecoveryKey();
943
+ return {
944
+ success: false,
945
+ recoveryKeyAccepted,
946
+ backupUsable,
947
+ deviceOwnerVerified: false,
948
+ error: "Matrix recovery key was applied, but this device still lacks full Matrix identity trust. The recovery key can unlock usable backup material only when 'Backup usable' is yes; full identity trust still requires Matrix cross-signing verification.",
949
+ ...status
950
+ };
951
+ }
952
+ if (backupError) {
953
+ this.recoveryKeyStore.discardStagedRecoveryKey();
954
+ return {
955
+ success: false,
956
+ recoveryKeyAccepted,
957
+ backupUsable,
958
+ deviceOwnerVerified: true,
959
+ error: backupError,
960
+ ...status
961
+ };
962
+ }
963
+ this.recoveryKeyStore.commitStagedRecoveryKey({ keyId: stagedKeyId });
964
+ return {
965
+ success: true,
966
+ recoveryKeyAccepted: true,
967
+ backupUsable,
968
+ deviceOwnerVerified: true,
969
+ verifiedAt: (/* @__PURE__ */ new Date()).toISOString(),
970
+ ...status
971
+ };
972
+ }
973
+ try {
974
+ const cryptoBootstrapper = this.cryptoBootstrapper;
975
+ if (!cryptoBootstrapper) return await fail("Matrix crypto bootstrapper is not available");
976
+ await cryptoBootstrapper.bootstrap(crypto, { allowAutomaticCrossSigningReset: false });
977
+ await this.enableTrustedRoomKeyBackupIfPossible(crypto);
978
+ const status = await this.getOwnDeviceVerificationStatus();
979
+ const backupError = resolveMatrixRoomKeyBackupReadinessError(status.backup, { requireServerBackup: false });
980
+ const backupUsable = resolveMatrixRoomKeyBackupReadinessError(status.backup, { requireServerBackup: true }) === null;
981
+ const stagedRecoveryKeyUsed = this.recoveryKeyStore.hasStagedRecoveryKeyBeenUsed();
982
+ const secretStorageStatus = typeof crypto.getSecretStorageStatus === "function" ? await crypto.getSecretStorageStatus().catch(() => null) : null;
983
+ const stagedRecoveryKeyConfirmedBySecretStorage = Boolean(stagedKeyId) && secretStorageStatus?.secretStorageKeyValidityMap?.[stagedKeyId ?? ""] === true;
984
+ const stagedRecoveryKeyRejectedBySecretStorage = Boolean(stagedKeyId) && secretStorageStatus?.secretStorageKeyValidityMap?.[stagedKeyId ?? ""] === false;
985
+ const stagedRecoveryKeyValidated = stagedRecoveryKeyUsed && (stagedRecoveryKeyConfirmedBySecretStorage || stagedRecoveryKeyUsed && !stagedRecoveryKeyRejectedBySecretStorage && !stagedRecoveryKeyConfirmedBySecretStorage && !backupUsableBeforeStagedRecovery && backupUsable) || storedRecoveryKeyMatches && backupUsable;
986
+ const recoveryKeyAccepted = stagedRecoveryKeyValidated && (status.verified || backupUsable);
987
+ if (!status.verified) {
988
+ if (backupUsable && stagedRecoveryKeyValidated) this.recoveryKeyStore.commitStagedRecoveryKey({ keyId: stagedKeyId });
989
+ else this.recoveryKeyStore.discardStagedRecoveryKey();
990
+ return {
991
+ success: false,
992
+ recoveryKeyAccepted,
993
+ backupUsable,
994
+ deviceOwnerVerified: false,
995
+ error: "Matrix recovery key was applied, but this device still lacks full Matrix identity trust. The recovery key can unlock usable backup material only when 'Backup usable' is yes; full identity trust still requires Matrix cross-signing verification.",
996
+ ...recoveryKeyAccepted ? await this.getOwnDeviceVerificationStatus() : status
997
+ };
998
+ }
999
+ if (backupError) {
1000
+ this.recoveryKeyStore.discardStagedRecoveryKey();
1001
+ return {
1002
+ success: false,
1003
+ recoveryKeyAccepted,
1004
+ backupUsable,
1005
+ deviceOwnerVerified: true,
1006
+ error: backupError,
1007
+ ...status
1008
+ };
1009
+ }
1010
+ if (!stagedRecoveryKeyValidated) {
1011
+ this.recoveryKeyStore.discardStagedRecoveryKey();
1012
+ return {
1013
+ success: false,
1014
+ recoveryKeyAccepted: false,
1015
+ backupUsable,
1016
+ deviceOwnerVerified: true,
1017
+ error: "Matrix recovery key could not be verified against active Matrix backup material; existing backup may be usable from previously loaded recovery material.",
1018
+ ...status
1019
+ };
1020
+ }
1021
+ this.recoveryKeyStore.commitStagedRecoveryKey({ keyId: stagedKeyId });
1022
+ const committedStatus = await this.getOwnDeviceVerificationStatus();
1023
+ return {
1024
+ success: true,
1025
+ recoveryKeyAccepted: true,
1026
+ backupUsable,
1027
+ deviceOwnerVerified: true,
1028
+ verifiedAt: (/* @__PURE__ */ new Date()).toISOString(),
1029
+ ...committedStatus
1030
+ };
1031
+ } catch (err) {
1032
+ this.recoveryKeyStore.discardStagedRecoveryKey();
1033
+ return await fail(formatMatrixErrorMessage(err));
1034
+ }
1035
+ }
1036
+ async restoreRoomKeyBackup(params = {}) {
1037
+ let loadedFromSecretStorage = false;
1038
+ const fail = async (error) => {
1039
+ const backup = await this.getRoomKeyBackupStatus();
1040
+ return {
1041
+ success: false,
1042
+ error,
1043
+ backupVersion: backup.serverVersion,
1044
+ imported: 0,
1045
+ total: 0,
1046
+ loadedFromSecretStorage,
1047
+ backup
1048
+ };
1049
+ };
1050
+ if (!this.encryptionEnabled) return await fail("Matrix encryption is disabled for this client");
1051
+ await this.ensureStartedForCryptoControlPlane();
1052
+ const crypto = this.client.getCrypto();
1053
+ if (!crypto) return await fail("Matrix crypto is not available (start client with encryption enabled)");
1054
+ try {
1055
+ const rawRecoveryKey = params.recoveryKey?.trim();
1056
+ if (rawRecoveryKey) this.recoveryKeyStore.stageEncodedRecoveryKey({
1057
+ encodedPrivateKey: rawRecoveryKey,
1058
+ keyId: await this.resolveDefaultSecretStorageKeyId(crypto)
1059
+ });
1060
+ const backup = await this.getRoomKeyBackupStatus();
1061
+ loadedFromSecretStorage = backup.keyLoadAttempted && !backup.keyLoadError;
1062
+ const backupError = resolveMatrixRoomKeyBackupReadinessError(backup, {
1063
+ allowUntrustedMatchingKey: true,
1064
+ requireServerBackup: true
1065
+ });
1066
+ if (backupError) {
1067
+ this.recoveryKeyStore.discardStagedRecoveryKey();
1068
+ return await fail(backupError);
1069
+ }
1070
+ if (typeof crypto.restoreKeyBackup !== "function") {
1071
+ this.recoveryKeyStore.discardStagedRecoveryKey();
1072
+ return await fail("Matrix crypto backend does not support full key backup restore");
1073
+ }
1074
+ const restore = await crypto.restoreKeyBackup();
1075
+ if (rawRecoveryKey) this.recoveryKeyStore.commitStagedRecoveryKey({ keyId: await this.resolveDefaultSecretStorageKeyId(crypto) });
1076
+ const finalBackup = await this.getRoomKeyBackupStatus();
1077
+ return {
1078
+ success: true,
1079
+ backupVersion: backup.serverVersion,
1080
+ imported: typeof restore.imported === "number" ? restore.imported : 0,
1081
+ total: typeof restore.total === "number" ? restore.total : 0,
1082
+ loadedFromSecretStorage,
1083
+ restoredAt: (/* @__PURE__ */ new Date()).toISOString(),
1084
+ backup: finalBackup
1085
+ };
1086
+ } catch (err) {
1087
+ this.recoveryKeyStore.discardStagedRecoveryKey();
1088
+ return await fail(formatMatrixErrorMessage(err));
1089
+ }
1090
+ }
1091
+ async resetRoomKeyBackup(options = {}) {
1092
+ let previousVersion = null;
1093
+ let deletedVersion = null;
1094
+ const fail = async (error) => {
1095
+ const backup = await this.getRoomKeyBackupStatus();
1096
+ return {
1097
+ success: false,
1098
+ error,
1099
+ previousVersion,
1100
+ deletedVersion,
1101
+ createdVersion: backup.serverVersion,
1102
+ backup
1103
+ };
1104
+ };
1105
+ if (!this.encryptionEnabled) return await fail("Matrix encryption is disabled for this client");
1106
+ await this.ensureStartedForCryptoControlPlane();
1107
+ const crypto = this.client.getCrypto();
1108
+ if (!crypto) return await fail("Matrix crypto is not available (start client with encryption enabled)");
1109
+ previousVersion = await this.resolveRoomKeyBackupVersion();
1110
+ const forceNewSecretStorage = options.rotateRecoveryKey === true || await this.shouldForceSecretStorageRecreationForBackupReset(crypto);
1111
+ try {
1112
+ if (previousVersion) {
1113
+ try {
1114
+ await this.doRequest("DELETE", `/_matrix/client/v3/room_keys/version/${encodeURIComponent(previousVersion)}`);
1115
+ } catch (err) {
1116
+ if (!isMatrixNotFoundError(err)) throw err;
1117
+ }
1118
+ deletedVersion = previousVersion;
1119
+ }
1120
+ await this.recoveryKeyStore.bootstrapSecretStorageWithRecoveryKey(crypto, {
1121
+ setupNewKeyBackup: true,
1122
+ forceNewSecretStorage,
1123
+ forceNewRecoveryKey: options.rotateRecoveryKey === true,
1124
+ allowSecretStorageRecreateWithoutRecoveryKey: true
1125
+ });
1126
+ await this.enableTrustedRoomKeyBackupIfPossible(crypto);
1127
+ const backup = await this.getRoomKeyBackupStatus();
1128
+ const createdVersion = backup.serverVersion;
1129
+ if (!createdVersion) return await fail("Matrix room key backup is still missing after reset.");
1130
+ if (backup.activeVersion !== createdVersion) return await fail("Matrix room key backup was recreated on the server but is not active on this device.");
1131
+ if (backup.decryptionKeyCached === false) return await fail("Matrix room key backup was recreated but its decryption key is not cached on this device.");
1132
+ if (backup.matchesDecryptionKey === false) return await fail("Matrix room key backup was recreated but this device does not have the matching backup decryption key.");
1133
+ if (backup.trusted === false) return await fail("Matrix room key backup was recreated but is not trusted on this device.");
1134
+ return {
1135
+ success: true,
1136
+ previousVersion,
1137
+ deletedVersion,
1138
+ createdVersion,
1139
+ resetAt: (/* @__PURE__ */ new Date()).toISOString(),
1140
+ backup
1141
+ };
1142
+ } catch (err) {
1143
+ return await fail(formatMatrixErrorMessage(err));
1144
+ }
1145
+ }
1146
+ async getOwnCrossSigningPublicationStatus() {
1147
+ const userId = this.client.getUserId() ?? this.selfUserId ?? null;
1148
+ if (!userId) return {
1149
+ userId: null,
1150
+ masterKeyPublished: false,
1151
+ selfSigningKeyPublished: false,
1152
+ userSigningKeyPublished: false,
1153
+ published: false
1154
+ };
1155
+ try {
1156
+ const response = await this.doRequest("POST", "/_matrix/client/v3/keys/query", void 0, { device_keys: { [userId]: [] } });
1157
+ const masterKeyPublished = Boolean(response.master_keys?.[userId]);
1158
+ const selfSigningKeyPublished = Boolean(response.self_signing_keys?.[userId]);
1159
+ const userSigningKeyPublished = Boolean(response.user_signing_keys?.[userId]);
1160
+ return {
1161
+ userId,
1162
+ masterKeyPublished,
1163
+ selfSigningKeyPublished,
1164
+ userSigningKeyPublished,
1165
+ published: masterKeyPublished && selfSigningKeyPublished && userSigningKeyPublished
1166
+ };
1167
+ } catch {
1168
+ return {
1169
+ userId,
1170
+ masterKeyPublished: false,
1171
+ selfSigningKeyPublished: false,
1172
+ userSigningKeyPublished: false,
1173
+ published: false
1174
+ };
1175
+ }
1176
+ }
1177
+ async bootstrapOwnDeviceVerification(params) {
1178
+ const pendingVerifications = async () => this.crypto ? (await this.crypto.listVerifications()).length : 0;
1179
+ if (!this.encryptionEnabled) return {
1180
+ success: false,
1181
+ error: "Matrix encryption is disabled for this client",
1182
+ verification: await this.getOwnDeviceVerificationStatus(),
1183
+ crossSigning: await this.getOwnCrossSigningPublicationStatus(),
1184
+ pendingVerifications: await pendingVerifications(),
1185
+ cryptoBootstrap: null
1186
+ };
1187
+ let bootstrapError;
1188
+ let bootstrapSummary = null;
1189
+ let rawRecoveryKey;
1190
+ try {
1191
+ await this.ensureStartedForCryptoControlPlane();
1192
+ await this.ensureCryptoSupportInitialized();
1193
+ const crypto = this.client.getCrypto();
1194
+ if (!crypto) throw new Error("Matrix crypto is not available (start client with encryption enabled)");
1195
+ rawRecoveryKey = params?.recoveryKey?.trim();
1196
+ if (rawRecoveryKey) this.recoveryKeyStore.stageEncodedRecoveryKey({
1197
+ encodedPrivateKey: rawRecoveryKey,
1198
+ keyId: await this.resolveDefaultSecretStorageKeyId(crypto)
1199
+ });
1200
+ const cryptoBootstrapper = this.cryptoBootstrapper;
1201
+ if (!cryptoBootstrapper) throw new Error("Matrix crypto bootstrapper is not available");
1202
+ bootstrapSummary = await cryptoBootstrapper.bootstrap(crypto, createMatrixExplicitBootstrapOptions({
1203
+ ...params,
1204
+ allowAutomaticCrossSigningReset: rawRecoveryKey ? false : params?.allowAutomaticCrossSigningReset
1205
+ }));
1206
+ await this.ensureRoomKeyBackupEnabled(crypto);
1207
+ } catch (err) {
1208
+ this.recoveryKeyStore.discardStagedRecoveryKey();
1209
+ bootstrapError = formatMatrixErrorMessage(err);
1210
+ }
1211
+ const verification = await this.getOwnDeviceVerificationStatus();
1212
+ const crossSigning = await this.getOwnCrossSigningPublicationStatus();
1213
+ const verificationError = verification.verified && crossSigning.published ? null : bootstrapError ?? "Matrix verification bootstrap did not produce a device verified by its owner with published cross-signing keys";
1214
+ const backupError = verificationError === null ? resolveMatrixRoomKeyBackupReadinessError(verification.backup, {
1215
+ allowUntrustedMatchingKey: Boolean(rawRecoveryKey),
1216
+ requireServerBackup: true
1217
+ }) : null;
1218
+ const success = verificationError === null && backupError === null;
1219
+ if (success) this.recoveryKeyStore.commitStagedRecoveryKey({ keyId: await this.resolveDefaultSecretStorageKeyId(this.client.getCrypto()) });
1220
+ else this.recoveryKeyStore.discardStagedRecoveryKey();
1221
+ return {
1222
+ success,
1223
+ error: success ? void 0 : backupError ?? verificationError ?? void 0,
1224
+ verification: success ? await this.getOwnDeviceVerificationStatus() : verification,
1225
+ crossSigning,
1226
+ pendingVerifications: await pendingVerifications(),
1227
+ cryptoBootstrap: bootstrapSummary
1228
+ };
1229
+ }
1230
+ async listOwnDevices() {
1231
+ const currentDeviceId = this.client.getDeviceId()?.trim() || null;
1232
+ const devices = await this.client.getDevices();
1233
+ return (Array.isArray(devices?.devices) ? devices.devices : []).map((device) => ({
1234
+ deviceId: device.device_id,
1235
+ displayName: device.display_name?.trim() || null,
1236
+ lastSeenIp: device.last_seen_ip?.trim() || null,
1237
+ lastSeenTs: typeof device.last_seen_ts === "number" && Number.isFinite(device.last_seen_ts) ? device.last_seen_ts : null,
1238
+ current: currentDeviceId !== null && device.device_id === currentDeviceId
1239
+ }));
1240
+ }
1241
+ async deleteOwnDevices(deviceIds) {
1242
+ const uniqueDeviceIds = [...new Set(deviceIds.map((value) => value.trim()).filter(Boolean))];
1243
+ const currentDeviceId = this.client.getDeviceId()?.trim() || null;
1244
+ const protectedDeviceIds = uniqueDeviceIds.filter((deviceId) => deviceId === currentDeviceId);
1245
+ if (protectedDeviceIds.length > 0) throw new Error(`Refusing to delete the current Matrix device: ${protectedDeviceIds[0]}`);
1246
+ const deleteWithAuth = async (authData) => {
1247
+ await this.client.deleteMultipleDevices(uniqueDeviceIds, authData);
1248
+ };
1249
+ if (uniqueDeviceIds.length > 0) try {
1250
+ await deleteWithAuth();
1251
+ } catch (err) {
1252
+ const session = err && typeof err === "object" && "data" in err && err.data && typeof err.data === "object" && "session" in err.data && typeof err.data.session === "string" ? err.data.session : null;
1253
+ const userId = await this.getUserId().catch(() => this.selfUserId);
1254
+ if (!session || !userId || !this.password?.trim()) throw err;
1255
+ await deleteWithAuth({
1256
+ type: "m.login.password",
1257
+ session,
1258
+ identifier: {
1259
+ type: "m.id.user",
1260
+ user: userId
1261
+ },
1262
+ password: this.password
1263
+ });
1264
+ }
1265
+ return {
1266
+ currentDeviceId,
1267
+ deletedDeviceIds: uniqueDeviceIds,
1268
+ remainingDevices: await this.listOwnDevices()
1269
+ };
1270
+ }
1271
+ async resolveActiveRoomKeyBackupVersion(crypto) {
1272
+ if (typeof crypto.getActiveSessionBackupVersion !== "function") return null;
1273
+ return normalizeOptionalString$1(await crypto.getActiveSessionBackupVersion().catch(() => null));
1274
+ }
1275
+ async resolveCachedRoomKeyBackupDecryptionKey(crypto) {
1276
+ const getSessionBackupPrivateKey = crypto.getSessionBackupPrivateKey;
1277
+ if (typeof getSessionBackupPrivateKey !== "function") return null;
1278
+ const key = await getSessionBackupPrivateKey.call(crypto).catch(() => null);
1279
+ return key ? key.length > 0 : false;
1280
+ }
1281
+ async resolveRoomKeyBackupLocalState(crypto) {
1282
+ const [activeVersion, decryptionKeyCached] = await Promise.all([this.resolveActiveRoomKeyBackupVersion(crypto), this.resolveCachedRoomKeyBackupDecryptionKey(crypto)]);
1283
+ return {
1284
+ activeVersion,
1285
+ decryptionKeyCached
1286
+ };
1287
+ }
1288
+ async shouldForceSecretStorageRecreationForBackupReset(crypto) {
1289
+ if (await this.resolveCachedRoomKeyBackupDecryptionKey(crypto) !== false) return false;
1290
+ const loadSessionBackupPrivateKeyFromSecretStorage = crypto.loadSessionBackupPrivateKeyFromSecretStorage;
1291
+ if (typeof loadSessionBackupPrivateKeyFromSecretStorage !== "function") return false;
1292
+ try {
1293
+ await loadSessionBackupPrivateKeyFromSecretStorage.call(crypto);
1294
+ return false;
1295
+ } catch (err) {
1296
+ return isRepairableSecretStorageAccessError(err);
1297
+ }
1298
+ }
1299
+ async resolveRoomKeyBackupTrustState(crypto, fallbackVersion) {
1300
+ let serverVersion = fallbackVersion;
1301
+ let trusted = null;
1302
+ let matchesDecryptionKey = null;
1303
+ if (typeof crypto.getKeyBackupInfo === "function") {
1304
+ const info = await crypto.getKeyBackupInfo().catch(() => null);
1305
+ serverVersion = normalizeOptionalString$1(info?.version) ?? serverVersion;
1306
+ if (info && typeof crypto.isKeyBackupTrusted === "function") {
1307
+ const trustInfo = await crypto.isKeyBackupTrusted(info).catch(() => null);
1308
+ trusted = typeof trustInfo?.trusted === "boolean" ? trustInfo.trusted : null;
1309
+ matchesDecryptionKey = typeof trustInfo?.matchesDecryptionKey === "boolean" ? trustInfo.matchesDecryptionKey : null;
1310
+ }
1311
+ }
1312
+ return {
1313
+ serverVersion,
1314
+ trusted,
1315
+ matchesDecryptionKey
1316
+ };
1317
+ }
1318
+ async resolveDefaultSecretStorageKeyId(crypto) {
1319
+ const getSecretStorageStatus = crypto?.getSecretStorageStatus;
1320
+ if (typeof getSecretStorageStatus !== "function") return;
1321
+ return (await getSecretStorageStatus.call(crypto).catch(() => null))?.defaultKeyId;
1322
+ }
1323
+ async resolveRoomKeyBackupVersion() {
1324
+ try {
1325
+ return normalizeOptionalString$1((await this.doRequest("GET", "/_matrix/client/v3/room_keys/version")).version);
1326
+ } catch {
1327
+ return null;
1328
+ }
1329
+ }
1330
+ async enableTrustedRoomKeyBackupIfPossible(crypto) {
1331
+ if (typeof crypto.checkKeyBackupAndEnable !== "function") return;
1332
+ await crypto.checkKeyBackupAndEnable();
1333
+ }
1334
+ async ensureRoomKeyBackupEnabled(crypto) {
1335
+ if (await this.resolveRoomKeyBackupVersion()) return;
1336
+ LogService.info("MatrixClientLite", "No room key backup version found on server, creating one via secret storage bootstrap");
1337
+ await this.recoveryKeyStore.bootstrapSecretStorageWithRecoveryKey(crypto, { setupNewKeyBackup: true });
1338
+ const createdVersion = await this.resolveRoomKeyBackupVersion();
1339
+ if (!createdVersion) throw new Error("Matrix room key backup is still missing after bootstrap");
1340
+ LogService.info("MatrixClientLite", `Room key backup enabled (version ${createdVersion})`);
1341
+ }
1342
+ registerBridge() {
1343
+ if (this.bridgeRegistered || !this.decryptBridge) return;
1344
+ this.bridgeRegistered = true;
1345
+ const decryptBridge = this.decryptBridge;
1346
+ this.client.on(ClientEvent.Event, (event) => {
1347
+ const roomId = event.getRoomId();
1348
+ if (!roomId) return;
1349
+ const raw = matrixEventToRaw(event, { contentMode: "original" });
1350
+ const isEncryptedEvent = raw.type === "m.room.encrypted";
1351
+ this.emitter.emit("room.event", roomId, raw);
1352
+ if (isEncryptedEvent) this.emitter.emit("room.encrypted_event", roomId, raw);
1353
+ else if (decryptBridge.shouldEmitUnencryptedMessage(roomId, raw.event_id)) this.emitter.emit("room.message", roomId, raw);
1354
+ const stateKey = raw.state_key ?? "";
1355
+ const selfUserId = this.client.getUserId() ?? this.selfUserId ?? "";
1356
+ const membership = raw.type === "m.room.member" ? raw.content.membership : void 0;
1357
+ if (stateKey && selfUserId && stateKey === selfUserId) {
1358
+ if (membership === "invite") this.emitter.emit("room.invite", roomId, raw);
1359
+ else if (membership === "join") this.emitter.emit("room.join", roomId, raw);
1360
+ }
1361
+ if (isEncryptedEvent) decryptBridge.attachEncryptedEvent(event, roomId);
1362
+ });
1363
+ this.client.on(ClientEvent.Room, (room) => {
1364
+ this.emitMembershipForRoom(room);
1365
+ });
1366
+ this.client.on(ClientEvent.Sync, (state, prevState, data) => {
1367
+ this.currentSyncState = state;
1368
+ const error = data && typeof data === "object" && "error" in data ? data.error : void 0;
1369
+ this.emitter.emit("sync.state", state, prevState, error);
1370
+ });
1371
+ this.client.on(ClientEvent.SyncUnexpectedError, (error) => {
1372
+ this.emitter.emit("sync.unexpected_error", error);
1373
+ });
1374
+ }
1375
+ emitMembershipForRoom(room) {
1376
+ const roomObj = room;
1377
+ const roomId = roomObj.roomId?.trim();
1378
+ if (!roomId) return;
1379
+ const membership = roomObj.getMyMembership?.() ?? roomObj.selfMembership ?? void 0;
1380
+ const selfUserId = this.client.getUserId() ?? this.selfUserId ?? "";
1381
+ if (!selfUserId) return;
1382
+ const raw = {
1383
+ event_id: `$membership-${roomId}-${Date.now()}`,
1384
+ type: "m.room.member",
1385
+ sender: selfUserId,
1386
+ state_key: selfUserId,
1387
+ content: { membership },
1388
+ origin_server_ts: Date.now(),
1389
+ unsigned: { age: 0 }
1390
+ };
1391
+ if (membership === "invite") {
1392
+ this.emitter.emit("room.invite", roomId, raw);
1393
+ return;
1394
+ }
1395
+ if (membership === "join") this.emitter.emit("room.join", roomId, raw);
1396
+ }
1397
+ emitOutstandingInviteEvents() {
1398
+ const listRooms = this.client.getRooms;
1399
+ if (typeof listRooms !== "function") return;
1400
+ const rooms = listRooms.call(this.client);
1401
+ if (!Array.isArray(rooms)) return;
1402
+ for (const room of rooms) this.emitMembershipForRoom(room);
1403
+ }
1404
+ async refreshDmCache() {
1405
+ const direct = await this.getAccountData("m.direct");
1406
+ this.dmRoomIds.clear();
1407
+ if (!direct || typeof direct !== "object") return false;
1408
+ for (const value of Object.values(direct)) {
1409
+ if (!Array.isArray(value)) continue;
1410
+ for (const roomId of value) if (typeof roomId === "string" && roomId.trim()) this.dmRoomIds.add(roomId);
1411
+ }
1412
+ return true;
1413
+ }
1414
+ };
1415
+ //#endregion
1416
+ export { sdk_exports as n, MatrixClient as t };