matrix-nodejs-homeserver 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/LICENSE +75 -0
  2. package/README.md +457 -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 +560 -0
  12. package/dist/api/admin.js +251 -0
  13. package/dist/api/appservice.js +33 -0
  14. package/dist/api/backup.js +269 -0
  15. package/dist/api/federation.js +792 -0
  16. package/dist/api/filters.js +87 -0
  17. package/dist/api/index.js +47 -0
  18. package/dist/api/legacy.js +170 -0
  19. package/dist/api/media.js +255 -0
  20. package/dist/api/misc.js +86 -0
  21. package/dist/api/preview.js +200 -0
  22. package/dist/api/profile.js +165 -0
  23. package/dist/api/push.js +187 -0
  24. package/dist/api/relationships.js +264 -0
  25. package/dist/api/rooms.js +1364 -0
  26. package/dist/api/rtc.d.ts +24 -0
  27. package/dist/api/rtc.js +52 -0
  28. package/dist/api/search.js +155 -0
  29. package/dist/api/slidingsync.js +464 -0
  30. package/dist/api/spaces.js +172 -0
  31. package/dist/api/sync.js +600 -0
  32. package/dist/api/threadsubs.js +129 -0
  33. package/dist/api/threepid.js +122 -0
  34. package/dist/api/uia.js +126 -0
  35. package/dist/api/user.js +286 -0
  36. package/dist/core/admin.d.ts +85 -0
  37. package/dist/core/admin.js +307 -0
  38. package/dist/core/adminroom.d.ts +49 -0
  39. package/dist/core/adminroom.js +482 -0
  40. package/dist/core/appservices.d.ts +73 -0
  41. package/dist/core/appservices.js +368 -0
  42. package/dist/core/audit.d.ts +51 -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 +347 -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 +159 -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 +185 -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 +35 -0
  63. package/dist/core/json.js +16 -0
  64. package/dist/core/keys.d.ts +36 -0
  65. package/dist/core/keys.js +449 -0
  66. package/dist/core/notifier.d.ts +17 -0
  67. package/dist/core/notifier.js +54 -0
  68. package/dist/core/oauth.d.ts +38 -0
  69. package/dist/core/oauth.js +108 -0
  70. package/dist/core/partialstate.d.ts +32 -0
  71. package/dist/core/partialstate.js +197 -0
  72. package/dist/core/phone.js +268 -0
  73. package/dist/core/policy.d.ts +29 -0
  74. package/dist/core/policy.js +96 -0
  75. package/dist/core/profile.js +39 -0
  76. package/dist/core/push.d.ts +47 -0
  77. package/dist/core/push.js +306 -0
  78. package/dist/core/pushrules.d.ts +1 -0
  79. package/dist/core/pushrules.js +228 -0
  80. package/dist/core/ratelimit.d.ts +23 -0
  81. package/dist/core/ratelimit.js +77 -0
  82. package/dist/core/rooms.d.ts +215 -0
  83. package/dist/core/rooms.js +1729 -0
  84. package/dist/core/settings.d.ts +11 -0
  85. package/dist/core/settings.js +29 -0
  86. package/dist/core/sso.d.ts +46 -0
  87. package/dist/core/sso.js +165 -0
  88. package/dist/core/stateres.d.ts +4 -0
  89. package/dist/core/stateres.js +208 -0
  90. package/dist/core/threepid.d.ts +80 -0
  91. package/dist/core/threepid.js +275 -0
  92. package/dist/core/typing.d.ts +16 -0
  93. package/dist/core/typing.js +60 -0
  94. package/dist/federation/client.d.ts +51 -0
  95. package/dist/federation/client.js +278 -0
  96. package/dist/federation/index.d.ts +153 -0
  97. package/dist/federation/index.js +1058 -0
  98. package/dist/federation/keyring.d.ts +26 -0
  99. package/dist/federation/keyring.js +122 -0
  100. package/dist/federation/sender.d.ts +26 -0
  101. package/dist/federation/sender.js +164 -0
  102. package/dist/homeserver.d.ts +223 -0
  103. package/dist/homeserver.js +301 -0
  104. package/dist/http/adapters.d.ts +40 -0
  105. package/dist/http/adapters.js +130 -0
  106. package/dist/http/handler.d.ts +8 -0
  107. package/dist/http/handler.js +202 -0
  108. package/dist/http/input.js +24 -0
  109. package/dist/http/router.d.ts +60 -0
  110. package/dist/http/router.js +76 -0
  111. package/dist/index.d.ts +25 -0
  112. package/dist/index.js +14 -0
  113. package/dist/storage/memory.d.ts +5 -0
  114. package/dist/storage/memory.js +122 -0
  115. package/dist/storage/schema.d.ts +4 -0
  116. package/dist/storage/schema.js +664 -0
  117. package/dist/storage/sql.d.ts +72 -0
  118. package/dist/storage/sql.js +274 -0
  119. package/dist/storage/types.d.ts +55 -0
  120. package/package.json +67 -4
