@kodelyth/matrix 2026.5.39 → 2026.6.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 (117) hide show
  1. package/dist/account-selection-Y50DNJ2l.js +158 -0
  2. package/dist/active-client-CmFdvPdO.js +20 -0
  3. package/dist/api.js +12 -0
  4. package/dist/approval-handler.runtime-BIi4fL0R.js +377 -0
  5. package/dist/approval-ids-BGHK7PnZ.js +7 -0
  6. package/dist/approval-reaction-auth-CL0-nCNV.js +27 -0
  7. package/dist/approval-reactions-nDm2x-K5.js +162 -0
  8. package/dist/async-lock-SsmtFXtt.js +19 -0
  9. package/dist/auth-presence.js +26 -0
  10. package/dist/backup-health-3BHbHxyd.js +60 -0
  11. package/dist/channel-C0kCyTNB.js +1380 -0
  12. package/dist/channel-plugin-api.js +2 -0
  13. package/dist/channel.runtime-CdrdEN-0.js +250 -0
  14. package/dist/cli-FtY6Nuzw.js +1338 -0
  15. package/dist/cli-metadata-Dkwua7CB.js +22 -0
  16. package/dist/cli-metadata.js +2 -0
  17. package/dist/client-BnohYygh.js +25 -0
  18. package/dist/client-PhrTwuC4.js +30 -0
  19. package/dist/client-bootstrap-Mcj8ChJ5.js +114 -0
  20. package/dist/config-paths-DVvt6vM3.js +114 -0
  21. package/dist/config-schema-BMGOlhdI.js +308 -0
  22. package/dist/config-secret-input.runtime-Dv_4Br_f.js +2 -0
  23. package/dist/contract-api.js +8 -0
  24. package/dist/create-client-J0htTaRj.js +64 -0
  25. package/dist/credentials-B7GsBbgQ.js +56 -0
  26. package/dist/credentials-read-8fE4qoWs.js +112 -0
  27. package/dist/credentials-write.runtime-BibplB4Y.js +17 -0
  28. package/dist/crypto-node.runtime-D9qxgRPa.js +12 -0
  29. package/dist/crypto-runtime-1pKW4O2F.js +1214 -0
  30. package/dist/deps-DVpDS81G.js +208 -0
  31. package/dist/device-health-Ct2wDSPG.js +16 -0
  32. package/dist/directory-live-i3T8uORc.js +150 -0
  33. package/dist/doctor-contract-BLzYHl_9.js +246 -0
  34. package/dist/doctor-contract-api.js +2 -0
  35. package/dist/doctor-diR5gE7D.js +153 -0
  36. package/dist/draft-stream-HpPJ_VJt.js +143 -0
  37. package/dist/encryption-guidance-BNEgckrZ.js +15 -0
  38. package/dist/env-auth-UFiTGkDM.js +63 -0
  39. package/dist/env-vars-EQKQv-FE.js +63 -0
  40. package/dist/errors-BETj3zr9.js +17 -0
  41. package/dist/exec-approval-resolver-BxPorU_t.js +15 -0
  42. package/dist/helper-api.js +4 -0
  43. package/dist/http-client-DoQgbQsU.js +331 -0
  44. package/dist/index.js +46 -0
  45. package/dist/legacy-crypto-inspector-zK0hDCbt.js +41 -0
  46. package/dist/legacy-crypto-restore-DSFIXuDo.js +85 -0
  47. package/dist/logging-Df7aPD1z.js +99 -0
  48. package/dist/matrix-migration.runtime-BNoT1Prt.js +525 -0
  49. package/dist/media-text-ZhGA8Pcs.js +146 -0
  50. package/dist/messages-CRA9WGg0.js +140 -0
  51. package/dist/migration-snapshot-backup-BR-xD7Ew.js +69 -0
  52. package/dist/migration-snapshot.runtime-BLcy_Nvw.js +2 -0
  53. package/dist/monitor-DQm7_13y.js +4331 -0
  54. package/dist/plugin-entry.handlers.runtime.js +51 -0
  55. package/dist/probe.runtime-CjJS53Kz.js +3 -0
  56. package/dist/profile-update-DqkPgZ1P.js +68 -0
  57. package/dist/reaction-common-CmVLzP-u.js +71 -0
  58. package/dist/reaction-events-D0nUJuZV.js +121 -0
  59. package/dist/record-shared-DGvSFn5M.js +2 -0
  60. package/dist/resolve-targets-ChECUzD2.js +140 -0
  61. package/dist/resolver.runtime-hdY3n0GO.js +5 -0
  62. package/dist/rolldown-runtime-DUslC3ob.js +14 -0
  63. package/dist/route-xRKj_ESW.js +161 -0
  64. package/dist/runtime-B-Fyrmxo.js +8 -0
  65. package/dist/runtime-api-BYXXkxq2.js +24 -0
  66. package/dist/runtime-api.js +25 -0
  67. package/dist/runtime-heavy-api.js +3 -0
  68. package/dist/runtime-lwTSy9Yt.js +6 -0
  69. package/dist/runtime-setter-api.js +2 -0
  70. package/dist/sdk-Jhq7mLtD.js +1704 -0
  71. package/dist/secret-contract-DEMcDsjl.js +120 -0
  72. package/dist/secret-contract-api.js +2 -0
  73. package/dist/send-CJunc6QM.js +1517 -0
  74. package/dist/setup-bootstrap-rJ0qZWPe.js +62 -0
  75. package/dist/setup-core-BEYoXF3J.js +677 -0
  76. package/dist/setup-entry.js +19 -0
  77. package/dist/setup-plugin-api.js +43 -0
  78. package/dist/setup-surface-c28ON6jq.js +537 -0
  79. package/dist/shared-D6MFMnpG.js +642 -0
  80. package/dist/startup-abort-B2J3MU_h.js +109 -0
  81. package/dist/startup-verification-CkD4Cwce.js +132 -0
  82. package/dist/storage-nyO0DOFE.js +281 -0
  83. package/dist/storage-paths-BTAketfg.js +52 -0
  84. package/dist/subagent-hooks-api-Dr_xnMRG.js +170 -0
  85. package/dist/subagent-hooks-api.js +2 -0
  86. package/dist/sync-state-Bx0gPaGA.js +12 -0
  87. package/dist/target-ids-Bsazo8si.js +77 -0
  88. package/dist/test-api.js +4 -0
  89. package/dist/thread-binding-api-IGU0-L70.js +17 -0
  90. package/dist/thread-binding-api.js +2 -0
  91. package/dist/thread-bindings-FjAZmDUP.js +352 -0
  92. package/dist/thread-bindings-runtime.js +2 -0
  93. package/dist/thread-bindings-shared-fvfP7jVs.js +97 -0
  94. package/dist/timeout-abort-signal-DpSHDHhR.js +2 -0
  95. package/dist/tool-actions.runtime-Cbo7YcYZ.js +532 -0
  96. package/dist/url-validation-DlrXNjAE.js +36 -0
  97. package/dist/verification-7tDPRpJU.js +345 -0
  98. package/package.json +19 -7
  99. package/api.js +0 -7
  100. package/auth-presence.js +0 -7
  101. package/channel-plugin-api.js +0 -7
  102. package/cli-metadata.js +0 -7
  103. package/contract-api.js +0 -7
  104. package/doctor-contract-api.js +0 -7
  105. package/helper-api.js +0 -7
  106. package/index.js +0 -7
  107. package/plugin-entry.handlers.runtime.js +0 -7
  108. package/runtime-api.js +0 -7
  109. package/runtime-heavy-api.js +0 -7
  110. package/runtime-setter-api.js +0 -7
  111. package/secret-contract-api.js +0 -7
  112. package/setup-entry.js +0 -7
  113. package/setup-plugin-api.js +0 -7
  114. package/subagent-hooks-api.js +0 -7
  115. package/test-api.js +0 -7
  116. package/thread-binding-api.js +0 -7
  117. package/thread-bindings-runtime.js +0 -7
