matrix-nodejs-homeserver 0.0.0-stage → 0.3.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 (141) hide show
  1. package/LICENSE +75 -0
  2. package/README.md +479 -2
  3. package/dist/adapters/auth.d.ts +50 -0
  4. package/dist/adapters/auth.js +18 -0
  5. package/dist/adapters/cluster.d.ts +55 -0
  6. package/dist/adapters/cluster.js +249 -0
  7. package/dist/adapters/media.d.ts +50 -0
  8. package/dist/adapters/media.js +63 -0
  9. package/dist/adapters/s3.d.ts +33 -0
  10. package/dist/adapters/s3.js +95 -0
  11. package/dist/api/account.js +594 -0
  12. package/dist/api/admin.js +267 -0
  13. package/dist/api/appservice.js +45 -0
  14. package/dist/api/backup.js +280 -0
  15. package/dist/api/federation.js +826 -0
  16. package/dist/api/filters.js +88 -0
  17. package/dist/api/index.js +47 -0
  18. package/dist/api/legacy.js +183 -0
  19. package/dist/api/media.js +271 -0
  20. package/dist/api/misc.js +95 -0
  21. package/dist/api/preview.js +201 -0
  22. package/dist/api/profile.js +176 -0
  23. package/dist/api/push.js +202 -0
  24. package/dist/api/relationships.js +270 -0
  25. package/dist/api/rooms.js +1487 -0
  26. package/dist/api/rtc.d.ts +23 -0
  27. package/dist/api/rtc.js +66 -0
  28. package/dist/api/search.js +161 -0
  29. package/dist/api/slidingsync.js +474 -0
  30. package/dist/api/spaces.js +177 -0
  31. package/dist/api/sync.js +609 -0
  32. package/dist/api/threadsubs.js +140 -0
  33. package/dist/api/threepid.js +155 -0
  34. package/dist/api/uia.js +127 -0
  35. package/dist/api/user.js +329 -0
  36. package/dist/core/admin.d.ts +84 -0
  37. package/dist/core/admin.js +308 -0
  38. package/dist/core/adminroom.d.ts +49 -0
  39. package/dist/core/adminroom.js +483 -0
  40. package/dist/core/appservices.d.ts +74 -0
  41. package/dist/core/appservices.js +369 -0
  42. package/dist/core/audit.d.ts +50 -0
  43. package/dist/core/audit.js +86 -0
  44. package/dist/core/auth.d.ts +48 -0
  45. package/dist/core/auth.js +237 -0
  46. package/dist/core/authrules.d.ts +4 -0
  47. package/dist/core/authrules.js +348 -0
  48. package/dist/core/cluster.d.ts +66 -0
  49. package/dist/core/cluster.js +191 -0
  50. package/dist/core/crypto.d.ts +10 -0
  51. package/dist/core/crypto.js +100 -0
  52. package/dist/core/delayed.d.ts +39 -0
  53. package/dist/core/delayed.js +160 -0
  54. package/dist/core/errors.d.ts +9 -0
  55. package/dist/core/errors.js +45 -0
  56. package/dist/core/events.d.ts +32 -0
  57. package/dist/core/events.js +186 -0
  58. package/dist/core/glob.js +66 -0
  59. package/dist/core/identity.d.ts +24 -0
  60. package/dist/core/identity.js +96 -0
  61. package/dist/core/ids.js +36 -0
  62. package/dist/core/invitefilter.js +36 -0
  63. package/dist/core/json.js +16 -0
  64. package/dist/core/keys.d.ts +36 -0
  65. package/dist/core/keys.js +450 -0
  66. package/dist/core/m.d.ts +209 -0
  67. package/dist/core/m.js +182 -0
  68. package/dist/core/notifier.d.ts +17 -0
  69. package/dist/core/notifier.js +54 -0
  70. package/dist/core/oauth.d.ts +38 -0
  71. package/dist/core/oauth.js +108 -0
  72. package/dist/core/partialstate.d.ts +32 -0
  73. package/dist/core/partialstate.js +198 -0
  74. package/dist/core/phone.js +268 -0
  75. package/dist/core/policy.d.ts +29 -0
  76. package/dist/core/policy.js +97 -0
  77. package/dist/core/profile.js +39 -0
  78. package/dist/core/push.d.ts +46 -0
  79. package/dist/core/push.js +307 -0
  80. package/dist/core/pushrules.js +229 -0
  81. package/dist/core/ratelimit.d.ts +23 -0
  82. package/dist/core/ratelimit.js +77 -0
  83. package/dist/core/rooms.d.ts +215 -0
  84. package/dist/core/rooms.js +1731 -0
  85. package/dist/core/settings.d.ts +11 -0
  86. package/dist/core/settings.js +29 -0
  87. package/dist/core/sso.d.ts +46 -0
  88. package/dist/core/sso.js +165 -0
  89. package/dist/core/stateres.d.ts +4 -0
  90. package/dist/core/stateres.js +209 -0
  91. package/dist/core/threepid.d.ts +80 -0
  92. package/dist/core/threepid.js +275 -0
  93. package/dist/core/typing.d.ts +16 -0
  94. package/dist/core/typing.js +60 -0
  95. package/dist/federation/client.d.ts +51 -0
  96. package/dist/federation/client.js +279 -0
  97. package/dist/federation/index.d.ts +153 -0
  98. package/dist/federation/index.js +1059 -0
  99. package/dist/federation/keyring.d.ts +26 -0
  100. package/dist/federation/keyring.js +122 -0
  101. package/dist/federation/sender.d.ts +26 -0
  102. package/dist/federation/sender.js +164 -0
  103. package/dist/homeserver.d.ts +224 -0
  104. package/dist/homeserver.js +302 -0
  105. package/dist/http/adapters.d.ts +40 -0
  106. package/dist/http/adapters.js +130 -0
  107. package/dist/http/handler.d.ts +8 -0
  108. package/dist/http/handler.js +181 -0
  109. package/dist/http/input.js +24 -0
  110. package/dist/http/procedure.js +25 -0
  111. package/dist/http/router.d.ts +43 -0
  112. package/dist/http/router.js +56 -0
  113. package/dist/index.d.ts +26 -0
  114. package/dist/index.js +15 -0
  115. package/dist/storage/memory.d.ts +5 -0
  116. package/dist/storage/memory.js +122 -0
  117. package/dist/storage/schema.d.ts +4 -0
  118. package/dist/storage/schema.js +664 -0
  119. package/dist/storage/sql.d.ts +72 -0
  120. package/dist/storage/sql.js +274 -0
  121. package/dist/storage/types.d.ts +55 -0
  122. package/dist/vendor/zod/LICENSE +21 -0
  123. package/dist/vendor/zod/v4/classic/errors.js +54 -0
  124. package/dist/vendor/zod/v4/classic/parse.js +17 -0
  125. package/dist/vendor/zod/v4/classic/schemas.js +865 -0
  126. package/dist/vendor/zod/v4/core/api.js +512 -0
  127. package/dist/vendor/zod/v4/core/checks.js +300 -0
  128. package/dist/vendor/zod/v4/core/core.js +109 -0
  129. package/dist/vendor/zod/v4/core/doc.js +35 -0
  130. package/dist/vendor/zod/v4/core/errors.js +118 -0
  131. package/dist/vendor/zod/v4/core/json-schema-processors.js +428 -0
  132. package/dist/vendor/zod/v4/core/memoizer.js +255 -0
  133. package/dist/vendor/zod/v4/core/parse.js +206 -0
  134. package/dist/vendor/zod/v4/core/regexes.js +73 -0
  135. package/dist/vendor/zod/v4/core/registries.js +48 -0
  136. package/dist/vendor/zod/v4/core/schemas.js +1378 -0
  137. package/dist/vendor/zod/v4/core/to-json-schema.js +448 -0
  138. package/dist/vendor/zod/v4/core/util.js +537 -0
  139. package/dist/vendor/zod/v4/core/versions.js +8 -0
  140. package/dist/vendor/zod/v4/locales/en.js +110 -0
  141. package/package.json +65 -4