@@ -0,0 +1,264 @@
1
+ import { isObject } from "../core/json.js";
2
+ import { toClientEvent } from "../core/events.js";
3
+ import { errors } from "../core/errors.js";
4
+ import { getState } from "../core/authrules.js";
5
+ import { body } from "../http/input.js";
6
+ import { createHash } from "node:crypto";
7
+ //#region src/api/relationships.ts
8
+ function parseRequest(body) {
9
+ if (!isObject(body) || typeof body.event_id !== "string") throw errors.badJson("event_id is required");
10
+ const num = (v, d, max) => Number.isInteger(v) && v >= 0 ? Math.min(v, max) : d;
11
+ return {
12
+ event_id: body.event_id,
13
+ room_id: typeof body.room_id === "string" ? body.room_id : void 0,
14
+ max_depth: num(body.max_depth, 3, 50),
15
+ max_breadth: num(body.max_breadth, 10, 100),
16
+ limit: Math.max(num(body.limit, 100, 1e3), 1),
17
+ depth_first: body.depth_first === true,
18
+ recent_first: body.recent_first !== false,
19
+ include_parent: body.include_parent === true,
20
+ include_children: body.include_children === true,
21
+ direction: body.direction === "up" ? "up" : "down"
22
+ };
23
+ }
24
+ function relationshipOf(pdu) {
25
+ const rel = pdu.content?.["m.relationship"];
26
+ return isObject(rel) && typeof rel.event_id === "string" && typeof rel.rel_type === "string" ? {
27
+ event_id: rel.event_id,
28
+ rel_type: rel.rel_type
29
+ } : null;
30
+ }
31
+ /** Index an event's `m.relationship` edge (idempotent). */
32
+ async function recordEdge(hs, pdu) {
33
+ const rel = relationshipOf(pdu);
34
+ if (!rel || !pdu.event_id) return;
35
+ await hs.db.upsert("event_edges", {
36
+ event_id: pdu.event_id,
37
+ room_id: pdu.room_id,
38
+ parent_id: rel.event_id,
39
+ rel_type: rel.rel_type,
40
+ ts: pdu.origin_server_ts ?? 0,
41
+ depth: pdu.depth ?? 0
42
+ });
43
+ }
44
+ async function children(hs, eventId, recentFirst) {
45
+ const rows = await hs.db.findMany("event_edges", { where: [{
46
+ field: "parent_id",
47
+ value: eventId
48
+ }] });
49
+ rows.sort((a, b) => (a.ts - b.ts || a.depth - b.depth || a.event_id.localeCompare(b.event_id)) * (recentFirst ? -1 : 1));
50
+ return rows;
51
+ }
52
+ async function childrenSummary(hs, eventId) {
53
+ const rows = await children(hs, eventId, false);
54
+ const counts = {};
55
+ for (const r of rows) counts[r.rel_type] = (counts[r.rel_type] ?? 0) + 1;
56
+ const ids = rows.map((r) => r.event_id).sort();
57
+ return {
58
+ children: counts,
59
+ children_hash: createHash("sha256").update(ids.join("")).digest("base64").replace(/=+$/, "")
60
+ };
61
+ }
62
+ /** Walk the local tree. `missing` collects referenced events this server does not have. */
63
+ async function walk(hs, req, canSee, missing) {
64
+ const out = [];
65
+ const seen = /* @__PURE__ */ new Set();
66
+ let limited = false;
67
+ const push = async (id) => {
68
+ if (seen.has(id)) return false;
69
+ seen.add(id);
70
+ const pdu = await hs.rooms.getPdu(id);
71
+ if (!pdu) {
72
+ missing.add(id);
73
+ return false;
74
+ }
75
+ if (!await canSee(pdu)) return false;
76
+ if (out.length >= req.limit) {
77
+ limited = true;
78
+ return false;
79
+ }
80
+ out.push(pdu);
81
+ return true;
82
+ };
83
+ const root = await hs.rooms.getPdu(req.event_id);
84
+ if (!root) {
85
+ missing.add(req.event_id);
86
+ return {
87
+ events: out,
88
+ limited,
89
+ root: null
90
+ };
91
+ }
92
+ if (!await canSee(root)) throw errors.forbidden("You cannot see this event");
93
+ await push(req.event_id);
94
+ const parentId = relationshipOf(root)?.event_id;
95
+ if (req.include_parent && parentId) await push(parentId);
96
+ if (req.include_children) for (const c of await children(hs, req.event_id, req.recent_first)) await push(c.event_id);
97
+ if (req.direction === "up") {
98
+ let current = root;
99
+ for (let depth = 0; current && depth < req.max_depth; depth++) {
100
+ const up = relationshipOf(current)?.event_id;
101
+ if (!up) break;
102
+ if (!seen.has(up)) await push(up);
103
+ current = await hs.rooms.getPdu(up);
104
+ }
105
+ } else {
106
+ const queue = [{
107
+ id: req.event_id,
108
+ depth: 0
109
+ }];
110
+ while (queue.length && !limited) {
111
+ const { id, depth } = req.depth_first ? queue.pop() : queue.shift();
112
+ if (depth >= req.max_depth) continue;
113
+ const kids = (await children(hs, id, req.recent_first)).slice(0, req.max_breadth);
114
+ const next = [];
115
+ for (const kid of kids) {
116
+ if (seen.has(kid.event_id)) continue;
117
+ if (await push(kid.event_id)) next.push({
118
+ id: kid.event_id,
119
+ depth: depth + 1
120
+ });
121
+ }
122
+ if (req.depth_first) queue.push(...next.reverse());
123
+ else queue.push(...next);
124
+ }
125
+ }
126
+ return {
127
+ events: out,
128
+ limited,
129
+ root
130
+ };
131
+ }
132
+ async function withChildren(hs, pdu) {
133
+ return {
134
+ ...pdu,
135
+ unsigned: {
136
+ ...pdu.unsigned,
137
+ ...await childrenSummary(hs, pdu.event_id)
138
+ }
139
+ };
140
+ }
141
+ /** Ask the room's other servers for part of the tree and store what they return. */
142
+ async function fetchRemote(hs, roomId, req, remoteHashes) {
143
+ const version = await hs.rooms.roomVersion(roomId);
144
+ if (!version || !hs.federation.enabled) return false;
145
+ const servers = [...hs.federation.serversInRoom(await hs.rooms.currentState(roomId))].filter((s) => s !== hs.serverName);
146
+ for (const server of servers) {
147
+ let res;
148
+ try {
149
+ res = await hs.federation.client.request(server, "POST", "/_matrix/federation/unstable/event_relationships", { body: {
150
+ ...req,
151
+ room_id: roomId
152
+ } });
153
+ } catch (err) {
154
+ hs.logger.debug("event_relationships fetch failed", server, err);
155
+ continue;
156
+ }
157
+ if (!isObject(res) || !Array.isArray(res.events)) continue;
158
+ await hs.federation.storeOutliers(roomId, version, [...Array.isArray(res.auth_chain) ? res.auth_chain : [], ...res.events]);
159
+ for (const raw of res.events) {
160
+ if (!isObject(raw)) continue;
161
+ const id = hs.federation.computeEventId(raw, version);
162
+ const pdu = id ? await hs.rooms.getPdu(id) : null;
163
+ if (!pdu) continue;
164
+ await recordEdge(hs, pdu);
165
+ if (typeof raw.unsigned?.children_hash === "string") remoteHashes.set(pdu.event_id, raw.unsigned.children_hash);
166
+ }
167
+ return true;
168
+ }
169
+ return false;
170
+ }
171
+ function registerRelationshipRoutes(r) {
172
+ r.post("/_matrix/client/unstable/event_relationships", { input: body({}) }, async (ctx) => {
173
+ const s = await ctx.auth();
174
+ const hs = ctx.hs;
175
+ const req = parseRequest(ctx.input);
176
+ const roomOf = async () => req.room_id ?? (await hs.rooms.getPdu(req.event_id))?.room_id ?? null;
177
+ const roomId = await roomOf();
178
+ if (!roomId) throw errors.forbidden("Unknown event");
179
+ if (await hs.rooms.membership(roomId, s.userId) !== "join") {
180
+ if (getState(await hs.rooms.currentState(roomId), "m.room.history_visibility")?.content?.history_visibility !== "world_readable") throw errors.forbidden("You are not in this room");
181
+ }
182
+ const canSee = async (pdu) => {
183
+ if (pdu.room_id !== roomId) return false;
184
+ const ev = await hs.rooms.getEvent(pdu.event_id);
185
+ if (!ev) return true;
186
+ return (await hs.rooms.filterVisible(s.userId, roomId, [ev])).length > 0;
187
+ };
188
+ const asked = /* @__PURE__ */ new Set();
189
+ const remoteHashes = /* @__PURE__ */ new Map();
190
+ let result = {
191
+ events: [],
192
+ limited: false,
193
+ root: null
194
+ };
195
+ for (let round = 0; round < 4; round++) {
196
+ const missing = /* @__PURE__ */ new Set();
197
+ result = await walk(hs, req, canSee, missing);
198
+ const wanted = [];
199
+ for (const id of missing) {
200
+ if (asked.has(id)) continue;
201
+ asked.add(id);
202
+ wanted.push(id === req.event_id ? req : {
203
+ ...req,
204
+ event_id: id,
205
+ direction: "up",
206
+ include_parent: false,
207
+ include_children: false
208
+ });
209
+ }
210
+ for (const pdu of result.events) {
211
+ const remote = remoteHashes.get(pdu.event_id);
212
+ if (!remote || asked.has(`children:${pdu.event_id}`)) continue;
213
+ if (remote !== (await childrenSummary(hs, pdu.event_id)).children_hash) {
214
+ asked.add(`children:${pdu.event_id}`);
215
+ wanted.push({
216
+ ...req,
217
+ event_id: pdu.event_id,
218
+ direction: "down",
219
+ max_depth: 1,
220
+ include_parent: false,
221
+ include_children: false
222
+ });
223
+ }
224
+ }
225
+ if (!wanted.length) break;
226
+ for (const w of wanted) await fetchRemote(hs, roomId, w, remoteHashes);
227
+ }
228
+ if (!result.root) throw errors.notFound("Event not found");
229
+ const events = [];
230
+ for (const pdu of result.events) events.push(toClientEvent(await withChildren(hs, pdu)));
231
+ return {
232
+ events,
233
+ limited: result.limited,
234
+ next_batch: null
235
+ };
236
+ });
237
+ }
238
+ /** Server side of `/_matrix/federation/unstable/event_relationships`. */
239
+ async function relationshipsForServer(hs, origin, body, authChainOf) {
240
+ const req = parseRequest(body);
241
+ const root = await hs.rooms.getPdu(req.event_id);
242
+ if (!root) throw errors.notFound("Event not found");
243
+ const roomId = root.room_id;
244
+ if (!hs.federation.serversInRoom(await hs.rooms.currentState(roomId)).has(origin)) throw errors.forbidden("Server is not in this room");
245
+ const result = await walk(hs, req, async (pdu) => pdu.room_id === roomId, /* @__PURE__ */ new Set());
246
+ const version = await hs.rooms.roomVersion(roomId);
247
+ const events = [];
248
+ for (const pdu of result.events) {
249
+ const wire = hs.federation.toFederationPdu(pdu, version);
250
+ wire.unsigned = {
251
+ ...wire.unsigned,
252
+ ...await childrenSummary(hs, pdu.event_id)
253
+ };
254
+ events.push(wire);
255
+ }
256
+ const chain = await authChainOf(result.events.map((p) => p.event_id));
257
+ return {
258
+ events,
259
+ limited: result.limited,
260
+ auth_chain: chain.map((p) => hs.federation.toFederationPdu(p, version))
261
+ };
262
+ }
263
+ //#endregion
264
+ export { recordEdge, registerRelationshipRoutes, relationshipOf, relationshipsForServer };