@@ -0,0 +1,1704 @@
1
+ import { t as __exportAll } from "./rolldown-runtime-DUslC3ob.js";
2
+ import { t as isRecord } from "./record-shared-DGvSFn5M.js";
3
+ import { n as formatMatrixErrorReason, r as isMatrixNotFoundError, t as formatMatrixErrorMessage } from "./errors-BETj3zr9.js";
4
+ import { t as claimCurrentTokenStorageState } from "./storage-nyO0DOFE.js";
5
+ import { n as resolveMatrixRoomKeyBackupReadinessError } from "./backup-health-3BHbHxyd.js";
6
+ import { t as createAsyncLock } from "./async-lock-SsmtFXtt.js";
7
+ import { a as ConsoleLogger, i as throwIfMatrixStartupAborted, n as createMatrixStartupAbortError, o as LogService, s as noop } from "./startup-abort-B2J3MU_h.js";
8
+ import { t as createMatrixJsSdkClientLogger } from "./logging-Df7aPD1z.js";
9
+ import { a as matrixEventToRaw, n as createMatrixGuardedFetch, o as parseMxc, t as MatrixAuthedHttpClient } from "./http-client-DoQgbQsU.js";
10
+ import { n as isMatrixReadySyncState, r as isMatrixTerminalSyncState } from "./sync-state-Bx0gPaGA.js";
11
+ import { normalizeNullableString } from "klaw/plugin-sdk/string-coerce-runtime";
12
+ import { readFileSync } from "node:fs";
13
+ import path from "node:path";
14
+ import { loadJsonFile, saveJsonFile, writeJsonFileAtomically } from "klaw/plugin-sdk/json-store";
15
+ import { KeyedAsyncQueue } from "klaw/plugin-sdk/keyed-async-queue";
16
+ import { EventEmitter } from "node:events";
17
+ import { Category, ClientEvent, Filter, MatrixEventEvent, MemoryStore, Preset, SyncAccumulator, createClient } from "matrix-js-sdk/lib/matrix.js";
18
+ import { VerificationMethod } from "matrix-js-sdk/lib/types.js";
19
+ import fs$1 from "node:fs/promises";
20
+ import { decodeRecoveryKey } from "matrix-js-sdk/lib/crypto-api/recovery-key.js";
21
+ //#region extensions/matrix/src/matrix/client/file-sync-store.ts
22
+ const STORE_VERSION = 1;
23
+ const PERSIST_DEBOUNCE_MS = 250;
24
+ function normalizeRoomsData(value) {
25
+ if (!isRecord(value)) return null;
26
+ return {
27
+ [Category.Join]: isRecord(value[Category.Join]) ? value[Category.Join] : {},
28
+ [Category.Invite]: isRecord(value[Category.Invite]) ? value[Category.Invite] : {},
29
+ [Category.Leave]: isRecord(value[Category.Leave]) ? value[Category.Leave] : {},
30
+ [Category.Knock]: isRecord(value[Category.Knock]) ? value[Category.Knock] : {}
31
+ };
32
+ }
33
+ function toPersistedSyncData(value) {
34
+ if (!isRecord(value)) return null;
35
+ if (typeof value.nextBatch === "string" && value.nextBatch.trim()) {
36
+ const roomsData = normalizeRoomsData(value.roomsData);
37
+ if (!Array.isArray(value.accountData) || !roomsData) return null;
38
+ return {
39
+ nextBatch: value.nextBatch,
40
+ accountData: value.accountData,
41
+ roomsData
42
+ };
43
+ }
44
+ if (typeof value.next_batch === "string" && value.next_batch.trim()) {
45
+ const roomsData = normalizeRoomsData(value.rooms);
46
+ if (!roomsData) return null;
47
+ return {
48
+ nextBatch: value.next_batch,
49
+ accountData: isRecord(value.account_data) && Array.isArray(value.account_data.events) ? value.account_data.events : [],
50
+ roomsData
51
+ };
52
+ }
53
+ return null;
54
+ }
55
+ function readPersistedStore(raw) {
56
+ try {
57
+ const parsed = JSON.parse(raw);
58
+ const savedSync = toPersistedSyncData(parsed.savedSync);
59
+ if (parsed.version === STORE_VERSION) return {
60
+ version: STORE_VERSION,
61
+ savedSync,
62
+ clientOptions: isRecord(parsed.clientOptions) ? parsed.clientOptions : void 0,
63
+ cleanShutdown: parsed.cleanShutdown === true
64
+ };
65
+ return {
66
+ version: STORE_VERSION,
67
+ savedSync: toPersistedSyncData(parsed),
68
+ cleanShutdown: false
69
+ };
70
+ } catch {
71
+ return null;
72
+ }
73
+ }
74
+ function cloneJson(value) {
75
+ return structuredClone(value);
76
+ }
77
+ function syncDataToSyncResponse(syncData) {
78
+ return {
79
+ next_batch: syncData.nextBatch,
80
+ rooms: syncData.roomsData,
81
+ account_data: { events: syncData.accountData }
82
+ };
83
+ }
84
+ var FileBackedMatrixSyncStore = class extends MemoryStore {
85
+ constructor(storagePath) {
86
+ super();
87
+ this.storagePath = storagePath;
88
+ this.persistLock = createAsyncLock();
89
+ this.accumulator = new SyncAccumulator();
90
+ this.savedSync = null;
91
+ this.cleanShutdown = false;
92
+ this.dirty = false;
93
+ this.persistTimer = null;
94
+ this.persistPromise = null;
95
+ let restoredSavedSync = null;
96
+ let restoredClientOptions;
97
+ let restoredCleanShutdown = false;
98
+ try {
99
+ const persisted = readPersistedStore(readFileSync(this.storagePath, "utf8"));
100
+ restoredSavedSync = persisted?.savedSync ?? null;
101
+ restoredClientOptions = persisted?.clientOptions;
102
+ restoredCleanShutdown = persisted?.cleanShutdown === true;
103
+ } catch {}
104
+ this.savedSync = restoredSavedSync;
105
+ this.savedClientOptions = restoredClientOptions;
106
+ this.hadSavedSyncOnLoad = restoredSavedSync !== null;
107
+ this.hadCleanShutdownOnLoad = this.hadSavedSyncOnLoad && restoredCleanShutdown;
108
+ this.cleanShutdown = this.hadCleanShutdownOnLoad;
109
+ if (this.savedSync) {
110
+ this.accumulator.accumulate(syncDataToSyncResponse(this.savedSync), true);
111
+ super.setSyncToken(this.savedSync.nextBatch);
112
+ }
113
+ if (this.savedClientOptions) super.storeClientOptions(this.savedClientOptions);
114
+ }
115
+ hasSavedSync() {
116
+ return this.hadSavedSyncOnLoad;
117
+ }
118
+ hasSavedSyncFromCleanShutdown() {
119
+ return this.hadCleanShutdownOnLoad;
120
+ }
121
+ getSavedSync() {
122
+ return Promise.resolve(this.savedSync ? cloneJson(this.savedSync) : null);
123
+ }
124
+ getSavedSyncToken() {
125
+ return Promise.resolve(this.savedSync?.nextBatch ?? null);
126
+ }
127
+ setSyncData(syncData) {
128
+ this.accumulator.accumulate(syncData);
129
+ this.savedSync = this.accumulator.getJSON();
130
+ this.markDirtyAndSchedulePersist();
131
+ return Promise.resolve();
132
+ }
133
+ getClientOptions() {
134
+ return Promise.resolve(this.savedClientOptions ? cloneJson(this.savedClientOptions) : void 0);
135
+ }
136
+ storeClientOptions(options) {
137
+ this.savedClientOptions = cloneJson(options);
138
+ super.storeClientOptions(options);
139
+ this.markDirtyAndSchedulePersist();
140
+ return Promise.resolve();
141
+ }
142
+ save(force = false) {
143
+ if (force) return this.flush();
144
+ return Promise.resolve();
145
+ }
146
+ wantsSave() {
147
+ return false;
148
+ }
149
+ async deleteAllData() {
150
+ if (this.persistTimer) {
151
+ clearTimeout(this.persistTimer);
152
+ this.persistTimer = null;
153
+ }
154
+ this.dirty = false;
155
+ await this.persistPromise?.catch(() => void 0);
156
+ await super.deleteAllData();
157
+ this.savedSync = null;
158
+ this.savedClientOptions = void 0;
159
+ this.cleanShutdown = false;
160
+ await fs$1.rm(this.storagePath, { force: true }).catch(() => void 0);
161
+ }
162
+ markCleanShutdown() {
163
+ this.cleanShutdown = true;
164
+ this.dirty = true;
165
+ }
166
+ async flush() {
167
+ if (this.persistTimer) {
168
+ clearTimeout(this.persistTimer);
169
+ this.persistTimer = null;
170
+ }
171
+ while (this.dirty || this.persistPromise) {
172
+ if (this.dirty && !this.persistPromise) this.persistPromise = this.persist().finally(() => {
173
+ this.persistPromise = null;
174
+ });
175
+ await this.persistPromise;
176
+ }
177
+ }
178
+ markDirtyAndSchedulePersist() {
179
+ this.cleanShutdown = false;
180
+ this.dirty = true;
181
+ if (this.persistTimer) return;
182
+ this.persistTimer = setTimeout(() => {
183
+ this.persistTimer = null;
184
+ this.flush().catch((err) => {
185
+ LogService.warn("MatrixFileSyncStore", "Failed to persist Matrix sync store:", err);
186
+ });
187
+ }, PERSIST_DEBOUNCE_MS);
188
+ this.persistTimer.unref?.();
189
+ }
190
+ async persist() {
191
+ this.dirty = false;
192
+ const payload = {
193
+ version: STORE_VERSION,
194
+ savedSync: this.savedSync ? cloneJson(this.savedSync) : null,
195
+ cleanShutdown: this.cleanShutdown,
196
+ ...this.savedClientOptions ? { clientOptions: cloneJson(this.savedClientOptions) } : {}
197
+ };
198
+ try {
199
+ await this.persistLock(async () => {
200
+ await writeJsonFileAtomically(this.storagePath, payload);
201
+ claimCurrentTokenStorageState({ rootDir: path.dirname(this.storagePath) });
202
+ });
203
+ } catch (err) {
204
+ this.dirty = true;
205
+ throw err;
206
+ }
207
+ }
208
+ };
209
+ //#endregion
210
+ //#region extensions/matrix/src/matrix/sdk/idb-persistence-lock.ts
211
+ const MATRIX_IDB_PERSIST_INTERVAL_MS = 6e4;
212
+ const IDB_SNAPSHOT_LOCK_STALE_MS = 5 * 6e4;
213
+ const IDB_SNAPSHOT_LOCK_RETRY_BASE = {
214
+ factor: 2,
215
+ minTimeout: 50,
216
+ maxTimeout: 5e3,
217
+ randomize: true
218
+ };
219
+ function computeRetryDelayMs(retries, attempt) {
220
+ return Math.min(retries.maxTimeout, Math.max(retries.minTimeout, retries.minTimeout * retries.factor ** attempt));
221
+ }
222
+ function computeMinimumRetryWindowMs(retries) {
223
+ let total = 0;
224
+ const attempts = Math.max(1, retries.retries + 1);
225
+ for (let attempt = 0; attempt < attempts - 1; attempt += 1) total += computeRetryDelayMs(retries, attempt);
226
+ return total;
227
+ }
228
+ function resolveRetriesForMinimumWindowMs(retries, minimumWindowMs) {
229
+ const resolved = {
230
+ ...retries,
231
+ retries: 0
232
+ };
233
+ while (computeMinimumRetryWindowMs(resolved) < minimumWindowMs) resolved.retries += 1;
234
+ return resolved;
235
+ }
236
+ const MATRIX_IDB_SNAPSHOT_LOCK_OPTIONS = {
237
+ retries: resolveRetriesForMinimumWindowMs(IDB_SNAPSHOT_LOCK_RETRY_BASE, MATRIX_IDB_PERSIST_INTERVAL_MS),
238
+ stale: IDB_SNAPSHOT_LOCK_STALE_MS
239
+ };
240
+ //#endregion
241
+ //#region extensions/matrix/src/matrix/sdk/recovery-key-store.ts
242
+ function isRepairableSecretStorageAccessError(err) {
243
+ const message = formatMatrixErrorReason(err);
244
+ if (!message) return false;
245
+ if (message.includes("getsecretstoragekey callback returned falsey")) return true;
246
+ if (message.includes("decrypting secret") && message.includes("bad mac")) return true;
247
+ return false;
248
+ }
249
+ var MatrixRecoveryKeyStore = class {
250
+ constructor(recoveryKeyPath) {
251
+ this.recoveryKeyPath = recoveryKeyPath;
252
+ this.secretStorageKeyCache = /* @__PURE__ */ new Map();
253
+ this.stagedRecoveryKey = null;
254
+ this.stagedRecoveryKeyUsed = false;
255
+ this.stagedCacheKeyIds = /* @__PURE__ */ new Set();
256
+ }
257
+ buildCryptoCallbacks() {
258
+ return {
259
+ getSecretStorageKey: async ({ keys }) => {
260
+ const requestedKeyIds = Object.keys(keys ?? {});
261
+ if (requestedKeyIds.length === 0) return null;
262
+ const staged = this.resolveStagedSecretStorageKey(requestedKeyIds);
263
+ if (staged) return staged;
264
+ for (const keyId of requestedKeyIds) {
265
+ const cached = this.secretStorageKeyCache.get(keyId);
266
+ if (cached) return [keyId, new Uint8Array(cached.key)];
267
+ }
268
+ const stored = this.loadStoredRecoveryKey();
269
+ if (!stored?.privateKeyBase64) return null;
270
+ const privateKey = new Uint8Array(Buffer.from(stored.privateKeyBase64, "base64"));
271
+ if (privateKey.length === 0) return null;
272
+ if (stored.keyId && requestedKeyIds.includes(stored.keyId)) {
273
+ this.rememberSecretStorageKey(stored.keyId, privateKey, stored.keyInfo);
274
+ return [stored.keyId, privateKey];
275
+ }
276
+ const firstRequestedKeyId = requestedKeyIds[0];
277
+ if (!firstRequestedKeyId) return null;
278
+ this.rememberSecretStorageKey(firstRequestedKeyId, privateKey, stored.keyInfo);
279
+ return [firstRequestedKeyId, privateKey];
280
+ },
281
+ cacheSecretStorageKey: (keyId, keyInfo, key) => {
282
+ const privateKey = new Uint8Array(key);
283
+ const normalizedKeyInfo = {
284
+ passphrase: keyInfo?.passphrase,
285
+ name: typeof keyInfo?.name === "string" ? keyInfo.name : void 0
286
+ };
287
+ this.rememberSecretStorageKey(keyId, privateKey, normalizedKeyInfo);
288
+ const stored = this.loadStoredRecoveryKey();
289
+ this.saveRecoveryKeyToDisk({
290
+ keyId,
291
+ keyInfo: normalizedKeyInfo,
292
+ privateKey,
293
+ encodedPrivateKey: stored?.encodedPrivateKey
294
+ });
295
+ }
296
+ };
297
+ }
298
+ getRecoveryKeySummary() {
299
+ const stored = this.loadStoredRecoveryKey();
300
+ if (!stored) return null;
301
+ return {
302
+ encodedPrivateKey: stored.encodedPrivateKey,
303
+ keyId: stored.keyId,
304
+ createdAt: stored.createdAt
305
+ };
306
+ }
307
+ resolveEncodedRecoveryKeyInput(params) {
308
+ const encodedPrivateKey = params.encodedPrivateKey.trim();
309
+ if (!encodedPrivateKey) throw new Error("Matrix recovery key is required");
310
+ let privateKey;
311
+ try {
312
+ privateKey = decodeRecoveryKey(encodedPrivateKey);
313
+ } catch (err) {
314
+ throw new Error(`Invalid Matrix recovery key: ${formatMatrixErrorMessage(err)}`, { cause: err });
315
+ }
316
+ const keyId = typeof params.keyId === "string" && params.keyId.trim() ? params.keyId.trim() : null;
317
+ return {
318
+ encodedPrivateKey,
319
+ privateKey,
320
+ keyId,
321
+ keyInfo: params.keyInfo ?? this.loadStoredRecoveryKey()?.keyInfo
322
+ };
323
+ }
324
+ storeEncodedRecoveryKey(params) {
325
+ const prepared = this.resolveEncodedRecoveryKeyInput(params);
326
+ this.saveRecoveryKeyToDisk({
327
+ keyId: prepared.keyId,
328
+ keyInfo: prepared.keyInfo,
329
+ privateKey: prepared.privateKey,
330
+ encodedPrivateKey: prepared.encodedPrivateKey
331
+ });
332
+ if (prepared.keyId) this.rememberSecretStorageKey(prepared.keyId, prepared.privateKey, prepared.keyInfo);
333
+ return this.getRecoveryKeySummary() ?? {};
334
+ }
335
+ stageEncodedRecoveryKey(params) {
336
+ const prepared = this.resolveEncodedRecoveryKeyInput(params);
337
+ this.discardStagedRecoveryKey();
338
+ this.stagedRecoveryKey = {
339
+ version: 1,
340
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
341
+ keyId: prepared.keyId,
342
+ encodedPrivateKey: prepared.encodedPrivateKey,
343
+ privateKeyBase64: Buffer.from(prepared.privateKey).toString("base64"),
344
+ keyInfo: prepared.keyInfo
345
+ };
346
+ }
347
+ hasStagedRecoveryKeyBeenUsed() {
348
+ return this.stagedRecoveryKeyUsed;
349
+ }
350
+ commitStagedRecoveryKey(params) {
351
+ if (!this.stagedRecoveryKey) return this.getRecoveryKeySummary();
352
+ const staged = this.stagedRecoveryKey;
353
+ const privateKey = new Uint8Array(Buffer.from(staged.privateKeyBase64, "base64"));
354
+ const keyId = typeof params?.keyId === "string" && params.keyId.trim() ? params.keyId.trim() : staged.keyId;
355
+ this.saveRecoveryKeyToDisk({
356
+ keyId,
357
+ keyInfo: params?.keyInfo ?? staged.keyInfo,
358
+ privateKey,
359
+ encodedPrivateKey: staged.encodedPrivateKey
360
+ });
361
+ this.clearStagedRecoveryKeyTracking();
362
+ return this.getRecoveryKeySummary();
363
+ }
364
+ discardStagedRecoveryKey() {
365
+ for (const keyId of this.stagedCacheKeyIds) this.secretStorageKeyCache.delete(keyId);
366
+ this.clearStagedRecoveryKeyTracking();
367
+ }
368
+ async bootstrapSecretStorageWithRecoveryKey(crypto, options = {}) {
369
+ let status = null;
370
+ const getSecretStorageStatus = crypto.getSecretStorageStatus;
371
+ if (typeof getSecretStorageStatus === "function") try {
372
+ status = await getSecretStorageStatus.call(crypto);
373
+ } catch (err) {
374
+ LogService.warn("MatrixClientLite", "Failed to read secret storage status:", err);
375
+ }
376
+ const hasDefaultSecretStorageKey = Boolean(status?.defaultKeyId);
377
+ const hasKnownInvalidSecrets = Object.values(status?.secretStorageKeyValidityMap ?? {}).some((valid) => !valid);
378
+ let generatedRecoveryKey = false;
379
+ const storedRecovery = this.loadStoredRecoveryKey();
380
+ const stagedRecovery = this.stagedRecoveryKey;
381
+ const sourceRecovery = options.forceNewRecoveryKey === true ? null : stagedRecovery ?? storedRecovery;
382
+ let recoveryKey = sourceRecovery ? {
383
+ keyInfo: sourceRecovery.keyInfo,
384
+ privateKey: new Uint8Array(Buffer.from(sourceRecovery.privateKeyBase64, "base64")),
385
+ encodedPrivateKey: sourceRecovery.encodedPrivateKey
386
+ } : null;
387
+ if (recoveryKey && status?.defaultKeyId) {
388
+ const defaultKeyId = status.defaultKeyId;
389
+ if (!stagedRecovery) {
390
+ this.rememberSecretStorageKey(defaultKeyId, recoveryKey.privateKey, recoveryKey.keyInfo);
391
+ if (storedRecovery && storedRecovery.keyId !== defaultKeyId) this.saveRecoveryKeyToDisk({
392
+ keyId: defaultKeyId,
393
+ keyInfo: recoveryKey.keyInfo,
394
+ privateKey: recoveryKey.privateKey,
395
+ encodedPrivateKey: recoveryKey.encodedPrivateKey
396
+ });
397
+ }
398
+ }
399
+ const ensureRecoveryKey = async () => {
400
+ if (recoveryKey) {
401
+ if (stagedRecovery) this.stagedRecoveryKeyUsed = true;
402
+ return recoveryKey;
403
+ }
404
+ if (typeof crypto.createRecoveryKeyFromPassphrase !== "function") throw new Error("Matrix crypto backend does not support recovery key generation (createRecoveryKeyFromPassphrase missing)");
405
+ recoveryKey = await crypto.createRecoveryKeyFromPassphrase();
406
+ this.saveRecoveryKeyToDisk(recoveryKey);
407
+ generatedRecoveryKey = true;
408
+ return recoveryKey;
409
+ };
410
+ const shouldRecreateSecretStorage = options.forceNewSecretStorage === true || !hasDefaultSecretStorageKey || !recoveryKey && status?.ready === false || hasKnownInvalidSecrets;
411
+ if (hasKnownInvalidSecrets) recoveryKey = null;
412
+ const secretStorageOptions = { setupNewKeyBackup: options.setupNewKeyBackup === true };
413
+ if (shouldRecreateSecretStorage) {
414
+ secretStorageOptions.setupNewSecretStorage = true;
415
+ secretStorageOptions.createSecretStorageKey = ensureRecoveryKey;
416
+ }
417
+ try {
418
+ await crypto.bootstrapSecretStorage(secretStorageOptions);
419
+ } catch (err) {
420
+ if (!(options.allowSecretStorageRecreateWithoutRecoveryKey === true && hasDefaultSecretStorageKey && isRepairableSecretStorageAccessError(err))) throw err;
421
+ recoveryKey = null;
422
+ LogService.warn("MatrixClientLite", "Secret storage exists on the server but local recovery material cannot unlock it; recreating secret storage during explicit bootstrap.");
423
+ await crypto.bootstrapSecretStorage({
424
+ setupNewSecretStorage: true,
425
+ setupNewKeyBackup: options.setupNewKeyBackup === true,
426
+ createSecretStorageKey: ensureRecoveryKey
427
+ });
428
+ }
429
+ if (generatedRecoveryKey && this.recoveryKeyPath) LogService.warn("MatrixClientLite", `Generated Matrix recovery key and saved it to ${this.recoveryKeyPath}. Keep this file secure.`);
430
+ }
431
+ clearStagedRecoveryKeyTracking() {
432
+ this.stagedRecoveryKey = null;
433
+ this.stagedRecoveryKeyUsed = false;
434
+ this.stagedCacheKeyIds.clear();
435
+ }
436
+ resolveStagedSecretStorageKey(requestedKeyIds) {
437
+ const staged = this.stagedRecoveryKey;
438
+ if (!staged?.privateKeyBase64) return null;
439
+ const privateKey = new Uint8Array(Buffer.from(staged.privateKeyBase64, "base64"));
440
+ if (privateKey.length === 0) return null;
441
+ const keyId = staged.keyId && requestedKeyIds.includes(staged.keyId) ? staged.keyId : requestedKeyIds[0];
442
+ if (!keyId) return null;
443
+ this.rememberStagedSecretStorageKey(keyId, privateKey, staged.keyInfo);
444
+ this.stagedCacheKeyIds.add(keyId);
445
+ return [keyId, privateKey];
446
+ }
447
+ rememberStagedSecretStorageKey(keyId, key, keyInfo) {
448
+ this.stagedRecoveryKeyUsed = true;
449
+ this.rememberSecretStorageKey(keyId, key, keyInfo);
450
+ }
451
+ rememberSecretStorageKey(keyId, key, keyInfo) {
452
+ if (!keyId.trim()) return;
453
+ this.secretStorageKeyCache.set(keyId, {
454
+ key: new Uint8Array(key),
455
+ keyInfo
456
+ });
457
+ }
458
+ loadStoredRecoveryKey() {
459
+ if (!this.recoveryKeyPath) return null;
460
+ try {
461
+ const parsed = loadJsonFile(this.recoveryKeyPath);
462
+ if (parsed?.version !== 1 || typeof parsed.createdAt !== "string" || typeof parsed.privateKeyBase64 !== "string" || !parsed.privateKeyBase64.trim()) return null;
463
+ return {
464
+ version: 1,
465
+ createdAt: parsed.createdAt,
466
+ keyId: typeof parsed.keyId === "string" ? parsed.keyId : null,
467
+ encodedPrivateKey: typeof parsed.encodedPrivateKey === "string" ? parsed.encodedPrivateKey : void 0,
468
+ privateKeyBase64: parsed.privateKeyBase64,
469
+ keyInfo: parsed.keyInfo && typeof parsed.keyInfo === "object" ? {
470
+ passphrase: parsed.keyInfo.passphrase,
471
+ name: typeof parsed.keyInfo.name === "string" ? parsed.keyInfo.name : void 0
472
+ } : void 0
473
+ };
474
+ } catch {
475
+ return null;
476
+ }
477
+ }
478
+ saveRecoveryKeyToDisk(params) {
479
+ if (!this.recoveryKeyPath) return;
480
+ try {
481
+ const payload = {
482
+ version: 1,
483
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
484
+ keyId: typeof params.keyId === "string" ? params.keyId : null,
485
+ encodedPrivateKey: params.encodedPrivateKey,
486
+ privateKeyBase64: Buffer.from(params.privateKey).toString("base64"),
487
+ keyInfo: params.keyInfo ? {
488
+ passphrase: params.keyInfo.passphrase,
489
+ name: params.keyInfo.name
490
+ } : void 0
491
+ };
492
+ saveJsonFile(this.recoveryKeyPath, payload);
493
+ } catch (err) {
494
+ LogService.warn("MatrixClientLite", "Failed to persist recovery key:", err);
495
+ }
496
+ }
497
+ };
498
+ //#endregion
499
+ //#region extensions/matrix/src/matrix/sdk.ts
500
+ var sdk_exports = /* @__PURE__ */ __exportAll({
501
+ ConsoleLogger: () => ConsoleLogger,
502
+ LogService: () => LogService,
503
+ MatrixClient: () => MatrixClient
504
+ });
505
+ const MATRIX_STATUS_DIAGNOSTIC_TIMEOUT_MS = 1e4;
506
+ function unresolvedMatrixRoomKeyBackupStatus() {
507
+ return {
508
+ serverVersion: null,
509
+ activeVersion: null,
510
+ trusted: null,
511
+ matchesDecryptionKey: null,
512
+ decryptionKeyCached: null,
513
+ keyLoadAttempted: false,
514
+ keyLoadError: null
515
+ };
516
+ }
517
+ function unresolvedMatrixDeviceVerificationStatus(params) {
518
+ return {
519
+ encryptionEnabled: true,
520
+ userId: params.userId,
521
+ deviceId: params.deviceId,
522
+ verified: false,
523
+ localVerified: false,
524
+ crossSigningVerified: false,
525
+ signedByOwner: false
526
+ };
527
+ }
528
+ async function resolveMatrixDiagnostic(promise, timeoutMs) {
529
+ return (await resolveMatrixDiagnosticResult(promise, timeoutMs)).value;
530
+ }
531
+ async function resolveMatrixDiagnosticResult(promise, timeoutMs) {
532
+ let timeoutId;
533
+ try {
534
+ const guarded = promise.then((value) => ({
535
+ error: null,
536
+ timedOut: false,
537
+ value
538
+ })).catch((error) => ({
539
+ error,
540
+ timedOut: false,
541
+ value: null
542
+ }));
543
+ const timeout = new Promise((resolve) => {
544
+ timeoutId = setTimeout(() => resolve({
545
+ error: null,
546
+ timedOut: true,
547
+ value: null
548
+ }), timeoutMs);
549
+ timeoutId.unref?.();
550
+ });
551
+ return await Promise.race([guarded, timeout]);
552
+ } finally {
553
+ if (timeoutId) clearTimeout(timeoutId);
554
+ }
555
+ }
556
+ function isMatrixAccessTokenInvalidatedError(error) {
557
+ if (!error || typeof error !== "object") return false;
558
+ const err = error;
559
+ const errcode = err.body?.errcode ?? err.data?.errcode;
560
+ if (err.statusCode === 401 && errcode === "M_UNKNOWN_TOKEN") return true;
561
+ const reason = formatMatrixErrorReason(error);
562
+ return reason.includes("m_unknown_token") || reason.includes("unknown token") || reason.includes("access token") && (reason.includes("invalid") || reason.includes("unrecognized") || reason.includes("unknown"));
563
+ }
564
+ const MATRIX_INITIAL_CRYPTO_BOOTSTRAP_OPTIONS = { allowAutomaticCrossSigningReset: false };
565
+ const MATRIX_AUTOMATIC_REPAIR_BOOTSTRAP_OPTIONS = {
566
+ forceResetCrossSigning: true,
567
+ allowSecretStorageRecreateWithoutRecoveryKey: true,
568
+ strict: true
569
+ };
570
+ function createMatrixExplicitBootstrapOptions(params) {
571
+ return {
572
+ forceResetCrossSigning: params?.forceResetCrossSigning === true,
573
+ allowAutomaticCrossSigningReset: params?.allowAutomaticCrossSigningReset !== false,
574
+ allowSecretStorageRecreateWithoutRecoveryKey: true,
575
+ strict: params?.strict !== false
576
+ };
577
+ }
578
+ let loadedMatrixCryptoRuntime = null;
579
+ let matrixCryptoRuntimePromise = null;
580
+ async function loadMatrixCryptoRuntime() {
581
+ matrixCryptoRuntimePromise ??= import("./crypto-runtime-1pKW4O2F.js").then((runtime) => {
582
+ loadedMatrixCryptoRuntime = runtime;
583
+ return runtime;
584
+ });
585
+ return await matrixCryptoRuntimePromise;
586
+ }
587
+ const normalizeOptionalString$1 = normalizeNullableString;
588
+ function isUnsupportedAuthenticatedMediaEndpointError(err) {
589
+ const statusCode = err?.statusCode;
590
+ if (statusCode === 404 || statusCode === 405 || statusCode === 501) return true;
591
+ const message = formatMatrixErrorReason(err);
592
+ return message.includes("m_unrecognized") || message.includes("unrecognized request") || message.includes("method not allowed") || message.includes("not implemented");
593
+ }
594
+ var MatrixClient = class {
595
+ constructor(homeserver, accessToken, opts = {}) {
596
+ this.emitter = new EventEmitter();
597
+ this.bridgeRegistered = false;
598
+ this.started = false;
599
+ this.cryptoBootstrapped = false;
600
+ this.dmRoomIds = /* @__PURE__ */ new Set();
601
+ this.cryptoInitialized = false;
602
+ this.sendQueue = new KeyedAsyncQueue();
603
+ this.stopPersistPromise = null;
604
+ this.verificationSummaryListenerBound = false;
605
+ this.currentSyncState = null;
606
+ this.dms = {
607
+ update: async () => {
608
+ return await this.refreshDmCache();
609
+ },
610
+ isDm: (roomId) => this.dmRoomIds.has(roomId)
611
+ };
612
+ this.idbPersistTimer = null;
613
+ this.httpClient = new MatrixAuthedHttpClient({
614
+ homeserver,
615
+ accessToken,
616
+ ssrfPolicy: opts.ssrfPolicy,
617
+ dispatcherPolicy: opts.dispatcherPolicy
618
+ });
619
+ this.localTimeoutMs = Math.max(1, opts.localTimeoutMs ?? 6e4);
620
+ this.initialSyncLimit = opts.initialSyncLimit;
621
+ this.syncFilter = opts.syncFilter;
622
+ this.encryptionEnabled = opts.encryption === true;
623
+ this.password = opts.password;
624
+ this.syncStore = opts.storagePath ? new FileBackedMatrixSyncStore(opts.storagePath) : void 0;
625
+ this.idbSnapshotPath = opts.idbSnapshotPath;
626
+ this.cryptoDatabasePrefix = opts.cryptoDatabasePrefix;
627
+ this.selfUserId = opts.userId?.trim() || null;
628
+ this.autoBootstrapCrypto = opts.autoBootstrapCrypto !== false;
629
+ this.recoveryKeyStore = new MatrixRecoveryKeyStore(opts.recoveryKeyPath);
630
+ const cryptoCallbacks = this.encryptionEnabled ? this.recoveryKeyStore.buildCryptoCallbacks() : void 0;
631
+ this.client = createClient({
632
+ baseUrl: homeserver,
633
+ accessToken,
634
+ userId: opts.userId,
635
+ deviceId: opts.deviceId,
636
+ logger: createMatrixJsSdkClientLogger("MatrixClient"),
637
+ localTimeoutMs: this.localTimeoutMs,
638
+ fetchFn: createMatrixGuardedFetch({
639
+ ssrfPolicy: opts.ssrfPolicy,
640
+ dispatcherPolicy: opts.dispatcherPolicy
641
+ }),
642
+ store: this.syncStore,
643
+ cryptoCallbacks,
644
+ verificationMethods: [
645
+ VerificationMethod.Sas,
646
+ VerificationMethod.ShowQrCode,
647
+ VerificationMethod.ScanQrCode,
648
+ VerificationMethod.Reciprocate
649
+ ]
650
+ });
651
+ }
652
+ on(eventName, listener) {
653
+ this.emitter.on(eventName, listener);
654
+ return this;
655
+ }
656
+ off(eventName, listener) {
657
+ this.emitter.off(eventName, listener);
658
+ return this;
659
+ }
660
+ async ensureCryptoSupportInitialized() {
661
+ if (this.decryptBridge && (!this.encryptionEnabled || this.verificationManager && this.cryptoBootstrapper && this.crypto)) return;
662
+ const runtime = await loadMatrixCryptoRuntime();
663
+ this.decryptBridge ??= new runtime.MatrixDecryptBridge({
664
+ client: this.client,
665
+ toRaw: (event) => matrixEventToRaw(event, { contentMode: "original" }),
666
+ emitDecryptedEvent: (roomId, event) => {
667
+ this.emitter.emit("room.decrypted_event", roomId, event);
668
+ },
669
+ emitMessage: (roomId, event) => {
670
+ this.emitter.emit("room.message", roomId, event);
671
+ },
672
+ emitFailedDecryption: (roomId, event, error) => {
673
+ this.emitter.emit("room.failed_decryption", roomId, event, error);
674
+ }
675
+ });
676
+ if (!this.encryptionEnabled) return;
677
+ this.verificationManager ??= new runtime.MatrixVerificationManager({ trustOwnDeviceAfterSas: async (deviceId) => {
678
+ const crypto = this.client.getCrypto();
679
+ if (typeof crypto?.crossSignDevice !== "function") return;
680
+ await crypto.crossSignDevice(deviceId);
681
+ } });
682
+ this.cryptoBootstrapper ??= new runtime.MatrixCryptoBootstrapper({
683
+ getUserId: () => this.getUserId(),
684
+ getPassword: () => this.password,
685
+ getDeviceId: () => this.client.getDeviceId(),
686
+ verificationManager: this.verificationManager,
687
+ recoveryKeyStore: this.recoveryKeyStore,
688
+ decryptBridge: this.decryptBridge
689
+ });
690
+ if (!this.crypto) this.crypto = runtime.createMatrixCryptoFacade({
691
+ client: this.client,
692
+ verificationManager: this.verificationManager,
693
+ recoveryKeyStore: this.recoveryKeyStore,
694
+ getRoomStateEvent: (roomId, eventType, stateKey = "") => this.getRoomStateEvent(roomId, eventType, stateKey),
695
+ downloadContent: (mxcUrl) => this.downloadContent(mxcUrl)
696
+ });
697
+ if (!this.verificationSummaryListenerBound) {
698
+ this.verificationSummaryListenerBound = true;
699
+ this.verificationManager.onSummaryChanged((summary) => {
700
+ this.emitter.emit("verification.summary", summary);
701
+ });
702
+ }
703
+ }
704
+ async start(opts = {}) {
705
+ await this.startSyncSession({
706
+ bootstrapCrypto: true,
707
+ abortSignal: opts.abortSignal,
708
+ readyTimeoutMs: opts.readyTimeoutMs
709
+ });
710
+ }
711
+ async waitForInitialSyncReady(params = {}) {
712
+ const timeoutMs = params.timeoutMs ?? 3e4;
713
+ if (isMatrixReadySyncState(this.currentSyncState)) return;
714
+ if (isMatrixTerminalSyncState(this.currentSyncState)) throw new Error(`Matrix sync entered ${this.currentSyncState} during startup`);
715
+ await new Promise((resolve, reject) => {
716
+ let settled = false;
717
+ let timeoutId;
718
+ const abortSignal = params.abortSignal;
719
+ const cleanup = () => {
720
+ this.off("sync.state", onSyncState);
721
+ this.off("sync.unexpected_error", onUnexpectedError);
722
+ abortSignal?.removeEventListener("abort", onAbort);
723
+ if (timeoutId) {
724
+ clearTimeout(timeoutId);
725
+ timeoutId = void 0;
726
+ }
727
+ };
728
+ const settleResolve = () => {
729
+ if (settled) return;
730
+ settled = true;
731
+ cleanup();
732
+ resolve();
733
+ };
734
+ const settleReject = (error) => {
735
+ if (settled) return;
736
+ settled = true;
737
+ cleanup();
738
+ reject(error);
739
+ };
740
+ const onSyncState = (state, _prevState, error) => {
741
+ if (isMatrixReadySyncState(state)) {
742
+ settleResolve();
743
+ return;
744
+ }
745
+ if (isMatrixTerminalSyncState(state)) settleReject(new Error(error instanceof Error && error.message ? error.message : `Matrix sync entered ${state} during startup`));
746
+ };
747
+ const onUnexpectedError = (error) => {
748
+ settleReject(error);
749
+ };
750
+ const onAbort = () => {
751
+ settleReject(createMatrixStartupAbortError());
752
+ };
753
+ this.on("sync.state", onSyncState);
754
+ this.on("sync.unexpected_error", onUnexpectedError);
755
+ if (abortSignal?.aborted) {
756
+ onAbort();
757
+ return;
758
+ }
759
+ abortSignal?.addEventListener("abort", onAbort, { once: true });
760
+ timeoutId = setTimeout(() => {
761
+ settleReject(/* @__PURE__ */ new Error(`Matrix client did not reach a ready sync state within ${timeoutMs}ms`));
762
+ }, timeoutMs);
763
+ timeoutId.unref?.();
764
+ });
765
+ }
766
+ async startSyncSession(opts) {
767
+ if (this.started) return;
768
+ throwIfMatrixStartupAborted(opts.abortSignal);
769
+ await this.ensureCryptoSupportInitialized();
770
+ throwIfMatrixStartupAborted(opts.abortSignal);
771
+ this.registerBridge();
772
+ await this.initializeCryptoIfNeeded(opts.abortSignal);
773
+ await this.client.startClient({
774
+ initialSyncLimit: this.initialSyncLimit,
775
+ ...this.syncFilter ? { filter: Filter.fromJson(this.selfUserId, "", this.syncFilter) } : {}
776
+ });
777
+ await this.waitForInitialSyncReady({
778
+ abortSignal: opts.abortSignal,
779
+ timeoutMs: opts.readyTimeoutMs
780
+ });
781
+ throwIfMatrixStartupAborted(opts.abortSignal);
782
+ if (opts.bootstrapCrypto && this.autoBootstrapCrypto) await this.bootstrapCryptoIfNeeded(opts.abortSignal);
783
+ throwIfMatrixStartupAborted(opts.abortSignal);
784
+ this.started = true;
785
+ this.emitOutstandingInviteEvents();
786
+ await this.refreshDmCache().catch(noop);
787
+ }
788
+ async prepareForOneOff() {
789
+ if (!this.encryptionEnabled) return;
790
+ await this.ensureCryptoSupportInitialized();
791
+ await this.initializeCryptoIfNeeded();
792
+ if (!this.crypto) return;
793
+ try {
794
+ const joinedRooms = await this.getJoinedRooms();
795
+ await this.crypto.prepare(joinedRooms);
796
+ } catch {}
797
+ }
798
+ hasPersistedSyncState() {
799
+ return this.syncStore?.hasSavedSyncFromCleanShutdown() === true;
800
+ }
801
+ async ensureStartedForCryptoControlPlane() {
802
+ if (this.started) return;
803
+ await this.startSyncSession({ bootstrapCrypto: false });
804
+ }
805
+ stopSyncWithoutPersist() {
806
+ if (this.idbPersistTimer) {
807
+ clearInterval(this.idbPersistTimer);
808
+ this.idbPersistTimer = null;
809
+ }
810
+ this.currentSyncState = null;
811
+ this.client.stopClient();
812
+ this.started = false;
813
+ }
814
+ async drainPendingDecryptions(reason = "matrix client shutdown") {
815
+ await this.decryptBridge?.drainPendingDecryptions(reason);
816
+ }
817
+ stop() {
818
+ this.stopSyncWithoutPersist();
819
+ this.decryptBridge?.stop();
820
+ this.syncStore?.markCleanShutdown();
821
+ if (loadedMatrixCryptoRuntime) {
822
+ const { persistIdbToDisk } = loadedMatrixCryptoRuntime;
823
+ this.stopPersistPromise = Promise.all([persistIdbToDisk({
824
+ snapshotPath: this.idbSnapshotPath,
825
+ databasePrefix: this.cryptoDatabasePrefix
826
+ }).catch(noop), this.syncStore?.flush().catch(noop)]).then(() => void 0);
827
+ return;
828
+ }
829
+ this.stopPersistPromise = loadMatrixCryptoRuntime().then(async ({ persistIdbToDisk }) => {
830
+ await Promise.all([persistIdbToDisk({
831
+ snapshotPath: this.idbSnapshotPath,
832
+ databasePrefix: this.cryptoDatabasePrefix
833
+ }).catch(noop), this.syncStore?.flush().catch(noop)]);
834
+ }).catch(noop).then(() => void 0);
835
+ }
836
+ async stopAndPersist() {
837
+ this.stop();
838
+ await this.stopPersistPromise;
839
+ }
840
+ stopWithoutPersist() {
841
+ this.stopSyncWithoutPersist();
842
+ this.decryptBridge?.stop();
843
+ this.stopPersistPromise = Promise.resolve();
844
+ }
845
+ async bootstrapCryptoIfNeeded(abortSignal) {
846
+ if (!this.encryptionEnabled || !this.cryptoInitialized || this.cryptoBootstrapped) return;
847
+ throwIfMatrixStartupAborted(abortSignal);
848
+ await this.ensureCryptoSupportInitialized();
849
+ const crypto = this.client.getCrypto();
850
+ if (!crypto) return;
851
+ const cryptoBootstrapper = this.cryptoBootstrapper;
852
+ if (!cryptoBootstrapper) return;
853
+ const initial = await cryptoBootstrapper.bootstrap(crypto, MATRIX_INITIAL_CRYPTO_BOOTSTRAP_OPTIONS);
854
+ throwIfMatrixStartupAborted(abortSignal);
855
+ 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.");
856
+ else try {
857
+ const repaired = await cryptoBootstrapper.bootstrap(crypto, MATRIX_AUTOMATIC_REPAIR_BOOTSTRAP_OPTIONS);
858
+ throwIfMatrixStartupAborted(abortSignal);
859
+ if (repaired.crossSigningPublished && repaired.ownDeviceVerified !== false) LogService.info("MatrixClientLite", "Cross-signing/bootstrap recovered after forced reset");
860
+ } catch (err) {
861
+ LogService.warn("MatrixClientLite", "Failed to recover cross-signing/bootstrap with forced reset:", err);
862
+ }
863
+ this.cryptoBootstrapped = true;
864
+ }
865
+ async initializeCryptoIfNeeded(abortSignal) {
866
+ if (!this.encryptionEnabled || this.cryptoInitialized) return;
867
+ throwIfMatrixStartupAborted(abortSignal);
868
+ const { persistIdbToDisk, restoreIdbFromDisk } = await loadMatrixCryptoRuntime();
869
+ await restoreIdbFromDisk(this.idbSnapshotPath);
870
+ throwIfMatrixStartupAborted(abortSignal);
871
+ try {
872
+ await this.client.initRustCrypto({ cryptoDatabasePrefix: this.cryptoDatabasePrefix });
873
+ this.cryptoInitialized = true;
874
+ throwIfMatrixStartupAborted(abortSignal);
875
+ await persistIdbToDisk({
876
+ snapshotPath: this.idbSnapshotPath,
877
+ databasePrefix: this.cryptoDatabasePrefix
878
+ });
879
+ throwIfMatrixStartupAborted(abortSignal);
880
+ this.idbPersistTimer = setInterval(() => {
881
+ persistIdbToDisk({
882
+ snapshotPath: this.idbSnapshotPath,
883
+ databasePrefix: this.cryptoDatabasePrefix
884
+ }).catch(noop);
885
+ }, MATRIX_IDB_PERSIST_INTERVAL_MS);
886
+ this.idbPersistTimer.unref?.();
887
+ } catch (err) {
888
+ LogService.warn("MatrixClientLite", "Failed to initialize rust crypto:", err);
889
+ }
890
+ }
891
+ async getUserId() {
892
+ const fromClient = this.client.getUserId();
893
+ if (fromClient) {
894
+ this.selfUserId = fromClient;
895
+ return fromClient;
896
+ }
897
+ if (this.selfUserId) return this.selfUserId;
898
+ const resolved = (await this.doRequest("GET", "/_matrix/client/v3/account/whoami")).user_id?.trim();
899
+ if (!resolved) throw new Error("Matrix whoami did not return user_id");
900
+ this.selfUserId = resolved;
901
+ return resolved;
902
+ }
903
+ async getJoinedRooms() {
904
+ const joined = await this.doRequest("GET", "/_matrix/client/v3/joined_rooms");
905
+ return Array.isArray(joined.joined_rooms) ? joined.joined_rooms : [];
906
+ }
907
+ async getJoinedRoomMembers(roomId) {
908
+ const joined = (await this.client.getJoinedRoomMembers(roomId))?.joined;
909
+ if (!joined || typeof joined !== "object") return [];
910
+ return Object.keys(joined);
911
+ }
912
+ hasSyncedJoinedRoomMember(roomId, userId) {
913
+ return (this.client.getRoom?.(roomId))?.currentState?.getMember?.(userId)?.membership === "join";
914
+ }
915
+ async getRoomStateEvent(roomId, eventType, stateKey = "") {
916
+ return await this.client.getStateEvent(roomId, eventType, stateKey) ?? {};
917
+ }
918
+ async getAccountData(eventType) {
919
+ return this.client.getAccountData(eventType)?.getContent() ?? void 0;
920
+ }
921
+ async setAccountData(eventType, content) {
922
+ await this.client.setAccountData(eventType, content);
923
+ await this.refreshDmCache().catch(noop);
924
+ }
925
+ async resolveRoom(aliasOrRoomId) {
926
+ if (aliasOrRoomId.startsWith("!")) return aliasOrRoomId;
927
+ if (!aliasOrRoomId.startsWith("#")) return aliasOrRoomId;
928
+ try {
929
+ return (await this.client.getRoomIdForAlias(aliasOrRoomId)).room_id ?? null;
930
+ } catch {
931
+ return null;
932
+ }
933
+ }
934
+ async createDirectRoom(remoteUserId, opts = {}) {
935
+ const initialState = opts.encrypted ? [{
936
+ type: "m.room.encryption",
937
+ state_key: "",
938
+ content: { algorithm: "m.megolm.v1.aes-sha2" }
939
+ }] : void 0;
940
+ return (await this.client.createRoom({
941
+ invite: [remoteUserId],
942
+ is_direct: true,
943
+ preset: Preset.TrustedPrivateChat,
944
+ initial_state: initialState
945
+ })).room_id;
946
+ }
947
+ async sendMessage(roomId, content) {
948
+ return await this.runSerializedRoomSend(roomId, async () => {
949
+ return (await this.client.sendMessage(roomId, content)).event_id;
950
+ });
951
+ }
952
+ async sendEvent(roomId, eventType, content) {
953
+ return await this.runSerializedRoomSend(roomId, async () => {
954
+ return (await this.client.sendEvent(roomId, eventType, content)).event_id;
955
+ });
956
+ }
957
+ async runSerializedRoomSend(roomId, task) {
958
+ return await this.sendQueue.enqueue(roomId, task);
959
+ }
960
+ async sendStateEvent(roomId, eventType, stateKey, content) {
961
+ return (await this.client.sendStateEvent(roomId, eventType, content, stateKey)).event_id;
962
+ }
963
+ async redactEvent(roomId, eventId, reason) {
964
+ return (await this.client.redactEvent(roomId, eventId, void 0, reason?.trim() ? { reason } : void 0)).event_id;
965
+ }
966
+ async doRequest(method, endpoint, qs, body, opts) {
967
+ return await this.httpClient.requestJson({
968
+ method,
969
+ endpoint,
970
+ qs,
971
+ body,
972
+ timeoutMs: this.localTimeoutMs,
973
+ allowAbsoluteEndpoint: opts?.allowAbsoluteEndpoint
974
+ });
975
+ }
976
+ async getUserProfile(userId) {
977
+ return await this.client.getProfileInfo(userId);
978
+ }
979
+ async setDisplayName(displayName) {
980
+ await this.client.setDisplayName(displayName);
981
+ }
982
+ async setAvatarUrl(avatarUrl) {
983
+ await this.client.setAvatarUrl(avatarUrl);
984
+ }
985
+ async joinRoom(roomId) {
986
+ await this.client.joinRoom(roomId);
987
+ }
988
+ mxcToHttp(mxcUrl) {
989
+ return this.client.mxcUrlToHttp(mxcUrl, void 0, void 0, void 0, true, false, true);
990
+ }
991
+ async downloadContent(mxcUrl, opts = {}) {
992
+ const parsed = parseMxc(mxcUrl);
993
+ if (!parsed) throw new Error(`Invalid Matrix content URI: ${mxcUrl}`);
994
+ const encodedServer = encodeURIComponent(parsed.server);
995
+ const encodedMediaId = encodeURIComponent(parsed.mediaId);
996
+ const request = async (endpoint) => await this.httpClient.requestRaw({
997
+ method: "GET",
998
+ endpoint,
999
+ qs: { allow_remote: opts.allowRemote ?? true },
1000
+ timeoutMs: this.localTimeoutMs,
1001
+ maxBytes: opts.maxBytes,
1002
+ readIdleTimeoutMs: opts.readIdleTimeoutMs
1003
+ });
1004
+ const authenticatedEndpoint = `/_matrix/client/v1/media/download/${encodedServer}/${encodedMediaId}`;
1005
+ try {
1006
+ return await request(authenticatedEndpoint);
1007
+ } catch (err) {
1008
+ if (!isUnsupportedAuthenticatedMediaEndpointError(err)) throw err;
1009
+ }
1010
+ return await request(`/_matrix/media/v3/download/${encodedServer}/${encodedMediaId}`);
1011
+ }
1012
+ async uploadContent(file, contentType, filename) {
1013
+ return (await this.client.uploadContent(new Uint8Array(file), {
1014
+ type: contentType || "application/octet-stream",
1015
+ name: filename,
1016
+ includeFilename: Boolean(filename)
1017
+ })).content_uri;
1018
+ }
1019
+ async getEvent(roomId, eventId) {
1020
+ const rawEvent = await this.client.fetchRoomEvent(roomId, eventId);
1021
+ if (rawEvent.type !== "m.room.encrypted") return rawEvent;
1022
+ const event = this.client.getEventMapper()(rawEvent);
1023
+ let decryptedEvent;
1024
+ const onDecrypted = (candidate) => {
1025
+ decryptedEvent = candidate;
1026
+ };
1027
+ event.once(MatrixEventEvent.Decrypted, onDecrypted);
1028
+ try {
1029
+ await this.client.decryptEventIfNeeded(event);
1030
+ } finally {
1031
+ event.off(MatrixEventEvent.Decrypted, onDecrypted);
1032
+ }
1033
+ return matrixEventToRaw(decryptedEvent ?? event);
1034
+ }
1035
+ async getRelations(roomId, eventId, relationType, eventType, opts = {}) {
1036
+ const result = await this.client.relations(roomId, eventId, relationType, eventType, opts);
1037
+ return {
1038
+ originalEvent: result.originalEvent ? matrixEventToRaw(result.originalEvent) : null,
1039
+ events: result.events.map((event) => matrixEventToRaw(event)),
1040
+ nextBatch: result.nextBatch ?? null,
1041
+ prevBatch: result.prevBatch ?? null
1042
+ };
1043
+ }
1044
+ async hydrateEvents(roomId, events) {
1045
+ if (events.length === 0) return [];
1046
+ const mapper = this.client.getEventMapper();
1047
+ const mappedEvents = events.map((event) => mapper({
1048
+ room_id: roomId,
1049
+ ...event
1050
+ }));
1051
+ await Promise.all(mappedEvents.map((event) => this.client.decryptEventIfNeeded(event)));
1052
+ return mappedEvents.map((event) => matrixEventToRaw(event));
1053
+ }
1054
+ async setTyping(roomId, typing, timeoutMs) {
1055
+ await this.client.sendTyping(roomId, typing, timeoutMs);
1056
+ }
1057
+ async sendReadReceipt(roomId, eventId) {
1058
+ await this.httpClient.requestJson({
1059
+ method: "POST",
1060
+ endpoint: `/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}/receipt/m.read/${encodeURIComponent(eventId)}`,
1061
+ body: {},
1062
+ timeoutMs: this.localTimeoutMs
1063
+ });
1064
+ }
1065
+ async getRoomKeyBackupStatus() {
1066
+ if (!this.encryptionEnabled) return {
1067
+ serverVersion: null,
1068
+ activeVersion: null,
1069
+ trusted: null,
1070
+ matchesDecryptionKey: null,
1071
+ decryptionKeyCached: null,
1072
+ keyLoadAttempted: false,
1073
+ keyLoadError: null
1074
+ };
1075
+ const crypto = this.client.getCrypto();
1076
+ const serverVersionFallback = await this.resolveRoomKeyBackupVersion();
1077
+ if (!crypto) return {
1078
+ serverVersion: serverVersionFallback,
1079
+ activeVersion: null,
1080
+ trusted: null,
1081
+ matchesDecryptionKey: null,
1082
+ decryptionKeyCached: null,
1083
+ keyLoadAttempted: false,
1084
+ keyLoadError: null
1085
+ };
1086
+ let { activeVersion, decryptionKeyCached } = await this.resolveRoomKeyBackupLocalState(crypto);
1087
+ let { serverVersion, trusted, matchesDecryptionKey } = await this.resolveRoomKeyBackupTrustState(crypto, serverVersionFallback);
1088
+ const shouldLoadBackupKey = Boolean(serverVersion) && (decryptionKeyCached === false || matchesDecryptionKey === false);
1089
+ const shouldActivateBackup = Boolean(serverVersion) && !activeVersion;
1090
+ let keyLoadAttempted = false;
1091
+ let keyLoadError = null;
1092
+ if (serverVersion && (shouldLoadBackupKey || shouldActivateBackup)) {
1093
+ if (shouldLoadBackupKey) if (typeof crypto.loadSessionBackupPrivateKeyFromSecretStorage === "function") {
1094
+ keyLoadAttempted = true;
1095
+ try {
1096
+ await crypto.loadSessionBackupPrivateKeyFromSecretStorage();
1097
+ } catch (err) {
1098
+ keyLoadError = formatMatrixErrorMessage(err);
1099
+ }
1100
+ } else keyLoadError = "Matrix crypto backend does not support loading backup keys from secret storage";
1101
+ if (!keyLoadError) await this.enableTrustedRoomKeyBackupIfPossible(crypto);
1102
+ ({activeVersion, decryptionKeyCached} = await this.resolveRoomKeyBackupLocalState(crypto));
1103
+ ({serverVersion, trusted, matchesDecryptionKey} = await this.resolveRoomKeyBackupTrustState(crypto, serverVersion));
1104
+ }
1105
+ return {
1106
+ serverVersion,
1107
+ activeVersion,
1108
+ trusted,
1109
+ matchesDecryptionKey,
1110
+ decryptionKeyCached,
1111
+ keyLoadAttempted,
1112
+ keyLoadError
1113
+ };
1114
+ }
1115
+ async getDeviceVerificationStatus(userId, deviceId) {
1116
+ const normalizedUserId = userId?.trim() || null;
1117
+ const normalizedDeviceId = deviceId?.trim() || null;
1118
+ if (!this.encryptionEnabled) return {
1119
+ encryptionEnabled: false,
1120
+ userId: normalizedUserId,
1121
+ deviceId: normalizedDeviceId,
1122
+ verified: false,
1123
+ localVerified: false,
1124
+ crossSigningVerified: false,
1125
+ signedByOwner: false
1126
+ };
1127
+ const crypto = this.client.getCrypto();
1128
+ let deviceStatus = null;
1129
+ if (crypto && normalizedUserId && normalizedDeviceId && typeof crypto.getDeviceVerificationStatus === "function") deviceStatus = await crypto.getDeviceVerificationStatus(normalizedUserId, normalizedDeviceId).catch(() => null);
1130
+ const { isMatrixDeviceVerifiedInCurrentClient } = await loadMatrixCryptoRuntime();
1131
+ return {
1132
+ encryptionEnabled: true,
1133
+ userId: normalizedUserId,
1134
+ deviceId: normalizedDeviceId,
1135
+ verified: isMatrixDeviceVerifiedInCurrentClient(deviceStatus),
1136
+ localVerified: deviceStatus?.localVerified === true,
1137
+ crossSigningVerified: deviceStatus?.crossSigningVerified === true,
1138
+ signedByOwner: deviceStatus?.signedByOwner === true
1139
+ };
1140
+ }
1141
+ async getOwnDeviceVerificationStatus() {
1142
+ const recoveryKey = this.recoveryKeyStore.getRecoveryKeySummary();
1143
+ const userId = this.client.getUserId() ?? this.selfUserId ?? null;
1144
+ const deviceId = this.client.getDeviceId()?.trim() || null;
1145
+ const diagnosticTimeoutMs = Math.min(this.localTimeoutMs, MATRIX_STATUS_DIAGNOSTIC_TIMEOUT_MS);
1146
+ const [backup, deviceVerification, ownDevices] = await Promise.all([
1147
+ resolveMatrixDiagnostic(this.getRoomKeyBackupStatus(), diagnosticTimeoutMs),
1148
+ resolveMatrixDiagnostic(this.getDeviceVerificationStatus(userId, deviceId), diagnosticTimeoutMs),
1149
+ resolveMatrixDiagnosticResult(this.listOwnDevices(), diagnosticTimeoutMs)
1150
+ ]);
1151
+ const resolvedBackup = backup ?? unresolvedMatrixRoomKeyBackupStatus();
1152
+ const resolvedDeviceVerification = deviceVerification ?? unresolvedMatrixDeviceVerificationStatus({
1153
+ userId,
1154
+ deviceId
1155
+ });
1156
+ const serverDeviceKnown = deviceId ? ownDevices.value ? ownDevices.value.some((device) => device.deviceId === deviceId) : isMatrixAccessTokenInvalidatedError(ownDevices.error) ? false : null : null;
1157
+ return {
1158
+ ...resolvedDeviceVerification,
1159
+ verified: resolvedDeviceVerification.crossSigningVerified,
1160
+ recoveryKeyStored: Boolean(recoveryKey),
1161
+ recoveryKeyCreatedAt: recoveryKey?.createdAt ?? null,
1162
+ recoveryKeyId: recoveryKey?.keyId ?? null,
1163
+ backupVersion: resolvedBackup.serverVersion,
1164
+ backup: resolvedBackup,
1165
+ serverDeviceKnown
1166
+ };
1167
+ }
1168
+ async getOwnDeviceIdentityVerificationStatus() {
1169
+ const userId = this.client.getUserId() ?? this.selfUserId ?? null;
1170
+ const deviceId = this.client.getDeviceId()?.trim() || null;
1171
+ const deviceVerification = await this.getDeviceVerificationStatus(userId, deviceId);
1172
+ return {
1173
+ ...deviceVerification,
1174
+ verified: deviceVerification.crossSigningVerified
1175
+ };
1176
+ }
1177
+ async trustOwnIdentityAfterSelfVerification() {
1178
+ if (!this.encryptionEnabled) return;
1179
+ await this.ensureStartedForCryptoControlPlane();
1180
+ await this.ensureCryptoSupportInitialized();
1181
+ const crypto = this.client.getCrypto();
1182
+ const ownIdentity = crypto && typeof crypto.getOwnIdentity === "function" ? await crypto.getOwnIdentity().catch(() => void 0) : void 0;
1183
+ if (!ownIdentity) return;
1184
+ try {
1185
+ if (typeof ownIdentity.isVerified === "function" && ownIdentity.isVerified()) return;
1186
+ if (typeof ownIdentity.verify !== "function") return;
1187
+ await ownIdentity.verify();
1188
+ } finally {
1189
+ ownIdentity.free?.();
1190
+ }
1191
+ }
1192
+ async verifyWithRecoveryKey(rawRecoveryKey) {
1193
+ const fail = async (error, fields = {}) => {
1194
+ const status = await this.getOwnDeviceVerificationStatus();
1195
+ return {
1196
+ success: false,
1197
+ recoveryKeyAccepted: fields.recoveryKeyAccepted ?? false,
1198
+ backupUsable: fields.backupUsable ?? false,
1199
+ deviceOwnerVerified: fields.deviceOwnerVerified ?? status.verified,
1200
+ error,
1201
+ ...status
1202
+ };
1203
+ };
1204
+ if (!this.encryptionEnabled) return await fail("Matrix encryption is disabled for this client");
1205
+ await this.ensureStartedForCryptoControlPlane();
1206
+ await this.ensureCryptoSupportInitialized();
1207
+ const crypto = this.client.getCrypto();
1208
+ if (!crypto) return await fail("Matrix crypto is not available (start client with encryption enabled)");
1209
+ const backupUsableBeforeStagedRecovery = resolveMatrixRoomKeyBackupReadinessError(await this.getRoomKeyBackupStatus(), { requireServerBackup: true }) === null;
1210
+ const trimmedRecoveryKey = rawRecoveryKey.trim();
1211
+ if (!trimmedRecoveryKey) return await fail("Matrix recovery key is required");
1212
+ let stagedKeyId = null;
1213
+ try {
1214
+ stagedKeyId = await this.resolveDefaultSecretStorageKeyId(crypto) ?? null;
1215
+ this.recoveryKeyStore.stageEncodedRecoveryKey({
1216
+ encodedPrivateKey: trimmedRecoveryKey,
1217
+ keyId: stagedKeyId
1218
+ });
1219
+ } catch (err) {
1220
+ return await fail(formatMatrixErrorMessage(err));
1221
+ }
1222
+ const storedRecoveryKeyMatches = this.recoveryKeyStore.getRecoveryKeySummary()?.encodedPrivateKey?.trim() === trimmedRecoveryKey;
1223
+ if (backupUsableBeforeStagedRecovery && storedRecoveryKeyMatches) {
1224
+ const status = await this.getOwnDeviceVerificationStatus();
1225
+ const backupUsable = resolveMatrixRoomKeyBackupReadinessError(status.backup, { requireServerBackup: true }) === null;
1226
+ const backupError = resolveMatrixRoomKeyBackupReadinessError(status.backup, { requireServerBackup: false });
1227
+ const recoveryKeyAccepted = backupUsable;
1228
+ if (!status.verified) {
1229
+ if (recoveryKeyAccepted) this.recoveryKeyStore.commitStagedRecoveryKey({ keyId: stagedKeyId });
1230
+ else this.recoveryKeyStore.discardStagedRecoveryKey();
1231
+ return {
1232
+ success: false,
1233
+ recoveryKeyAccepted,
1234
+ backupUsable,
1235
+ deviceOwnerVerified: false,
1236
+ 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.",
1237
+ ...status
1238
+ };
1239
+ }
1240
+ if (backupError) {
1241
+ this.recoveryKeyStore.discardStagedRecoveryKey();
1242
+ return {
1243
+ success: false,
1244
+ recoveryKeyAccepted,
1245
+ backupUsable,
1246
+ deviceOwnerVerified: true,
1247
+ error: backupError,
1248
+ ...status
1249
+ };
1250
+ }
1251
+ this.recoveryKeyStore.commitStagedRecoveryKey({ keyId: stagedKeyId });
1252
+ return {
1253
+ success: true,
1254
+ recoveryKeyAccepted: true,
1255
+ backupUsable,
1256
+ deviceOwnerVerified: true,
1257
+ verifiedAt: (/* @__PURE__ */ new Date()).toISOString(),
1258
+ ...status
1259
+ };
1260
+ }
1261
+ try {
1262
+ const cryptoBootstrapper = this.cryptoBootstrapper;
1263
+ if (!cryptoBootstrapper) return await fail("Matrix crypto bootstrapper is not available");
1264
+ await cryptoBootstrapper.bootstrap(crypto, { allowAutomaticCrossSigningReset: false });
1265
+ await this.enableTrustedRoomKeyBackupIfPossible(crypto);
1266
+ const status = await this.getOwnDeviceVerificationStatus();
1267
+ const backupError = resolveMatrixRoomKeyBackupReadinessError(status.backup, { requireServerBackup: false });
1268
+ const backupUsable = resolveMatrixRoomKeyBackupReadinessError(status.backup, { requireServerBackup: true }) === null;
1269
+ const stagedRecoveryKeyUsed = this.recoveryKeyStore.hasStagedRecoveryKeyBeenUsed();
1270
+ const secretStorageStatus = typeof crypto.getSecretStorageStatus === "function" ? await crypto.getSecretStorageStatus().catch(() => null) : null;
1271
+ const stagedRecoveryKeyConfirmedBySecretStorage = Boolean(stagedKeyId) && secretStorageStatus?.secretStorageKeyValidityMap?.[stagedKeyId ?? ""] === true;
1272
+ const stagedRecoveryKeyRejectedBySecretStorage = Boolean(stagedKeyId) && secretStorageStatus?.secretStorageKeyValidityMap?.[stagedKeyId ?? ""] === false;
1273
+ const stagedRecoveryKeyValidated = stagedRecoveryKeyUsed && (stagedRecoveryKeyConfirmedBySecretStorage || stagedRecoveryKeyUsed && !stagedRecoveryKeyRejectedBySecretStorage && !stagedRecoveryKeyConfirmedBySecretStorage && !backupUsableBeforeStagedRecovery && backupUsable) || storedRecoveryKeyMatches && backupUsable;
1274
+ const recoveryKeyAccepted = stagedRecoveryKeyValidated && (status.verified || backupUsable);
1275
+ if (!status.verified) {
1276
+ if (backupUsable && stagedRecoveryKeyValidated) this.recoveryKeyStore.commitStagedRecoveryKey({ keyId: stagedKeyId });
1277
+ else this.recoveryKeyStore.discardStagedRecoveryKey();
1278
+ return {
1279
+ success: false,
1280
+ recoveryKeyAccepted,
1281
+ backupUsable,
1282
+ deviceOwnerVerified: false,
1283
+ 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.",
1284
+ ...recoveryKeyAccepted ? await this.getOwnDeviceVerificationStatus() : status
1285
+ };
1286
+ }
1287
+ if (backupError) {
1288
+ this.recoveryKeyStore.discardStagedRecoveryKey();
1289
+ return {
1290
+ success: false,
1291
+ recoveryKeyAccepted,
1292
+ backupUsable,
1293
+ deviceOwnerVerified: true,
1294
+ error: backupError,
1295
+ ...status
1296
+ };
1297
+ }
1298
+ if (!stagedRecoveryKeyValidated) {
1299
+ this.recoveryKeyStore.discardStagedRecoveryKey();
1300
+ return {
1301
+ success: false,
1302
+ recoveryKeyAccepted: false,
1303
+ backupUsable,
1304
+ deviceOwnerVerified: true,
1305
+ error: "Matrix recovery key could not be verified against active Matrix backup material; existing backup may be usable from previously loaded recovery material.",
1306
+ ...status
1307
+ };
1308
+ }
1309
+ this.recoveryKeyStore.commitStagedRecoveryKey({ keyId: stagedKeyId });
1310
+ const committedStatus = await this.getOwnDeviceVerificationStatus();
1311
+ return {
1312
+ success: true,
1313
+ recoveryKeyAccepted: true,
1314
+ backupUsable,
1315
+ deviceOwnerVerified: true,
1316
+ verifiedAt: (/* @__PURE__ */ new Date()).toISOString(),
1317
+ ...committedStatus
1318
+ };
1319
+ } catch (err) {
1320
+ this.recoveryKeyStore.discardStagedRecoveryKey();
1321
+ return await fail(formatMatrixErrorMessage(err));
1322
+ }
1323
+ }
1324
+ async restoreRoomKeyBackup(params = {}) {
1325
+ let loadedFromSecretStorage = false;
1326
+ const fail = async (error) => {
1327
+ const backup = await this.getRoomKeyBackupStatus();
1328
+ return {
1329
+ success: false,
1330
+ error,
1331
+ backupVersion: backup.serverVersion,
1332
+ imported: 0,
1333
+ total: 0,
1334
+ loadedFromSecretStorage,
1335
+ backup
1336
+ };
1337
+ };
1338
+ if (!this.encryptionEnabled) return await fail("Matrix encryption is disabled for this client");
1339
+ await this.ensureStartedForCryptoControlPlane();
1340
+ const crypto = this.client.getCrypto();
1341
+ if (!crypto) return await fail("Matrix crypto is not available (start client with encryption enabled)");
1342
+ try {
1343
+ const rawRecoveryKey = params.recoveryKey?.trim();
1344
+ if (rawRecoveryKey) this.recoveryKeyStore.stageEncodedRecoveryKey({
1345
+ encodedPrivateKey: rawRecoveryKey,
1346
+ keyId: await this.resolveDefaultSecretStorageKeyId(crypto)
1347
+ });
1348
+ const backup = await this.getRoomKeyBackupStatus();
1349
+ loadedFromSecretStorage = backup.keyLoadAttempted && !backup.keyLoadError;
1350
+ const backupError = resolveMatrixRoomKeyBackupReadinessError(backup, {
1351
+ allowUntrustedMatchingKey: true,
1352
+ requireServerBackup: true
1353
+ });
1354
+ if (backupError) {
1355
+ this.recoveryKeyStore.discardStagedRecoveryKey();
1356
+ return await fail(backupError);
1357
+ }
1358
+ if (typeof crypto.restoreKeyBackup !== "function") {
1359
+ this.recoveryKeyStore.discardStagedRecoveryKey();
1360
+ return await fail("Matrix crypto backend does not support full key backup restore");
1361
+ }
1362
+ const restore = await crypto.restoreKeyBackup();
1363
+ if (rawRecoveryKey) this.recoveryKeyStore.commitStagedRecoveryKey({ keyId: await this.resolveDefaultSecretStorageKeyId(crypto) });
1364
+ const finalBackup = await this.getRoomKeyBackupStatus();
1365
+ return {
1366
+ success: true,
1367
+ backupVersion: backup.serverVersion,
1368
+ imported: typeof restore.imported === "number" ? restore.imported : 0,
1369
+ total: typeof restore.total === "number" ? restore.total : 0,
1370
+ loadedFromSecretStorage,
1371
+ restoredAt: (/* @__PURE__ */ new Date()).toISOString(),
1372
+ backup: finalBackup
1373
+ };
1374
+ } catch (err) {
1375
+ this.recoveryKeyStore.discardStagedRecoveryKey();
1376
+ return await fail(formatMatrixErrorMessage(err));
1377
+ }
1378
+ }
1379
+ async resetRoomKeyBackup(options = {}) {
1380
+ let previousVersion = null;
1381
+ let deletedVersion = null;
1382
+ const fail = async (error) => {
1383
+ const backup = await this.getRoomKeyBackupStatus();
1384
+ return {
1385
+ success: false,
1386
+ error,
1387
+ previousVersion,
1388
+ deletedVersion,
1389
+ createdVersion: backup.serverVersion,
1390
+ backup
1391
+ };
1392
+ };
1393
+ if (!this.encryptionEnabled) return await fail("Matrix encryption is disabled for this client");
1394
+ await this.ensureStartedForCryptoControlPlane();
1395
+ const crypto = this.client.getCrypto();
1396
+ if (!crypto) return await fail("Matrix crypto is not available (start client with encryption enabled)");
1397
+ previousVersion = await this.resolveRoomKeyBackupVersion();
1398
+ const forceNewSecretStorage = options.rotateRecoveryKey === true || await this.shouldForceSecretStorageRecreationForBackupReset(crypto);
1399
+ try {
1400
+ if (previousVersion) {
1401
+ try {
1402
+ await this.doRequest("DELETE", `/_matrix/client/v3/room_keys/version/${encodeURIComponent(previousVersion)}`);
1403
+ } catch (err) {
1404
+ if (!isMatrixNotFoundError(err)) throw err;
1405
+ }
1406
+ deletedVersion = previousVersion;
1407
+ }
1408
+ await this.recoveryKeyStore.bootstrapSecretStorageWithRecoveryKey(crypto, {
1409
+ setupNewKeyBackup: true,
1410
+ forceNewSecretStorage,
1411
+ forceNewRecoveryKey: options.rotateRecoveryKey === true,
1412
+ allowSecretStorageRecreateWithoutRecoveryKey: true
1413
+ });
1414
+ await this.enableTrustedRoomKeyBackupIfPossible(crypto);
1415
+ const backup = await this.getRoomKeyBackupStatus();
1416
+ const createdVersion = backup.serverVersion;
1417
+ if (!createdVersion) return await fail("Matrix room key backup is still missing after reset.");
1418
+ if (backup.activeVersion !== createdVersion) return await fail("Matrix room key backup was recreated on the server but is not active on this device.");
1419
+ if (backup.decryptionKeyCached === false) return await fail("Matrix room key backup was recreated but its decryption key is not cached on this device.");
1420
+ if (backup.matchesDecryptionKey === false) return await fail("Matrix room key backup was recreated but this device does not have the matching backup decryption key.");
1421
+ if (backup.trusted === false) return await fail("Matrix room key backup was recreated but is not trusted on this device.");
1422
+ return {
1423
+ success: true,
1424
+ previousVersion,
1425
+ deletedVersion,
1426
+ createdVersion,
1427
+ resetAt: (/* @__PURE__ */ new Date()).toISOString(),
1428
+ backup
1429
+ };
1430
+ } catch (err) {
1431
+ return await fail(formatMatrixErrorMessage(err));
1432
+ }
1433
+ }
1434
+ async getOwnCrossSigningPublicationStatus() {
1435
+ const userId = this.client.getUserId() ?? this.selfUserId ?? null;
1436
+ if (!userId) return {
1437
+ userId: null,
1438
+ masterKeyPublished: false,
1439
+ selfSigningKeyPublished: false,
1440
+ userSigningKeyPublished: false,
1441
+ published: false
1442
+ };
1443
+ try {
1444
+ const response = await this.doRequest("POST", "/_matrix/client/v3/keys/query", void 0, { device_keys: { [userId]: [] } });
1445
+ const masterKeyPublished = Boolean(response.master_keys?.[userId]);
1446
+ const selfSigningKeyPublished = Boolean(response.self_signing_keys?.[userId]);
1447
+ const userSigningKeyPublished = Boolean(response.user_signing_keys?.[userId]);
1448
+ return {
1449
+ userId,
1450
+ masterKeyPublished,
1451
+ selfSigningKeyPublished,
1452
+ userSigningKeyPublished,
1453
+ published: masterKeyPublished && selfSigningKeyPublished && userSigningKeyPublished
1454
+ };
1455
+ } catch {
1456
+ return {
1457
+ userId,
1458
+ masterKeyPublished: false,
1459
+ selfSigningKeyPublished: false,
1460
+ userSigningKeyPublished: false,
1461
+ published: false
1462
+ };
1463
+ }
1464
+ }
1465
+ async bootstrapOwnDeviceVerification(params) {
1466
+ const pendingVerifications = async () => this.crypto ? (await this.crypto.listVerifications()).length : 0;
1467
+ if (!this.encryptionEnabled) return {
1468
+ success: false,
1469
+ error: "Matrix encryption is disabled for this client",
1470
+ verification: await this.getOwnDeviceVerificationStatus(),
1471
+ crossSigning: await this.getOwnCrossSigningPublicationStatus(),
1472
+ pendingVerifications: await pendingVerifications(),
1473
+ cryptoBootstrap: null
1474
+ };
1475
+ let bootstrapError;
1476
+ let bootstrapSummary = null;
1477
+ let rawRecoveryKey;
1478
+ try {
1479
+ await this.ensureStartedForCryptoControlPlane();
1480
+ await this.ensureCryptoSupportInitialized();
1481
+ const crypto = this.client.getCrypto();
1482
+ if (!crypto) throw new Error("Matrix crypto is not available (start client with encryption enabled)");
1483
+ rawRecoveryKey = params?.recoveryKey?.trim();
1484
+ if (rawRecoveryKey) this.recoveryKeyStore.stageEncodedRecoveryKey({
1485
+ encodedPrivateKey: rawRecoveryKey,
1486
+ keyId: await this.resolveDefaultSecretStorageKeyId(crypto)
1487
+ });
1488
+ const cryptoBootstrapper = this.cryptoBootstrapper;
1489
+ if (!cryptoBootstrapper) throw new Error("Matrix crypto bootstrapper is not available");
1490
+ bootstrapSummary = await cryptoBootstrapper.bootstrap(crypto, createMatrixExplicitBootstrapOptions({
1491
+ ...params,
1492
+ allowAutomaticCrossSigningReset: rawRecoveryKey ? false : params?.allowAutomaticCrossSigningReset
1493
+ }));
1494
+ await this.ensureRoomKeyBackupEnabled(crypto);
1495
+ } catch (err) {
1496
+ this.recoveryKeyStore.discardStagedRecoveryKey();
1497
+ bootstrapError = formatMatrixErrorMessage(err);
1498
+ }
1499
+ const verification = await this.getOwnDeviceVerificationStatus();
1500
+ const crossSigning = await this.getOwnCrossSigningPublicationStatus();
1501
+ 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";
1502
+ const backupError = verificationError === null ? resolveMatrixRoomKeyBackupReadinessError(verification.backup, {
1503
+ allowUntrustedMatchingKey: Boolean(rawRecoveryKey),
1504
+ requireServerBackup: true
1505
+ }) : null;
1506
+ const success = verificationError === null && backupError === null;
1507
+ if (success) this.recoveryKeyStore.commitStagedRecoveryKey({ keyId: await this.resolveDefaultSecretStorageKeyId(this.client.getCrypto()) });
1508
+ else this.recoveryKeyStore.discardStagedRecoveryKey();
1509
+ return {
1510
+ success,
1511
+ error: success ? void 0 : backupError ?? verificationError ?? void 0,
1512
+ verification: success ? await this.getOwnDeviceVerificationStatus() : verification,
1513
+ crossSigning,
1514
+ pendingVerifications: await pendingVerifications(),
1515
+ cryptoBootstrap: bootstrapSummary
1516
+ };
1517
+ }
1518
+ async listOwnDevices() {
1519
+ const currentDeviceId = this.client.getDeviceId()?.trim() || null;
1520
+ const devices = await this.client.getDevices();
1521
+ return (Array.isArray(devices?.devices) ? devices.devices : []).map((device) => ({
1522
+ deviceId: device.device_id,
1523
+ displayName: device.display_name?.trim() || null,
1524
+ lastSeenIp: device.last_seen_ip?.trim() || null,
1525
+ lastSeenTs: typeof device.last_seen_ts === "number" && Number.isFinite(device.last_seen_ts) ? device.last_seen_ts : null,
1526
+ current: currentDeviceId !== null && device.device_id === currentDeviceId
1527
+ }));
1528
+ }
1529
+ async deleteOwnDevices(deviceIds) {
1530
+ const uniqueDeviceIds = [...new Set(deviceIds.map((value) => value.trim()).filter(Boolean))];
1531
+ const currentDeviceId = this.client.getDeviceId()?.trim() || null;
1532
+ const protectedDeviceIds = uniqueDeviceIds.filter((deviceId) => deviceId === currentDeviceId);
1533
+ if (protectedDeviceIds.length > 0) throw new Error(`Refusing to delete the current Matrix device: ${protectedDeviceIds[0]}`);
1534
+ const deleteWithAuth = async (authData) => {
1535
+ await this.client.deleteMultipleDevices(uniqueDeviceIds, authData);
1536
+ };
1537
+ if (uniqueDeviceIds.length > 0) try {
1538
+ await deleteWithAuth();
1539
+ } catch (err) {
1540
+ 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;
1541
+ const userId = await this.getUserId().catch(() => this.selfUserId);
1542
+ if (!session || !userId || !this.password?.trim()) throw err;
1543
+ await deleteWithAuth({
1544
+ type: "m.login.password",
1545
+ session,
1546
+ identifier: {
1547
+ type: "m.id.user",
1548
+ user: userId
1549
+ },
1550
+ password: this.password
1551
+ });
1552
+ }
1553
+ return {
1554
+ currentDeviceId,
1555
+ deletedDeviceIds: uniqueDeviceIds,
1556
+ remainingDevices: await this.listOwnDevices()
1557
+ };
1558
+ }
1559
+ async resolveActiveRoomKeyBackupVersion(crypto) {
1560
+ if (typeof crypto.getActiveSessionBackupVersion !== "function") return null;
1561
+ return normalizeOptionalString$1(await crypto.getActiveSessionBackupVersion().catch(() => null));
1562
+ }
1563
+ async resolveCachedRoomKeyBackupDecryptionKey(crypto) {
1564
+ const getSessionBackupPrivateKey = crypto.getSessionBackupPrivateKey;
1565
+ if (typeof getSessionBackupPrivateKey !== "function") return null;
1566
+ const key = await getSessionBackupPrivateKey.call(crypto).catch(() => null);
1567
+ return key ? key.length > 0 : false;
1568
+ }
1569
+ async resolveRoomKeyBackupLocalState(crypto) {
1570
+ const [activeVersion, decryptionKeyCached] = await Promise.all([this.resolveActiveRoomKeyBackupVersion(crypto), this.resolveCachedRoomKeyBackupDecryptionKey(crypto)]);
1571
+ return {
1572
+ activeVersion,
1573
+ decryptionKeyCached
1574
+ };
1575
+ }
1576
+ async shouldForceSecretStorageRecreationForBackupReset(crypto) {
1577
+ if (await this.resolveCachedRoomKeyBackupDecryptionKey(crypto) !== false) return false;
1578
+ const loadSessionBackupPrivateKeyFromSecretStorage = crypto.loadSessionBackupPrivateKeyFromSecretStorage;
1579
+ if (typeof loadSessionBackupPrivateKeyFromSecretStorage !== "function") return false;
1580
+ try {
1581
+ await loadSessionBackupPrivateKeyFromSecretStorage.call(crypto);
1582
+ return false;
1583
+ } catch (err) {
1584
+ return isRepairableSecretStorageAccessError(err);
1585
+ }
1586
+ }
1587
+ async resolveRoomKeyBackupTrustState(crypto, fallbackVersion) {
1588
+ let serverVersion = fallbackVersion;
1589
+ let trusted = null;
1590
+ let matchesDecryptionKey = null;
1591
+ if (typeof crypto.getKeyBackupInfo === "function") {
1592
+ const info = await crypto.getKeyBackupInfo().catch(() => null);
1593
+ serverVersion = normalizeOptionalString$1(info?.version) ?? serverVersion;
1594
+ if (info && typeof crypto.isKeyBackupTrusted === "function") {
1595
+ const trustInfo = await crypto.isKeyBackupTrusted(info).catch(() => null);
1596
+ trusted = typeof trustInfo?.trusted === "boolean" ? trustInfo.trusted : null;
1597
+ matchesDecryptionKey = typeof trustInfo?.matchesDecryptionKey === "boolean" ? trustInfo.matchesDecryptionKey : null;
1598
+ }
1599
+ }
1600
+ return {
1601
+ serverVersion,
1602
+ trusted,
1603
+ matchesDecryptionKey
1604
+ };
1605
+ }
1606
+ async resolveDefaultSecretStorageKeyId(crypto) {
1607
+ const getSecretStorageStatus = crypto?.getSecretStorageStatus;
1608
+ if (typeof getSecretStorageStatus !== "function") return;
1609
+ return (await getSecretStorageStatus.call(crypto).catch(() => null))?.defaultKeyId;
1610
+ }
1611
+ async resolveRoomKeyBackupVersion() {
1612
+ try {
1613
+ return normalizeOptionalString$1((await this.doRequest("GET", "/_matrix/client/v3/room_keys/version")).version);
1614
+ } catch {
1615
+ return null;
1616
+ }
1617
+ }
1618
+ async enableTrustedRoomKeyBackupIfPossible(crypto) {
1619
+ if (typeof crypto.checkKeyBackupAndEnable !== "function") return;
1620
+ await crypto.checkKeyBackupAndEnable();
1621
+ }
1622
+ async ensureRoomKeyBackupEnabled(crypto) {
1623
+ if (await this.resolveRoomKeyBackupVersion()) return;
1624
+ LogService.info("MatrixClientLite", "No room key backup version found on server, creating one via secret storage bootstrap");
1625
+ await this.recoveryKeyStore.bootstrapSecretStorageWithRecoveryKey(crypto, { setupNewKeyBackup: true });
1626
+ const createdVersion = await this.resolveRoomKeyBackupVersion();
1627
+ if (!createdVersion) throw new Error("Matrix room key backup is still missing after bootstrap");
1628
+ LogService.info("MatrixClientLite", `Room key backup enabled (version ${createdVersion})`);
1629
+ }
1630
+ registerBridge() {
1631
+ if (this.bridgeRegistered || !this.decryptBridge) return;
1632
+ this.bridgeRegistered = true;
1633
+ const decryptBridge = this.decryptBridge;
1634
+ this.client.on(ClientEvent.Event, (event) => {
1635
+ const roomId = event.getRoomId();
1636
+ if (!roomId) return;
1637
+ const raw = matrixEventToRaw(event, { contentMode: "original" });
1638
+ const isEncryptedEvent = raw.type === "m.room.encrypted";
1639
+ this.emitter.emit("room.event", roomId, raw);
1640
+ if (isEncryptedEvent) this.emitter.emit("room.encrypted_event", roomId, raw);
1641
+ else if (decryptBridge.shouldEmitUnencryptedMessage(roomId, raw.event_id)) this.emitter.emit("room.message", roomId, raw);
1642
+ const stateKey = raw.state_key ?? "";
1643
+ const selfUserId = this.client.getUserId() ?? this.selfUserId ?? "";
1644
+ const membership = raw.type === "m.room.member" ? raw.content.membership : void 0;
1645
+ if (stateKey && selfUserId && stateKey === selfUserId) {
1646
+ if (membership === "invite") this.emitter.emit("room.invite", roomId, raw);
1647
+ else if (membership === "join") this.emitter.emit("room.join", roomId, raw);
1648
+ }
1649
+ if (isEncryptedEvent) decryptBridge.attachEncryptedEvent(event, roomId);
1650
+ });
1651
+ this.client.on(ClientEvent.Room, (room) => {
1652
+ this.emitMembershipForRoom(room);
1653
+ });
1654
+ this.client.on(ClientEvent.Sync, (state, prevState, data) => {
1655
+ this.currentSyncState = state;
1656
+ const error = data && typeof data === "object" && "error" in data ? data.error : void 0;
1657
+ this.emitter.emit("sync.state", state, prevState, error);
1658
+ });
1659
+ this.client.on(ClientEvent.SyncUnexpectedError, (error) => {
1660
+ this.emitter.emit("sync.unexpected_error", error);
1661
+ });
1662
+ }
1663
+ emitMembershipForRoom(room) {
1664
+ const roomObj = room;
1665
+ const roomId = roomObj.roomId?.trim();
1666
+ if (!roomId) return;
1667
+ const membership = roomObj.getMyMembership?.() ?? roomObj.selfMembership ?? void 0;
1668
+ const selfUserId = this.client.getUserId() ?? this.selfUserId ?? "";
1669
+ if (!selfUserId) return;
1670
+ const raw = {
1671
+ event_id: `$membership-${roomId}-${Date.now()}`,
1672
+ type: "m.room.member",
1673
+ sender: selfUserId,
1674
+ state_key: selfUserId,
1675
+ content: { membership },
1676
+ origin_server_ts: Date.now(),
1677
+ unsigned: { age: 0 }
1678
+ };
1679
+ if (membership === "invite") {
1680
+ this.emitter.emit("room.invite", roomId, raw);
1681
+ return;
1682
+ }
1683
+ if (membership === "join") this.emitter.emit("room.join", roomId, raw);
1684
+ }
1685
+ emitOutstandingInviteEvents() {
1686
+ const listRooms = this.client.getRooms;
1687
+ if (typeof listRooms !== "function") return;
1688
+ const rooms = listRooms.call(this.client);
1689
+ if (!Array.isArray(rooms)) return;
1690
+ for (const room of rooms) this.emitMembershipForRoom(room);
1691
+ }
1692
+ async refreshDmCache() {
1693
+ const direct = await this.getAccountData("m.direct");
1694
+ this.dmRoomIds.clear();
1695
+ if (!direct || typeof direct !== "object") return false;
1696
+ for (const value of Object.values(direct)) {
1697
+ if (!Array.isArray(value)) continue;
1698
+ for (const roomId of value) if (typeof roomId === "string" && roomId.trim()) this.dmRoomIds.add(roomId);
1699
+ }
1700
+ return true;
1701
+ }
1702
+ };
1703
+ //#endregion
1704
+ export { MATRIX_IDB_SNAPSHOT_LOCK_OPTIONS as i, sdk_exports as n, isRepairableSecretStorageAccessError as r, MatrixClient as t };