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,172 @@
1
+ import { splitStateKey, stripState } from "../core/events.js";
2
+ import { errors } from "../core/errors.js";
3
+ import { getState, membershipOf } from "../core/authrules.js";
4
+ //#region src/api/spaces.ts
5
+ /** Room-level details shared by /hierarchy, /room_summary and /publicRooms. */
6
+ async function roomDetails(hs, roomId) {
7
+ const state = await hs.rooms.currentState(roomId);
8
+ const get = (t) => getState(state, t)?.content;
9
+ const out = {
10
+ room_id: roomId,
11
+ num_joined_members: (await hs.rooms.joinedMembers(roomId)).length,
12
+ world_readable: get("m.room.history_visibility")?.history_visibility === "world_readable",
13
+ guest_can_join: get("m.room.guest_access")?.guest_access === "can_join",
14
+ join_rule: get("m.room.join_rules")?.join_rule ?? "invite"
15
+ };
16
+ const str = (v) => typeof v === "string" ? v : void 0;
17
+ if (str(get("m.room.name")?.name)) out.name = get("m.room.name").name;
18
+ if (str(get("m.room.topic")?.topic)) out.topic = get("m.room.topic").topic;
19
+ if (str(get("m.room.canonical_alias")?.alias)) out.canonical_alias = get("m.room.canonical_alias").alias;
20
+ if (str(get("m.room.avatar")?.url)) out.avatar_url = get("m.room.avatar").url;
21
+ if (str(get("m.room.create")?.type)) out.room_type = get("m.room.create").type;
22
+ return out;
23
+ }
24
+ /** Whether a user may see a room's summary (spec: "accessible" rooms). */
25
+ async function canAccess(hs, roomId, userId) {
26
+ const state = await hs.rooms.currentState(roomId);
27
+ if (userId && [
28
+ "join",
29
+ "invite",
30
+ "knock"
31
+ ].includes(membershipOf(state, userId))) return true;
32
+ if (getState(state, "m.room.history_visibility")?.content?.history_visibility === "world_readable") return true;
33
+ const joinRules = getState(state, "m.room.join_rules")?.content;
34
+ const rule = joinRules?.join_rule ?? "invite";
35
+ if (rule === "public" || rule === "knock") return true;
36
+ if ((rule === "restricted" || rule === "knock_restricted") && userId) {
37
+ if (rule === "knock_restricted") return true;
38
+ for (const allow of Array.isArray(joinRules?.allow) ? joinRules.allow : []) if (allow?.type === "m.room_membership" && typeof allow.room_id === "string" && await hs.rooms.exists(allow.room_id)) {
39
+ if (await hs.rooms.membership(allow.room_id, userId) === "join") return true;
40
+ }
41
+ }
42
+ return false;
43
+ }
44
+ function allowedRoomIds(joinRules) {
45
+ if (!joinRules || !["restricted", "knock_restricted"].includes(joinRules.join_rule)) return void 0;
46
+ return (Array.isArray(joinRules.allow) ? joinRules.allow : []).filter((a) => a?.type === "m.room_membership" && typeof a.room_id === "string").map((a) => a.room_id);
47
+ }
48
+ /** Spec ordering of space children: valid `order` strings first, then child event timestamp, then room ID. */
49
+ function childOrder(a, b) {
50
+ const valid = (o) => typeof o === "string" && o.length <= 50 && /^[\x20-\x7E]*$/.test(o) ? o : null;
51
+ const oa = valid(a.content?.order);
52
+ const ob = valid(b.content?.order);
53
+ if (oa !== null && ob === null) return -1;
54
+ if (oa === null && ob !== null) return 1;
55
+ if (oa !== null && ob !== null && oa !== ob) return oa < ob ? -1 : 1;
56
+ return (a.origin_server_ts ?? 0) - (b.origin_server_ts ?? 0) || (a.state_key < b.state_key ? -1 : a.state_key > b.state_key ? 1 : 0);
57
+ }
58
+ /** Fetch a room's summary from another server via federation /hierarchy, if the user may see it. */
59
+ async function remoteSummary(hs, roomId, via, suggestedOnly, userId) {
60
+ if (!hs.federation.enabled) return null;
61
+ for (const server of via.filter((v) => v !== hs.serverName)) try {
62
+ const room = (await hs.federation.queryHierarchy(server, roomId, suggestedOnly)).room;
63
+ if (!room || room.room_id !== roomId) continue;
64
+ const rule = room.join_rule ?? "invite";
65
+ let accessible = room.world_readable === true || [
66
+ "public",
67
+ "knock",
68
+ "knock_restricted"
69
+ ].includes(rule);
70
+ if (!accessible && rule === "restricted" && Array.isArray(room.allowed_room_ids)) {
71
+ for (const allowed of room.allowed_room_ids) if (await hs.rooms.exists(allowed) && await hs.rooms.membership(allowed, userId) === "join") {
72
+ accessible = true;
73
+ break;
74
+ }
75
+ }
76
+ return accessible ? room : null;
77
+ } catch {}
78
+ return null;
79
+ }
80
+ function registerSpaceRoutes(r) {
81
+ r.client("GET", "/rooms/:roomId/hierarchy", async (ctx) => {
82
+ const s = await ctx.auth();
83
+ const hs = ctx.hs;
84
+ const rootId = ctx.params.roomId;
85
+ if (!await hs.rooms.exists(rootId) || !await canAccess(hs, rootId, s.userId)) throw errors.forbidden("You cannot view this room");
86
+ const suggestedOnly = ctx.query.get("suggested_only") === "true";
87
+ const maxDepth = ctx.query.has("max_depth") ? Number.parseInt(ctx.query.get("max_depth"), 10) : Infinity;
88
+ const limit = Math.min(Math.max(Number.parseInt(ctx.query.get("limit") ?? "50", 10) || 50, 1), 100);
89
+ const from = ctx.query.get("from");
90
+ let offset = 0;
91
+ if (from) {
92
+ const m = /^(\d+)_(.+)$/.exec(from);
93
+ if (!m || m[2] !== `${rootId}|${suggestedOnly}|${ctx.query.get("max_depth") ?? ""}`) throw errors.invalidParam("Invalid pagination token");
94
+ offset = Number(m[1]);
95
+ }
96
+ const rooms = [];
97
+ const seen = /* @__PURE__ */ new Set();
98
+ const queue = [{
99
+ roomId: rootId,
100
+ depth: 0,
101
+ via: []
102
+ }];
103
+ while (queue.length) {
104
+ const { roomId, depth, via } = queue.pop();
105
+ if (seen.has(roomId)) continue;
106
+ seen.add(roomId);
107
+ if (!await hs.rooms.serverIsJoined(roomId)) {
108
+ const remote = await remoteSummary(hs, roomId, via, suggestedOnly, s.userId);
109
+ if (!remote) continue;
110
+ rooms.push(remote);
111
+ const remoteChildren = (remote.room_type === "m.space" ? remote.children_state ?? [] : []).filter((c) => Array.isArray(c.content?.via) && c.content.via.length).sort(childOrder);
112
+ if (depth < maxDepth) for (const child of [...remoteChildren].reverse()) queue.push({
113
+ roomId: child.state_key,
114
+ depth: depth + 1,
115
+ via: child.content.via
116
+ });
117
+ continue;
118
+ }
119
+ if (roomId !== rootId && !await canAccess(hs, roomId, s.userId)) continue;
120
+ const state = await hs.rooms.currentState(roomId);
121
+ const children = [];
122
+ const isSpace = getState(state, "m.room.create")?.content?.type === "m.space";
123
+ for (const [key, ev] of isSpace ? state : []) {
124
+ const [type] = splitStateKey(key);
125
+ if (type !== "m.space.child" || !Array.isArray(ev.content?.via) || ev.content.via.length === 0) continue;
126
+ if (suggestedOnly && ev.content.suggested !== true) continue;
127
+ children.push({
128
+ ...stripState(ev),
129
+ origin_server_ts: ev.origin_server_ts
130
+ });
131
+ }
132
+ children.sort(childOrder);
133
+ if (depth < maxDepth) for (const child of [...children].reverse()) queue.push({
134
+ roomId: child.state_key,
135
+ depth: depth + 1,
136
+ via: child.content.via
137
+ });
138
+ const details = await roomDetails(hs, roomId);
139
+ const allowed = allowedRoomIds(getState(state, "m.room.join_rules")?.content);
140
+ if (allowed) details.allowed_room_ids = allowed;
141
+ details.children_state = children;
142
+ rooms.push(details);
143
+ }
144
+ const out = { rooms: rooms.slice(offset, offset + limit) };
145
+ if (offset + limit < rooms.length) out.next_batch = `${offset + limit}_${rootId}|${suggestedOnly}|${ctx.query.get("max_depth") ?? ""}`;
146
+ return out;
147
+ }, ["v1"]);
148
+ const summary = async (ctx) => {
149
+ const s = await ctx.optionalAuth();
150
+ const hs = ctx.hs;
151
+ let roomId;
152
+ try {
153
+ roomId = await hs.rooms.resolveRoomIdOrAlias(ctx.params.roomIdOrAlias);
154
+ } catch {
155
+ throw errors.notFound("Room not found");
156
+ }
157
+ if (!await hs.rooms.exists(roomId) || !await canAccess(hs, roomId, s?.userId ?? null)) throw errors.notFound("Room not found");
158
+ const state = await hs.rooms.currentState(roomId);
159
+ const out = await roomDetails(hs, roomId);
160
+ out.room_version = await hs.rooms.roomVersion(roomId);
161
+ const enc = getState(state, "m.room.encryption")?.content?.algorithm;
162
+ if (typeof enc === "string") out.encryption = enc;
163
+ if (s) out.membership = membershipOf(state, s.userId);
164
+ const allowed = allowedRoomIds(getState(state, "m.room.join_rules")?.content);
165
+ if (allowed) out.allowed_room_ids = allowed;
166
+ return out;
167
+ };
168
+ r.client("GET", "/room_summary/:roomIdOrAlias", summary, ["v1"]);
169
+ r.get("/_matrix/client/unstable/im.nheko.summary/summary/:roomIdOrAlias", summary);
170
+ }
171
+ //#endregion
172
+ export { registerSpaceRoutes, roomDetails };