@@ -0,0 +1,450 @@
1
+ import { isObject } from "./json.js";
2
+ import { UNSTABLE } from "./m.js";
3
+ import { errors } from "./errors.js";
4
+ //#region src/core/keys.ts
5
+ /** End-to-end encryption key storage (device keys, one-time keys, cross-signing). */
6
+ var KeyService = class {
7
+ hs;
8
+ constructor(hs) {
9
+ this.hs = hs;
10
+ }
11
+ /** A local user's devices or device keys changed: update local clients and remote servers. */
12
+ async deviceChanged(userId, deletedDeviceId) {
13
+ await this.markDeviceListChanged(userId);
14
+ await this.hs.federation.onDeviceListChange(userId, deletedDeviceId).catch((err) => this.hs.logger.warn("Device list EDU failed", err));
15
+ }
16
+ async markDeviceListChanged(userId, notify = true) {
17
+ await this.hs.streams.write((stream) => this.hs.db.upsert("device_list_changes", {
18
+ user_id: userId,
19
+ stream
20
+ }));
21
+ if (notify) this.hs.notifier.notify(await this.hs.rooms.usersSharingRooms(userId));
22
+ }
23
+ /**
24
+ * A partial-state room got its full state (MSC3902): device list updates received meanwhile for
25
+ * users we could not place count as changes now, if those users turned out to be members.
26
+ */
27
+ async onPartialStateComplete(roomId, pendingUsers) {
28
+ const members = new Set(await this.hs.rooms.joinedMembers(roomId));
29
+ for (const userId of pendingUsers) if (members.has(userId)) await this.markDeviceListChanged(userId, false);
30
+ this.hs.notifier.notify([...members].filter((u) => this.hs.isLocalUser(u)));
31
+ }
32
+ async upload(userId, deviceId, body) {
33
+ if (body.device_keys !== void 0) {
34
+ const dk = body.device_keys;
35
+ if (!isObject(dk) || dk.user_id !== userId || dk.device_id !== deviceId) throw errors.invalidParam("device_keys do not match this device");
36
+ if (!Array.isArray(dk.algorithms) || !isObject(dk.keys) || !isObject(dk.signatures)) throw errors.badJson("device_keys must contain algorithms, keys and signatures");
37
+ const existing = await this.hs.db.findOne("device_keys", [{
38
+ field: "user_id",
39
+ value: userId
40
+ }, {
41
+ field: "device_id",
42
+ value: deviceId
43
+ }]);
44
+ if (!existing || JSON.stringify(existing.keys) !== JSON.stringify(dk)) {
45
+ await this.hs.streams.write((stream) => this.hs.db.upsert("device_keys", {
46
+ user_id: userId,
47
+ device_id: deviceId,
48
+ keys: dk,
49
+ stream
50
+ }));
51
+ await this.deviceChanged(userId);
52
+ }
53
+ }
54
+ const device = [{
55
+ field: "user_id",
56
+ value: userId
57
+ }, {
58
+ field: "device_id",
59
+ value: deviceId
60
+ }];
61
+ if (isObject(body.one_time_keys)) for (const [fullId, key] of Object.entries(body.one_time_keys)) {
62
+ const [algorithm] = fullId.split(":");
63
+ const existing = await this.hs.db.findOne("one_time_keys", [...device, {
64
+ field: "key_id",
65
+ value: fullId
66
+ }]);
67
+ if (existing) {
68
+ if (JSON.stringify(existing.key) !== JSON.stringify(key)) throw errors.invalidParam(`One time key ${fullId} already exists`);
69
+ continue;
70
+ }
71
+ await this.hs.streams.write((seq) => this.hs.db.create("one_time_keys", {
72
+ user_id: userId,
73
+ device_id: deviceId,
74
+ key_id: fullId,
75
+ algorithm,
76
+ key,
77
+ fallback: false,
78
+ used: false,
79
+ seq
80
+ }));
81
+ }
82
+ const fallbackKeys = body.fallback_keys ?? body[UNSTABLE.ORG.MATRIX.MSC2732.FALLBACK_KEYS];
83
+ if (isObject(fallbackKeys)) for (const [fullId, key] of Object.entries(fallbackKeys)) {
84
+ const [algorithm] = fullId.split(":");
85
+ const existing = await this.hs.db.findOne("fallback_keys", [...device, {
86
+ field: "algorithm",
87
+ value: algorithm
88
+ }]);
89
+ if (existing && existing.key_id === fullId && JSON.stringify(existing.key) === JSON.stringify(key)) continue;
90
+ await this.hs.db.upsert("fallback_keys", {
91
+ user_id: userId,
92
+ device_id: deviceId,
93
+ algorithm,
94
+ key_id: fullId,
95
+ key,
96
+ used: false
97
+ });
98
+ }
99
+ return this.oneTimeKeyCounts(userId, deviceId);
100
+ }
101
+ async oneTimeKeyCounts(userId, deviceId) {
102
+ const rows = await this.hs.db.findMany("one_time_keys", { where: [
103
+ {
104
+ field: "user_id",
105
+ value: userId
106
+ },
107
+ {
108
+ field: "device_id",
109
+ value: deviceId
110
+ },
111
+ {
112
+ field: "fallback",
113
+ value: false
114
+ }
115
+ ] });
116
+ const counts = { signed_curve25519: 0 };
117
+ for (const r of rows) counts[r.algorithm] = (counts[r.algorithm] ?? 0) + 1;
118
+ return counts;
119
+ }
120
+ async unusedFallbackKeyTypes(userId, deviceId) {
121
+ const rows = await this.hs.db.findMany("fallback_keys", { where: [
122
+ {
123
+ field: "user_id",
124
+ value: userId
125
+ },
126
+ {
127
+ field: "device_id",
128
+ value: deviceId
129
+ },
130
+ {
131
+ field: "used",
132
+ value: false
133
+ }
134
+ ] });
135
+ return [...new Set(rows.map((r) => r.algorithm))];
136
+ }
137
+ async query(requester, deviceKeys, timeoutMs = 1e4) {
138
+ const result = {
139
+ device_keys: {},
140
+ failures: {},
141
+ master_keys: {},
142
+ self_signing_keys: {},
143
+ user_signing_keys: {}
144
+ };
145
+ const remote = {};
146
+ for (const [userId, devices] of Object.entries(deviceKeys ?? {})) {
147
+ if (!this.hs.isLocalUser(userId)) {
148
+ remote[userId] = devices;
149
+ continue;
150
+ }
151
+ const rows = await this.hs.db.findMany("device_keys", { where: [{
152
+ field: "user_id",
153
+ value: userId
154
+ }] });
155
+ const deviceRows = await this.hs.db.findMany("devices", { where: [{
156
+ field: "user_id",
157
+ value: userId
158
+ }] });
159
+ const names = new Map(deviceRows.map((d) => [d.device_id, d.display_name]));
160
+ const wanted = Array.isArray(devices) && devices.length ? new Set(devices) : null;
161
+ const out = {};
162
+ for (const row of rows) {
163
+ if (wanted && !wanted.has(row.device_id)) continue;
164
+ if (!names.has(row.device_id)) continue;
165
+ const keys = await this.withSignatures(userId, `${row.device_id}`, row.keys);
166
+ const name = names.get(row.device_id);
167
+ keys.unsigned = {
168
+ ...keys.unsigned,
169
+ ...name ? { device_display_name: name } : {}
170
+ };
171
+ out[row.device_id] = keys;
172
+ }
173
+ result.device_keys[userId] = out;
174
+ const csRows = await this.hs.db.findMany("cross_signing_keys", { where: [{
175
+ field: "user_id",
176
+ value: userId
177
+ }] });
178
+ for (const cs of csRows) {
179
+ if (cs.key_type === "user_signing" && userId !== requester) continue;
180
+ const pub = Object.values(cs.key.keys ?? {})[0];
181
+ result[`${cs.key_type}_keys`][userId] = await this.withSignatures(userId, pub, cs.key);
182
+ }
183
+ }
184
+ if (Object.keys(remote).length && requester) {
185
+ const fetch = {};
186
+ const tracked = /* @__PURE__ */ new Set();
187
+ for (const [userId, devices] of Object.entries(remote)) {
188
+ if (!await this.isTracked(userId)) {
189
+ await this.dropRemoteCache(userId);
190
+ fetch[userId] = devices;
191
+ continue;
192
+ }
193
+ tracked.add(userId);
194
+ const list = await this.hs.db.findOne("remote_device_lists", [{
195
+ field: "user_id",
196
+ value: userId
197
+ }]);
198
+ if (!list || list.stale) {
199
+ fetch[userId] = [];
200
+ continue;
201
+ }
202
+ await this.fromRemoteCache(userId, devices, result);
203
+ }
204
+ if (Object.keys(fetch).length) {
205
+ const { results, failures } = await this.hs.federation.queryKeys(fetch, timeoutMs);
206
+ Object.assign(result.failures, failures);
207
+ const fetched = {
208
+ device_keys: {},
209
+ master_keys: {},
210
+ self_signing_keys: {}
211
+ };
212
+ for (const res of results) for (const kind of [
213
+ "device_keys",
214
+ "master_keys",
215
+ "self_signing_keys"
216
+ ]) for (const [userId, value] of Object.entries(isObject(res[kind]) ? res[kind] : {})) if (fetch[userId] !== void 0) fetched[kind][userId] = value;
217
+ for (const userId of Object.keys(fetch)) {
218
+ if (tracked.has(userId) && isObject(fetched.device_keys[userId])) {
219
+ await this.storeRemoteCache(userId, fetched.device_keys[userId], fetched.master_keys[userId], fetched.self_signing_keys[userId]);
220
+ await this.fromRemoteCache(userId, remote[userId], result);
221
+ continue;
222
+ }
223
+ for (const kind of [
224
+ "device_keys",
225
+ "master_keys",
226
+ "self_signing_keys"
227
+ ]) if (fetched[kind][userId] !== void 0) result[kind][userId] = fetched[kind][userId];
228
+ }
229
+ }
230
+ }
231
+ return result;
232
+ }
233
+ /** Whether a remote user shares a room with a local user (by known membership). */
234
+ async isTracked(userId) {
235
+ const rows = await this.hs.db.findMany("memberships", { where: [{
236
+ field: "user_id",
237
+ value: userId
238
+ }, {
239
+ field: "membership",
240
+ value: "join"
241
+ }] });
242
+ for (const row of rows) if ((await this.hs.rooms.joinedMembers(row.room_id)).some((u) => this.hs.isLocalUser(u))) return true;
243
+ return false;
244
+ }
245
+ async dropRemoteCache(userId) {
246
+ await this.hs.db.delete("remote_devices", [{
247
+ field: "user_id",
248
+ value: userId
249
+ }]);
250
+ await this.hs.db.delete("remote_device_lists", [{
251
+ field: "user_id",
252
+ value: userId
253
+ }]);
254
+ }
255
+ /** A tracked user's device list changed: refetch it on the next query. */
256
+ async markRemoteStale(userId) {
257
+ if (await this.isTracked(userId)) await this.hs.db.upsert("remote_device_lists", {
258
+ user_id: userId,
259
+ stream_id: 0,
260
+ stale: true
261
+ });
262
+ else await this.dropRemoteCache(userId);
263
+ }
264
+ async storeRemoteCache(userId, devices, master, selfSigning) {
265
+ await this.hs.db.delete("remote_devices", [{
266
+ field: "user_id",
267
+ value: userId
268
+ }]);
269
+ for (const [deviceId, keys] of Object.entries(devices)) {
270
+ if (!isObject(keys)) continue;
271
+ const name = keys.unsigned?.device_display_name;
272
+ await this.hs.db.upsert("remote_devices", {
273
+ user_id: userId,
274
+ device_id: deviceId,
275
+ keys,
276
+ display_name: typeof name === "string" ? name : null,
277
+ stream_id: 0
278
+ });
279
+ }
280
+ for (const [type, key] of [["master", master], ["self_signing", selfSigning]]) if (isObject(key)) await this.hs.streams.write((stream) => this.hs.db.upsert("cross_signing_keys", {
281
+ user_id: userId,
282
+ key_type: type,
283
+ key,
284
+ stream
285
+ }));
286
+ await this.hs.db.upsert("remote_device_lists", {
287
+ user_id: userId,
288
+ stream_id: 0,
289
+ stale: false
290
+ });
291
+ }
292
+ async fromRemoteCache(userId, devices, result) {
293
+ const wanted = Array.isArray(devices) && devices.length ? new Set(devices) : null;
294
+ const out = {};
295
+ for (const row of await this.hs.db.findMany("remote_devices", { where: [{
296
+ field: "user_id",
297
+ value: userId
298
+ }] })) if (!wanted || wanted.has(row.device_id)) out[row.device_id] = row.keys;
299
+ result.device_keys[userId] = out;
300
+ for (const cs of await this.hs.db.findMany("cross_signing_keys", { where: [{
301
+ field: "user_id",
302
+ value: userId
303
+ }] })) if (cs.key_type === "master" || cs.key_type === "self_signing") result[`${cs.key_type}_keys`][userId] = cs.key;
304
+ }
305
+ async withSignatures(userId, keyId, key) {
306
+ const sigs = await this.hs.db.findMany("key_signatures", { where: [{
307
+ field: "user_id",
308
+ value: userId
309
+ }, {
310
+ field: "key_id",
311
+ value: keyId
312
+ }] });
313
+ if (sigs.length === 0) return structuredClone(key);
314
+ const out = structuredClone(key);
315
+ out.signatures ??= {};
316
+ for (const s of sigs) out.signatures[s.signer] = {
317
+ ...out.signatures[s.signer],
318
+ [s.signer_key_id]: s.signature
319
+ };
320
+ return out;
321
+ }
322
+ async claim(request) {
323
+ const result = {};
324
+ for (const [userId, devices] of Object.entries(request ?? {})) for (const [deviceId, algorithm] of Object.entries(devices ?? {})) {
325
+ const base = [
326
+ {
327
+ field: "user_id",
328
+ value: userId
329
+ },
330
+ {
331
+ field: "device_id",
332
+ value: deviceId
333
+ },
334
+ {
335
+ field: "algorithm",
336
+ value: algorithm
337
+ }
338
+ ];
339
+ let key = (await this.hs.db.findMany("one_time_keys", {
340
+ where: [...base, {
341
+ field: "fallback",
342
+ value: false
343
+ }],
344
+ sortBy: {
345
+ field: "seq",
346
+ direction: "asc"
347
+ },
348
+ limit: 1
349
+ }))[0] ?? null;
350
+ if (key) await this.hs.db.delete("one_time_keys", [...base, {
351
+ field: "key_id",
352
+ value: key.key_id
353
+ }]);
354
+ else {
355
+ key = await this.hs.db.findOne("fallback_keys", base);
356
+ if (key && !key.used) await this.hs.db.update("fallback_keys", base, { used: true });
357
+ }
358
+ if (!key) continue;
359
+ result[userId] ??= {};
360
+ result[userId][deviceId] = { [key.key_id]: key.key };
361
+ }
362
+ return result;
363
+ }
364
+ async uploadCrossSigning(userId, body) {
365
+ for (const type of [
366
+ "master",
367
+ "self_signing",
368
+ "user_signing"
369
+ ]) {
370
+ const key = body[`${type}_key`];
371
+ if (key === void 0) continue;
372
+ if (!isObject(key) || key.user_id !== userId || !isObject(key.keys)) throw errors.invalidParam(`Invalid ${type}_key`);
373
+ await this.hs.streams.write((stream) => this.hs.db.upsert("cross_signing_keys", {
374
+ user_id: userId,
375
+ key_type: type,
376
+ key,
377
+ stream
378
+ }));
379
+ }
380
+ await this.markDeviceListChanged(userId);
381
+ await this.hs.federation.onSigningKeyUpdate(userId).catch(() => {});
382
+ }
383
+ async uploadSignatures(signer, body) {
384
+ const failures = {};
385
+ const changed = /* @__PURE__ */ new Set();
386
+ for (const [userId, keys] of Object.entries(body ?? {})) {
387
+ if (!isObject(keys)) continue;
388
+ for (const [keyId, signed] of Object.entries(keys)) {
389
+ const sigs = signed?.signatures?.[signer];
390
+ if (!isObject(sigs)) {
391
+ failures[userId] = {
392
+ ...failures[userId],
393
+ [keyId]: {
394
+ errcode: "M_INVALID_SIGNATURE",
395
+ error: "No signature from signer"
396
+ }
397
+ };
398
+ continue;
399
+ }
400
+ for (const [signerKeyId, signature] of Object.entries(sigs)) await this.hs.db.upsert("key_signatures", {
401
+ user_id: userId,
402
+ key_id: keyId,
403
+ signer,
404
+ signer_key_id: signerKeyId,
405
+ signature: String(signature)
406
+ });
407
+ changed.add(userId);
408
+ }
409
+ }
410
+ for (const u of changed) if (this.hs.isLocalUser(u)) await this.deviceChanged(u);
411
+ else await this.markDeviceListChanged(u);
412
+ if (changed.has(signer)) await this.hs.federation.onSigningKeyUpdate(signer).catch(() => {});
413
+ return { failures };
414
+ }
415
+ /** Users (sharing a room with `userId`) whose device lists changed after `since`. */
416
+ async changesSince(userId, since, upTo) {
417
+ const sharing = await this.hs.rooms.usersSharingRooms(userId);
418
+ const rows = await this.hs.db.findMany("device_list_changes", { where: [{
419
+ field: "stream",
420
+ operator: "gt",
421
+ value: since
422
+ }] });
423
+ const changed = /* @__PURE__ */ new Set();
424
+ for (const r of rows) if (r.stream <= upTo && sharing.has(r.user_id)) changed.add(r.user_id);
425
+ const left = /* @__PURE__ */ new Set();
426
+ const memberRows = await this.hs.db.findMany("memberships", { where: [{
427
+ field: "stream",
428
+ operator: "gt",
429
+ value: since
430
+ }] });
431
+ const myRooms = /* @__PURE__ */ new Set([...await this.hs.rooms.roomsForUser(userId), ...memberRows.filter((m) => m.user_id === userId).map((m) => m.room_id)]);
432
+ for (const m of memberRows) {
433
+ if (m.stream > upTo || !myRooms.has(m.room_id)) continue;
434
+ if (m.user_id === userId) {
435
+ const members = await this.hs.rooms.joinedMembers(m.room_id);
436
+ if (m.membership === "join") {
437
+ for (const u of members) if (u !== userId) changed.add(u);
438
+ } else for (const u of members) if (!sharing.has(u)) left.add(u);
439
+ } else if (m.membership === "join") changed.add(m.user_id);
440
+ else if (!sharing.has(m.user_id)) left.add(m.user_id);
441
+ }
442
+ for (const u of left) changed.delete(u);
443
+ return {
444
+ changed: [...changed],
445
+ left: [...left]
446
+ };
447
+ }
448
+ };
449
+ //#endregion
450
+ export { KeyService };
@@ -0,0 +1,209 @@
1
+ //#region src/core/m.d.ts
2
+ export declare const M: {
3
+ readonly BEACON_INFO: "m.beacon_info";
4
+ readonly CALL: {
5
+ readonly INVITE: "m.call.invite";
6
+ };
7
+ readonly CHANGE_PASSWORD: "m.change_password";
8
+ readonly DEVICE_LIST_UPDATE: "m.device_list_update";
9
+ readonly DIRECT: "m.direct";
10
+ readonly DIRECT_TO_DEVICE: "m.direct_to_device";
11
+ readonly FEDERATE: "m.federate";
12
+ readonly FULLY_READ: "m.fully_read";
13
+ readonly GET_LOGIN_TOKEN: "m.get_login_token";
14
+ readonly HEROES: "m.heroes";
15
+ readonly HOMESERVER: "m.homeserver";
16
+ readonly ID: {
17
+ readonly PHONE: "m.id.phone";
18
+ readonly THIRDPARTY: "m.id.thirdparty";
19
+ readonly USER: "m.id.user";
20
+ };
21
+ readonly IGNORED_USER_LIST: "m.ignored_user_list";
22
+ readonly IN_REPLY_TO: "m.in_reply_to";
23
+ readonly INVITED_MEMBER_COUNT: "m.invited_member_count";
24
+ readonly JOINED_MEMBER_COUNT: "m.joined_member_count";
25
+ readonly LOGIN: {
26
+ readonly APPLICATION_SERVICE: "m.login.application_service";
27
+ readonly DUMMY: "m.login.dummy";
28
+ readonly EMAIL: {
29
+ readonly IDENTITY: "m.login.email.identity";
30
+ };
31
+ readonly MSISDN: "m.login.msisdn";
32
+ readonly PASSWORD: "m.login.password";
33
+ readonly REGISTRATION_TOKEN: "m.login.registration_token";
34
+ readonly SSO: "m.login.sso";
35
+ readonly TOKEN: "m.login.token";
36
+ };
37
+ readonly NOTICE: "m.notice";
38
+ readonly OAUTH: "m.oauth";
39
+ readonly POLL: {
40
+ readonly START: "m.poll.start";
41
+ };
42
+ readonly PRESENCE: "m.presence";
43
+ readonly PROFILE_FIELDS: "m.profile_fields";
44
+ readonly PUSH_RULES: "m.push_rules";
45
+ readonly REACTION: "m.reaction";
46
+ readonly READ: {
47
+ readonly _: "m.read";
48
+ readonly PRIVATE: "m.read.private";
49
+ };
50
+ readonly RECEIPT: "m.receipt";
51
+ readonly RELATES_TO: "m.relates_to";
52
+ readonly RELATIONS: "m.relations";
53
+ readonly RELATIONSHIP: "m.relationship";
54
+ readonly REPLACE: "m.replace";
55
+ readonly ROLE: {
56
+ readonly ADMIN: "m.role.admin";
57
+ readonly SECURITY: "m.role.security";
58
+ };
59
+ readonly ROOM: {
60
+ readonly ALIASES: "m.room.aliases";
61
+ readonly AVATAR: "m.room.avatar";
62
+ readonly CANONICAL_ALIAS: "m.room.canonical_alias";
63
+ readonly CREATE: "m.room.create";
64
+ readonly ENCRYPTED: "m.room.encrypted";
65
+ readonly ENCRYPTION: "m.room.encryption";
66
+ readonly GUEST_ACCESS: "m.room.guest_access";
67
+ readonly HISTORY_VISIBILITY: "m.room.history_visibility";
68
+ readonly JOIN_RULES: "m.room.join_rules";
69
+ readonly MEMBER: "m.room.member";
70
+ readonly MESSAGE: "m.room.message";
71
+ readonly NAME: "m.room.name";
72
+ readonly POLICY: "m.room.policy";
73
+ readonly POWER_LEVELS: "m.room.power_levels";
74
+ readonly REDACTION: "m.room.redaction";
75
+ readonly SERVER_ACL: "m.room.server_acl";
76
+ readonly THIRD_PARTY_INVITE: "m.room.third_party_invite";
77
+ readonly TOMBSTONE: "m.room.tombstone";
78
+ readonly TOPIC: "m.room.topic";
79
+ };
80
+ readonly ROOM_MEMBERSHIP: "m.room_membership";
81
+ readonly ROOM_VERSIONS: "m.room_versions";
82
+ readonly RULE: {
83
+ readonly CALL: ".m.rule.call";
84
+ readonly CONTAINS_DISPLAY_NAME: ".m.rule.contains_display_name";
85
+ readonly CONTAINS_USER_NAME: ".m.rule.contains_user_name";
86
+ readonly ENCRYPTED: ".m.rule.encrypted";
87
+ readonly ENCRYPTED_ROOM_ONE_TO_ONE: ".m.rule.encrypted_room_one_to_one";
88
+ readonly INVITE_FOR_ME: ".m.rule.invite_for_me";
89
+ readonly IS_ROOM_MENTION: ".m.rule.is_room_mention";
90
+ readonly IS_USER_MENTION: ".m.rule.is_user_mention";
91
+ readonly MASTER: ".m.rule.master";
92
+ readonly MEMBER_EVENT: ".m.rule.member_event";
93
+ readonly MESSAGE: ".m.rule.message";
94
+ readonly REACTION: ".m.rule.reaction";
95
+ readonly ROOM: {
96
+ readonly SERVER_ACL: ".m.rule.room.server_acl";
97
+ };
98
+ readonly ROOM_ONE_TO_ONE: ".m.rule.room_one_to_one";
99
+ readonly ROOMNOTIF: ".m.rule.roomnotif";
100
+ readonly SUPPRESS_EDITS: ".m.rule.suppress_edits";
101
+ readonly SUPPRESS_NOTICES: ".m.rule.suppress_notices";
102
+ readonly TOMBSTONE: ".m.rule.tombstone";
103
+ };
104
+ readonly SERVER: "m.server";
105
+ readonly SERVER_NOTICE: "m.server_notice";
106
+ readonly SET_AVATAR_URL: "m.set_avatar_url";
107
+ readonly SET_DISPLAYNAME: "m.set_displayname";
108
+ readonly SIGNING_KEY_UPDATE: "m.signing_key_update";
109
+ readonly SPACE: {
110
+ readonly _: "m.space";
111
+ readonly CHILD: "m.space.child";
112
+ };
113
+ readonly STICKER: "m.sticker";
114
+ readonly TAG: "m.tag";
115
+ readonly TEXT: "m.text";
116
+ readonly THREAD: "m.thread";
117
+ readonly THREEPID_CHANGES: "m.3pid_changes";
118
+ readonly TOPIC: "m.topic";
119
+ readonly TYPING: "m.typing";
120
+ readonly UPLOAD: {
121
+ readonly SIZE: "m.upload.size";
122
+ };
123
+ };
124
+ export declare const UNSTABLE: {
125
+ readonly DE: {
126
+ readonly SORUNOME: {
127
+ readonly MSC2409: {
128
+ readonly EPHEMERAL: "de.sorunome.msc2409.ephemeral";
129
+ readonly PUSH_EPHEMERAL: "de.sorunome.msc2409.push_ephemeral";
130
+ };
131
+ };
132
+ };
133
+ readonly IO: {
134
+ readonly ELEMENT: {
135
+ readonly MSC4306: {
136
+ readonly RULE: {
137
+ readonly SUBSCRIBED_THREAD: ".io.element.msc4306.rule.subscribed_thread";
138
+ readonly UNSUBSCRIBED_THREAD: ".io.element.msc4306.rule.unsubscribed_thread";
139
+ };
140
+ readonly THREAD_SUBSCRIPTION: "io.element.msc4306.thread_subscription";
141
+ };
142
+ readonly MSC4308: {
143
+ readonly THREAD_SUBSCRIPTIONS: "io.element.msc4308.thread_subscriptions";
144
+ };
145
+ };
146
+ };
147
+ readonly ORG: {
148
+ readonly MATRIX: {
149
+ readonly CROSS_SIGNING_RESET: "org.matrix.cross_signing_reset";
150
+ readonly CUSTOM: {
151
+ readonly HTML: "org.matrix.custom.html";
152
+ };
153
+ readonly MSC2732: {
154
+ readonly FALLBACK_KEYS: "org.matrix.msc2732.fallback_keys";
155
+ };
156
+ readonly MSC3202: {
157
+ readonly DEVICE_ID: "org.matrix.msc3202.device_id";
158
+ };
159
+ readonly MSC3381: {
160
+ readonly POLL: {
161
+ readonly END: "org.matrix.msc3381.poll.end";
162
+ readonly RESPONSE: "org.matrix.msc3381.poll.response";
163
+ readonly START: "org.matrix.msc3381.poll.start";
164
+ };
165
+ };
166
+ readonly MSC3874: {
167
+ readonly NOT_REL_TYPES: "org.matrix.msc3874.not_rel_types";
168
+ readonly REL_TYPES: "org.matrix.msc3874.rel_types";
169
+ };
170
+ readonly MSC3916: {
171
+ readonly STABLE: "org.matrix.msc3916.stable";
172
+ };
173
+ readonly MSC3930: {
174
+ readonly RULE: {
175
+ readonly POLL_END: ".org.matrix.msc3930.rule.poll_end";
176
+ readonly POLL_END_ONE_TO_ONE: ".org.matrix.msc3930.rule.poll_end_one_to_one";
177
+ readonly POLL_RESPONSE: ".org.matrix.msc3930.rule.poll_response";
178
+ readonly POLL_START: ".org.matrix.msc3930.rule.poll_start";
179
+ readonly POLL_START_ONE_TO_ONE: ".org.matrix.msc3930.rule.poll_start_one_to_one";
180
+ };
181
+ };
182
+ readonly MSC4140: {
183
+ readonly _: "org.matrix.msc4140";
184
+ readonly DELAY: "org.matrix.msc4140.delay";
185
+ readonly MAX_DELAY: "org.matrix.msc4140.max_delay";
186
+ };
187
+ readonly MSC4143: {
188
+ readonly _: "org.matrix.msc4143";
189
+ readonly RTC_FOCI: "org.matrix.msc4143.rtc_foci";
190
+ };
191
+ readonly MSC4155: {
192
+ readonly ERRCODE: "org.matrix.msc4155.errcode";
193
+ readonly INVITE_PERMISSION_CONFIG: "org.matrix.msc4155.invite_permission_config";
194
+ };
195
+ readonly MSC4222: {
196
+ readonly _: "org.matrix.msc4222";
197
+ readonly STATE_AFTER: "org.matrix.msc4222.state_after";
198
+ readonly USE_STATE_AFTER: "org.matrix.msc4222.use_state_after";
199
+ };
200
+ readonly MSC4306: "org.matrix.msc4306";
201
+ readonly MSC4429: {
202
+ readonly PROFILE_FIELDS: "org.matrix.msc4429.profile_fields";
203
+ readonly USERS: "org.matrix.msc4429.users";
204
+ };
205
+ readonly SIMPLIFIED_MSC3575: "org.matrix.simplified_msc3575";
206
+ };
207
+ };
208
+ };
209
+ //#endregion