matrix-nodejs-homeserver 0.2.0 → 0.3.2

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 (99) hide show
  1. package/README.md +89 -11
  2. package/dist/adapters/cluster.d.ts +1 -26
  3. package/dist/adapters/cluster.js +50 -103
  4. package/dist/api/account.js +407 -354
  5. package/dist/api/admin.js +82 -61
  6. package/dist/api/appservice.js +37 -25
  7. package/dist/api/backup.js +73 -62
  8. package/dist/api/federation.js +172 -138
  9. package/dist/api/filters.js +4 -3
  10. package/dist/api/legacy.js +91 -78
  11. package/dist/api/media.js +71 -55
  12. package/dist/api/misc.js +46 -37
  13. package/dist/api/preview.js +37 -73
  14. package/dist/api/profile.js +108 -97
  15. package/dist/api/push.js +114 -99
  16. package/dist/api/relationships.js +64 -58
  17. package/dist/api/rooms.js +703 -580
  18. package/dist/api/rtc.d.ts +0 -1
  19. package/dist/api/rtc.js +48 -34
  20. package/dist/api/search.js +133 -127
  21. package/dist/api/slidingsync.js +41 -31
  22. package/dist/api/spaces.js +82 -77
  23. package/dist/api/sync.js +325 -135
  24. package/dist/api/threadsubs.js +72 -61
  25. package/dist/api/threepid.js +99 -66
  26. package/dist/api/uia.js +17 -16
  27. package/dist/api/user.js +221 -178
  28. package/dist/core/admin.d.ts +0 -1
  29. package/dist/core/admin.js +6 -5
  30. package/dist/core/adminroom.js +9 -8
  31. package/dist/core/appservices.d.ts +2 -1
  32. package/dist/core/appservices.js +5 -4
  33. package/dist/core/audit.d.ts +0 -1
  34. package/dist/core/authrules.js +26 -25
  35. package/dist/core/cluster.d.ts +12 -5
  36. package/dist/core/cluster.js +46 -29
  37. package/dist/core/delayed.js +2 -1
  38. package/dist/core/events.js +11 -10
  39. package/dist/core/html.js +13 -0
  40. package/dist/core/identity.js +1 -1
  41. package/dist/core/invitefilter.js +2 -1
  42. package/dist/core/json.js +10 -1
  43. package/dist/core/keys.d.ts +10 -1
  44. package/dist/core/keys.js +62 -42
  45. package/dist/core/m.d.ts +209 -0
  46. package/dist/core/m.js +182 -0
  47. package/dist/core/outbound.d.ts +16 -0
  48. package/dist/core/outbound.js +172 -0
  49. package/dist/core/partialstate.js +2 -1
  50. package/dist/core/policy.js +4 -3
  51. package/dist/core/push.d.ts +0 -1
  52. package/dist/core/push.js +41 -28
  53. package/dist/core/pushrules.js +36 -35
  54. package/dist/core/ratelimit.d.ts +1 -1
  55. package/dist/core/rooms.d.ts +10 -1
  56. package/dist/core/rooms.js +101 -74
  57. package/dist/core/semaphore.js +23 -0
  58. package/dist/core/sso.d.ts +15 -1
  59. package/dist/core/sso.js +38 -2
  60. package/dist/core/stateres.js +7 -6
  61. package/dist/core/threepid.js +1 -9
  62. package/dist/federation/client.js +9 -1
  63. package/dist/federation/index.d.ts +2 -2
  64. package/dist/federation/index.js +43 -35
  65. package/dist/homeserver.d.ts +23 -4
  66. package/dist/homeserver.js +12 -3
  67. package/dist/http/handler.js +2 -23
  68. package/dist/http/input.js +2 -2
  69. package/dist/http/procedure.js +25 -0
  70. package/dist/http/router.d.ts +9 -26
  71. package/dist/http/router.js +10 -30
  72. package/dist/index.d.ts +4 -3
  73. package/dist/index.js +3 -2
  74. package/dist/storage/batch.js +11 -0
  75. package/dist/storage/memory.js +1 -0
  76. package/dist/storage/schema.js +1 -1
  77. package/dist/storage/sql.js +20 -1
  78. package/dist/storage/types.d.ts +16 -0
  79. package/dist/vendor/zod/LICENSE +21 -0
  80. package/dist/vendor/zod/v4/classic/errors.js +54 -0
  81. package/dist/vendor/zod/v4/classic/parse.js +17 -0
  82. package/dist/vendor/zod/v4/classic/schemas.js +865 -0
  83. package/dist/vendor/zod/v4/core/api.js +512 -0
  84. package/dist/vendor/zod/v4/core/checks.js +300 -0
  85. package/dist/vendor/zod/v4/core/core.js +109 -0
  86. package/dist/vendor/zod/v4/core/doc.js +35 -0
  87. package/dist/vendor/zod/v4/core/errors.js +118 -0
  88. package/dist/vendor/zod/v4/core/json-schema-processors.js +428 -0
  89. package/dist/vendor/zod/v4/core/memoizer.js +255 -0
  90. package/dist/vendor/zod/v4/core/parse.js +206 -0
  91. package/dist/vendor/zod/v4/core/regexes.js +73 -0
  92. package/dist/vendor/zod/v4/core/registries.js +48 -0
  93. package/dist/vendor/zod/v4/core/schemas.js +1378 -0
  94. package/dist/vendor/zod/v4/core/to-json-schema.js +448 -0
  95. package/dist/vendor/zod/v4/core/util.js +537 -0
  96. package/dist/vendor/zod/v4/core/versions.js +8 -0
  97. package/dist/vendor/zod/v4/locales/en.js +110 -0
  98. package/package.json +8 -6
  99. package/dist/core/pushrules.d.ts +0 -1
package/dist/api/rtc.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- import "../http/router.js";
2
1
  //#region src/api/rtc.d.ts
3
2
  export interface TurnOptions {
4
3
  uris: string[];
package/dist/api/rtc.js CHANGED
@@ -1,52 +1,66 @@
1
1
  import { errors } from "../core/errors.js";
2
+ import { string } from "../vendor/zod/v4/classic/schemas.js";
2
3
  import { body, fail } from "../http/input.js";
4
+ import { procedure } from "../http/procedure.js";
3
5
  import { createHmac } from "node:crypto";
4
- import { z } from "zod";
5
6
  //#region src/api/rtc.ts
6
7
  function registerRtcRoutes(r) {
7
- r.client("GET", "/voip/turnServer", async (ctx) => {
8
- const s = await ctx.auth();
9
- const turn = ctx.hs.config.rtc.turn;
10
- if (!turn || !turn.uris.length) return {};
11
- if (s.isGuest && !turn.allowGuests) throw errors.forbidden("Guests may not use TURN");
12
- const ttl = turn.ttlSeconds ?? 86400;
13
- if (turn.sharedSecret) {
14
- const username = `${Math.floor(Date.now() / 1e3) + ttl}:${s.userId}`;
8
+ r.client("GET", "/voip/turnServer", procedure({
9
+ auth: "user",
10
+ handler: async (ctx) => {
11
+ const s = ctx.session;
12
+ const turn = ctx.hs.config.rtc.turn;
13
+ if (!turn || !turn.uris.length) return {};
14
+ if (s.isGuest && !turn.allowGuests) throw errors.forbidden("Guests may not use TURN");
15
+ const ttl = turn.ttlSeconds ?? 86400;
16
+ if (turn.sharedSecret) {
17
+ const username = `${Math.floor(Date.now() / 1e3) + ttl}:${s.userId}`;
18
+ return {
19
+ username,
20
+ password: createHmac("sha1", turn.sharedSecret).update(username).digest("base64"),
21
+ uris: turn.uris,
22
+ ttl
23
+ };
24
+ }
15
25
  return {
16
- username,
17
- password: createHmac("sha1", turn.sharedSecret).update(username).digest("base64"),
26
+ username: turn.username ?? "",
27
+ password: turn.password ?? "",
18
28
  uris: turn.uris,
19
29
  ttl
20
30
  };
21
31
  }
22
- return {
23
- username: turn.username ?? "",
24
- password: turn.password ?? "",
25
- uris: turn.uris,
26
- ttl
27
- };
28
- });
32
+ }));
29
33
  const transports = async (ctx) => {
30
34
  await ctx.auth();
31
35
  return { rtc_transports: ctx.hs.config.rtc.transports ?? [] };
32
36
  };
33
- r.client("GET", "/rtc/transports", transports, ["v1"]);
34
- r.get("/_matrix/client/unstable/org.matrix.msc4143/rtc/transports", transports);
37
+ r.client("GET", "/rtc/transports", procedure({ handler: transports }), ["v1"]);
38
+ r.get("/_matrix/client/unstable/org.matrix.msc4143/rtc/transports", procedure({ handler: transports }));
35
39
  const base = "/_matrix/client/unstable/org.matrix.msc4140/delayed_events";
36
- r.get(base, async (ctx) => {
37
- const s = await ctx.auth();
38
- return { delayed_events: await ctx.hs.delayedEvents.list(s.userId) };
39
- });
40
- r.post(`${base}/:delayId`, { input: body({ action: z.string(fail("M_MISSING_PARAM", "Missing action")) }) }, async (ctx) => {
41
- const s = await ctx.auth();
42
- await ctx.hs.delayedEvents.update(s.userId, ctx.params.delayId, ctx.input.action);
43
- return {};
44
- });
45
- r.post(`${base}/:delayId/:action`, async (ctx) => {
46
- const s = await ctx.auth();
47
- await ctx.hs.delayedEvents.update(s.userId, ctx.params.delayId, ctx.params.action);
48
- return {};
49
- });
40
+ r.get(base, procedure({
41
+ auth: "user",
42
+ handler: async (ctx) => {
43
+ const s = ctx.session;
44
+ return { delayed_events: await ctx.hs.delayedEvents.list(s.userId) };
45
+ }
46
+ }));
47
+ r.post(`${base}/:delayId`, procedure({
48
+ auth: "user",
49
+ input: body({ action: string(fail("M_MISSING_PARAM", "Missing action")) }),
50
+ handler: async (ctx) => {
51
+ const s = ctx.session;
52
+ await ctx.hs.delayedEvents.update(s.userId, ctx.params.delayId, ctx.input.action);
53
+ return {};
54
+ }
55
+ }));
56
+ r.post(`${base}/:delayId/:action`, procedure({
57
+ auth: "user",
58
+ handler: async (ctx) => {
59
+ const s = ctx.session;
60
+ await ctx.hs.delayedEvents.update(s.userId, ctx.params.delayId, ctx.params.action);
61
+ return {};
62
+ }
63
+ }));
50
64
  }
51
65
  //#endregion
52
66
  export { registerRtcRoutes };
@@ -1,9 +1,11 @@
1
1
  import { isObject } from "../core/json.js";
2
+ import { M } from "../core/m.js";
2
3
  import { getState } from "../core/authrules.js";
4
+ import { looseObject, string, unknown } from "../vendor/zod/v4/classic/schemas.js";
3
5
  import { body, fail } from "../http/input.js";
6
+ import { procedure } from "../http/procedure.js";
4
7
  import { matchesFilter } from "./filters.js";
5
8
  import { paginate, topoToken } from "./rooms.js";
6
- import { z } from "zod";
7
9
  //#region src/api/search.ts
8
10
  const DEFAULT_KEYS = [
9
11
  "content.body",
@@ -15,141 +17,145 @@ function valueAt(obj, path) {
15
17
  }
16
18
  function registerSearchRoutes(r) {
17
19
  const missingTerm = fail("M_BAD_JSON", "Missing search_categories.room_events.search_term");
18
- const searchBody = body({ search_categories: z.looseObject({ room_events: z.looseObject({
19
- search_term: z.string(missingTerm),
20
- keys: z.unknown().optional(),
21
- filter: z.unknown().optional(),
22
- order_by: z.unknown().optional()
20
+ const searchBody = body({ search_categories: looseObject({ room_events: looseObject({
21
+ search_term: string(missingTerm),
22
+ keys: unknown().optional(),
23
+ filter: unknown().optional(),
24
+ order_by: unknown().optional()
23
25
  }, missingTerm) }, missingTerm) });
24
- r.client("POST", "/search", { input: searchBody }, async (ctx) => {
25
- const s = await ctx.auth();
26
- const hs = ctx.hs;
27
- const criteria = ctx.input.search_categories.room_events;
28
- const terms = criteria.search_term.toLowerCase().split(/\s+/).filter(Boolean);
29
- const keys = Array.isArray(criteria.keys) && criteria.keys.length ? criteria.keys : DEFAULT_KEYS;
30
- const filter = isObject(criteria.filter) ? criteria.filter : {};
31
- const limit = Math.min(filter.limit ?? 10, 100);
32
- const orderBy = criteria.order_by === "recent" ? "recent" : "rank";
33
- const offset = Number.parseInt(ctx.query.get("next_batch") ?? "0", 10) || 0;
34
- let rooms = await hs.rooms.roomsForUser(s.userId);
35
- if (filter.rooms) rooms = rooms.filter((r) => filter.rooms.includes(r));
36
- if (filter.not_rooms) rooms = rooms.filter((r) => !filter.not_rooms.includes(r));
37
- const hits = [];
38
- for (const roomId of rooms) {
39
- const rows = await hs.db.findMany("events", {
40
- where: [{
41
- field: "room_id",
42
- value: roomId
43
- }],
44
- sortBy: {
45
- field: "stream",
46
- direction: "desc"
47
- }
48
- });
49
- const candidates = [];
50
- for (const row of rows) {
51
- if (row.state_key !== null || !matchesFilter(filter, row.json)) continue;
52
- let rank = 0;
53
- const words = [];
54
- for (const key of keys) {
55
- const v = valueAt(row.json, key);
56
- if (typeof v === "string") words.push(...v.toLowerCase().split(/[^\p{L}\p{N}]+/u));
57
- }
58
- if (terms.every((t) => words.includes(t))) for (const t of terms) rank += words.filter((w) => w === t).length;
59
- if (rank > 0) candidates.push({
60
- ev: hs.rooms.rowToStored(row),
61
- rank
26
+ r.client("POST", "/search", procedure({
27
+ auth: "user",
28
+ input: searchBody,
29
+ handler: async (ctx) => {
30
+ const s = ctx.session;
31
+ const hs = ctx.hs;
32
+ const criteria = ctx.input.search_categories.room_events;
33
+ const terms = criteria.search_term.toLowerCase().split(/\s+/).filter(Boolean);
34
+ const keys = Array.isArray(criteria.keys) && criteria.keys.length ? criteria.keys : DEFAULT_KEYS;
35
+ const filter = isObject(criteria.filter) ? criteria.filter : {};
36
+ const limit = Math.min(filter.limit ?? 10, 100);
37
+ const orderBy = criteria.order_by === "recent" ? "recent" : "rank";
38
+ const offset = Number.parseInt(ctx.query.get("next_batch") ?? "0", 10) || 0;
39
+ let rooms = await hs.rooms.roomsForUser(s.userId);
40
+ if (filter.rooms) rooms = rooms.filter((r) => filter.rooms.includes(r));
41
+ if (filter.not_rooms) rooms = rooms.filter((r) => !filter.not_rooms.includes(r));
42
+ const hits = [];
43
+ for (const roomId of rooms) {
44
+ const rows = await hs.db.findMany("events", {
45
+ where: [{
46
+ field: "room_id",
47
+ value: roomId
48
+ }],
49
+ sortBy: {
50
+ field: "stream",
51
+ direction: "desc"
52
+ }
62
53
  });
54
+ const candidates = [];
55
+ for (const row of rows) {
56
+ if (row.state_key !== null || !matchesFilter(filter, row.json)) continue;
57
+ let rank = 0;
58
+ const words = [];
59
+ for (const key of keys) {
60
+ const v = valueAt(row.json, key);
61
+ if (typeof v === "string") words.push(...v.toLowerCase().split(/[^\p{L}\p{N}]+/u));
62
+ }
63
+ if (terms.every((t) => words.includes(t))) for (const t of terms) rank += words.filter((w) => w === t).length;
64
+ if (rank > 0) candidates.push({
65
+ ev: hs.rooms.rowToStored(row),
66
+ rank
67
+ });
68
+ }
69
+ const visible = new Set((await hs.rooms.filterVisible(s.userId, roomId, candidates.map((c) => c.ev))).map((v) => v.pdu.event_id));
70
+ hits.push(...candidates.filter((c) => visible.has(c.ev.pdu.event_id)));
63
71
  }
64
- const visible = new Set((await hs.rooms.filterVisible(s.userId, roomId, candidates.map((c) => c.ev))).map((v) => v.pdu.event_id));
65
- hits.push(...candidates.filter((c) => visible.has(c.ev.pdu.event_id)));
66
- }
67
- hits.sort((a, b) => orderBy === "rank" && b.rank !== a.rank ? b.rank - a.rank : b.ev.stream - a.ev.stream);
68
- const page = hits.slice(offset, offset + limit);
69
- const viewer = {
70
- userId: s.userId,
71
- deviceId: s.deviceId
72
- };
73
- const ec = isObject(criteria.event_context) ? criteria.event_context : null;
74
- const results = [];
75
- for (const hit of page) {
76
- const result = {
77
- rank: hit.rank,
78
- result: hs.rooms.formatEvent(hit.ev, viewer)
72
+ hits.sort((a, b) => orderBy === "rank" && b.rank !== a.rank ? b.rank - a.rank : b.ev.stream - a.ev.stream);
73
+ const page = hits.slice(offset, offset + limit);
74
+ const viewer = {
75
+ userId: s.userId,
76
+ deviceId: s.deviceId
79
77
  };
80
- if (ec) {
81
- const roomId = hit.ev.pdu.room_id;
82
- const here = {
83
- depth: hit.ev.pdu.depth,
84
- stream: hit.ev.stream
78
+ const ec = isObject(criteria.event_context) ? criteria.event_context : null;
79
+ const results = [];
80
+ for (const hit of page) {
81
+ const result = {
82
+ rank: hit.rank,
83
+ result: hs.rooms.formatEvent(hit.ev, viewer)
85
84
  };
86
- const before = await paginate(hs, s.userId, roomId, {
87
- from: here,
88
- to: null,
89
- dir: "b",
90
- limit: ec.before_limit ?? 5
91
- });
92
- const after = await paginate(hs, s.userId, roomId, {
93
- from: here,
94
- to: null,
95
- dir: "f",
96
- limit: ec.after_limit ?? 5
97
- });
98
- const context = {
99
- events_before: before.events.map((e) => hs.rooms.formatEvent(e, viewer)),
100
- events_after: after.events.map((e) => hs.rooms.formatEvent(e, viewer)),
101
- start: before.end ?? topoToken(before.events.at(-1) ?? hit.ev),
102
- end: topoToken(after.events.at(-1) ?? hit.ev)
103
- };
104
- if (ec.include_profile) {
105
- const state = await hs.rooms.currentState(roomId);
106
- const profile_info = {};
107
- for (const e of [
108
- ...before.events,
109
- hit.ev,
110
- ...after.events
111
- ]) {
112
- const m = getState(state, "m.room.member", e.pdu.sender)?.content;
113
- profile_info[e.pdu.sender] = {
114
- displayname: m?.displayname,
115
- avatar_url: m?.avatar_url
116
- };
85
+ if (ec) {
86
+ const roomId = hit.ev.pdu.room_id;
87
+ const here = {
88
+ depth: hit.ev.pdu.depth,
89
+ stream: hit.ev.stream
90
+ };
91
+ const before = await paginate(hs, s.userId, roomId, {
92
+ from: here,
93
+ to: null,
94
+ dir: "b",
95
+ limit: ec.before_limit ?? 5
96
+ });
97
+ const after = await paginate(hs, s.userId, roomId, {
98
+ from: here,
99
+ to: null,
100
+ dir: "f",
101
+ limit: ec.after_limit ?? 5
102
+ });
103
+ const context = {
104
+ events_before: before.events.map((e) => hs.rooms.formatEvent(e, viewer)),
105
+ events_after: after.events.map((e) => hs.rooms.formatEvent(e, viewer)),
106
+ start: before.end ?? topoToken(before.events.at(-1) ?? hit.ev),
107
+ end: topoToken(after.events.at(-1) ?? hit.ev)
108
+ };
109
+ if (ec.include_profile) {
110
+ const state = await hs.rooms.currentState(roomId);
111
+ const profile_info = {};
112
+ for (const e of [
113
+ ...before.events,
114
+ hit.ev,
115
+ ...after.events
116
+ ]) {
117
+ const m = getState(state, M.ROOM.MEMBER, e.pdu.sender)?.content;
118
+ profile_info[e.pdu.sender] = {
119
+ displayname: m?.displayname,
120
+ avatar_url: m?.avatar_url
121
+ };
122
+ }
123
+ context.profile_info = profile_info;
117
124
  }
118
- context.profile_info = profile_info;
125
+ result.context = context;
119
126
  }
120
- result.context = context;
127
+ results.push(result);
121
128
  }
122
- results.push(result);
123
- }
124
- const out = {
125
- results,
126
- count: hits.length,
127
- highlights: terms
128
- };
129
- if (page.length === limit && offset + limit <= hits.length) out.next_batch = String(offset + limit);
130
- if (criteria.include_state) {
131
- out.state = {};
132
- for (const roomId of new Set(page.map((h) => h.ev.pdu.room_id))) out.state[roomId] = [...(await hs.rooms.currentState(roomId)).values()].map((e) => hs.rooms.formatEvent({
133
- pdu: e,
134
- stream: 0,
135
- snapshot: 0,
136
- txnId: null,
137
- txnToken: null
138
- }));
139
- }
140
- if (criteria.groupings?.group_by?.some?.((g) => g?.key === "room_id")) {
141
- const groups = {};
142
- hits.forEach((h, i) => {
143
- (groups[h.ev.pdu.room_id] ??= {
144
- results: [],
145
- order: i,
146
- next_batch: null
147
- }).results.push(h.ev.pdu.event_id);
148
- });
149
- out.groups = { room_id: groups };
129
+ const out = {
130
+ results,
131
+ count: hits.length,
132
+ highlights: terms
133
+ };
134
+ if (page.length === limit && offset + limit <= hits.length) out.next_batch = String(offset + limit);
135
+ if (criteria.include_state) {
136
+ out.state = {};
137
+ for (const roomId of new Set(page.map((h) => h.ev.pdu.room_id))) out.state[roomId] = [...(await hs.rooms.currentState(roomId)).values()].map((e) => hs.rooms.formatEvent({
138
+ pdu: e,
139
+ stream: 0,
140
+ snapshot: 0,
141
+ txnId: null,
142
+ txnToken: null
143
+ }));
144
+ }
145
+ if (criteria.groupings?.group_by?.some?.((g) => g?.key === "room_id")) {
146
+ const groups = {};
147
+ hits.forEach((h, i) => {
148
+ (groups[h.ev.pdu.room_id] ??= {
149
+ results: [],
150
+ order: i,
151
+ next_batch: null
152
+ }).results.push(h.ev.pdu.event_id);
153
+ });
154
+ out.groups = { room_id: groups };
155
+ }
156
+ return { search_categories: { room_events: out } };
150
157
  }
151
- return { search_categories: { room_events: out } };
152
- });
158
+ }));
153
159
  }
154
160
  //#endregion
155
161
  export { registerSearchRoutes };
@@ -1,25 +1,27 @@
1
1
  import { isObject } from "../core/json.js";
2
+ import { M, UNSTABLE } from "../core/m.js";
2
3
  import { splitStateKey, toClientEvent } from "../core/events.js";
3
4
  import { MatrixError } from "../core/errors.js";
4
5
  import { getState } from "../core/authrules.js";
6
+ import { record, string, unknown } from "../vendor/zod/v4/classic/schemas.js";
5
7
  import { body, lenient } from "../http/input.js";
8
+ import { procedure } from "../http/procedure.js";
6
9
  import { roomEphemeral, roomTimeline, toStripped } from "./sync.js";
7
- import { z } from "zod";
8
10
  //#region src/api/slidingsync.ts
9
11
  const slidingSyncBody = body({
10
- conn_id: lenient(z.string()),
11
- lists: lenient(z.record(z.string(), z.unknown())),
12
- room_subscriptions: lenient(z.record(z.string(), z.unknown())),
13
- extensions: lenient(z.record(z.string(), z.unknown()))
12
+ conn_id: lenient(string()),
13
+ lists: lenient(record(string(), unknown())),
14
+ room_subscriptions: lenient(record(string(), unknown())),
15
+ extensions: lenient(record(string(), unknown()))
14
16
  });
15
17
  const BUMP_TYPES = /* @__PURE__ */ new Set([
16
- "m.room.create",
17
- "m.room.message",
18
- "m.room.encrypted",
19
- "m.sticker",
20
- "m.call.invite",
21
- "m.poll.start",
22
- "m.beacon_info"
18
+ M.ROOM.CREATE,
19
+ M.ROOM.MESSAGE,
20
+ M.ROOM.ENCRYPTED,
21
+ M.STICKER,
22
+ M.CALL.INVITE,
23
+ M.POLL.START,
24
+ M.BEACON_INFO
23
25
  ]);
24
26
  async function loadConnection(hs, s, connId) {
25
27
  return hs.db.findOne("sliding_sync_connections", [
@@ -62,17 +64,25 @@ function matchesRequiredState(required, type, key, userId, lazyMembers) {
62
64
  if (t !== "*" && t !== type) continue;
63
65
  if (k === "*" || k === key) return true;
64
66
  if (k === "$ME" && key === userId) return true;
65
- if (k === "$LAZY" && type === "m.room.member" && lazyMembers.has(key)) return true;
67
+ if (k === "$LAZY" && type === M.ROOM.MEMBER && lazyMembers.has(key)) return true;
66
68
  }
67
69
  return false;
68
70
  }
69
71
  function registerSlidingSyncRoutes(r) {
70
- r.post("/_matrix/client/unstable/org.matrix.simplified_msc3575/sync", { input: slidingSyncBody }, slidingSync);
71
- r.post("/_matrix/client/v5/sync", { input: slidingSyncBody }, slidingSync);
72
+ r.post("/_matrix/client/unstable/org.matrix.simplified_msc3575/sync", procedure({
73
+ auth: "user",
74
+ input: slidingSyncBody,
75
+ handler: slidingSync
76
+ }));
77
+ r.post("/_matrix/client/v5/sync", procedure({
78
+ auth: "user",
79
+ input: slidingSyncBody,
80
+ handler: slidingSync
81
+ }));
72
82
  }
73
83
  async function slidingSync(ctx) {
74
84
  const hs = ctx.hs;
75
- const s = await ctx.auth();
85
+ const s = ctx.session;
76
86
  const body = ctx.input;
77
87
  const connId = body.conn_id ?? "";
78
88
  const posParam = ctx.query.get("pos");
@@ -143,7 +153,7 @@ async function buildResponse(hs, s, body, conn, since, to) {
143
153
  },
144
154
  {
145
155
  field: "type",
146
- value: "m.direct"
156
+ value: M.DIRECT
147
157
  }
148
158
  ]);
149
159
  const dmRooms = new Set(Object.values(direct?.content ?? {}).flat().filter((x) => typeof x === "string"));
@@ -162,7 +172,7 @@ async function buildResponse(hs, s, body, conn, since, to) {
162
172
  bump,
163
173
  state
164
174
  });
165
- const invite = getState(state, "m.room.member", userId);
175
+ const invite = getState(state, M.ROOM.MEMBER, userId);
166
176
  if (m.membership === "invite" && invite?.content?.is_direct) dmRooms.add(m.room_id);
167
177
  }
168
178
  infos.sort((a, b) => b.bump - a.bump);
@@ -174,8 +184,8 @@ async function buildResponse(hs, s, body, conn, since, to) {
174
184
  let rooms = infos;
175
185
  if (f.is_dm !== void 0) rooms = rooms.filter((r) => dmRooms.has(r.roomId) === f.is_dm);
176
186
  if (f.is_invite !== void 0) rooms = rooms.filter((r) => r.membership === "invite" === f.is_invite);
177
- if (f.is_encrypted !== void 0) rooms = rooms.filter((r) => !!getState(r.state, "m.room.encryption") === f.is_encrypted);
178
- const roomType = (r) => getState(r.state, "m.room.create")?.content?.type ?? null;
187
+ if (f.is_encrypted !== void 0) rooms = rooms.filter((r) => !!getState(r.state, M.ROOM.ENCRYPTION) === f.is_encrypted);
188
+ const roomType = (r) => getState(r.state, M.ROOM.CREATE)?.content?.type ?? null;
179
189
  if (f.room_types) rooms = rooms.filter((r) => f.room_types.includes(roomType(r)));
180
190
  if (f.not_room_types) rooms = rooms.filter((r) => !f.not_room_types.includes(roomType(r)));
181
191
  if (f.spaces) {
@@ -184,7 +194,7 @@ async function buildResponse(hs, s, body, conn, since, to) {
184
194
  if (!await hs.rooms.exists(space)) continue;
185
195
  for (const key of (await hs.rooms.currentState(space)).keys()) {
186
196
  const [t, k] = splitStateKey(key);
187
- if (t === "m.space.child") children.add(k);
197
+ if (t === M.SPACE.CHILD) children.add(k);
188
198
  }
189
199
  }
190
200
  rooms = rooms.filter((r) => children.has(r.roomId));
@@ -265,13 +275,13 @@ async function buildRoom(hs, s, info, config, since, to, isDm) {
265
275
  timeline: [],
266
276
  required_state: []
267
277
  };
268
- const name = getState(state, "m.room.name")?.content?.name;
278
+ const name = getState(state, M.ROOM.NAME)?.content?.name;
269
279
  if (typeof name === "string") room.name = name;
270
- const avatar = getState(state, "m.room.avatar")?.content?.url;
280
+ const avatar = getState(state, M.ROOM.AVATAR)?.content?.url;
271
281
  if (typeof avatar === "string") room.avatar = avatar;
272
282
  if (isDm) room.is_dm = true;
273
283
  if (info.membership === "invite" || info.membership === "knock") {
274
- const own = getState(state, "m.room.member", userId);
284
+ const own = getState(state, M.ROOM.MEMBER, userId);
275
285
  const ev = own?.event_id ? await hs.rooms.getEvent(own.event_id) : null;
276
286
  if (ev) {
277
287
  const key = info.membership === "invite" ? "invite_room_state" : "knock_room_state";
@@ -323,7 +333,7 @@ async function buildRoom(hs, s, info, config, since, to, isDm) {
323
333
  const heroes = [];
324
334
  for (const [key, ev] of state) {
325
335
  const [type, sk] = splitStateKey(key);
326
- if (type !== "m.room.member") continue;
336
+ if (type !== M.ROOM.MEMBER) continue;
327
337
  const m = ev.content?.membership;
328
338
  if (m === "join") joined++;
329
339
  if (m === "invite") invited++;
@@ -410,10 +420,10 @@ async function buildExtensions(hs, s, ext, since, to, roomIds, next) {
410
420
  if (r.room_id === "") global.push(ev);
411
421
  else if (roomIds.includes(r.room_id) || since !== null) (rooms[r.room_id] ??= []).push(ev);
412
422
  }
413
- if (since === null && !global.some((e) => e.type === "m.push_rules")) {
423
+ if (since === null && !global.some((e) => e.type === M.PUSH_RULES)) {
414
424
  const { loadRules } = await import("../core/pushrules.js");
415
425
  global.push({
416
- type: "m.push_rules",
426
+ type: M.PUSH_RULES,
417
427
  content: { global: await loadRules(hs, userId) }
418
428
  });
419
429
  }
@@ -422,9 +432,9 @@ async function buildExtensions(hs, s, ext, since, to, roomIds, next) {
422
432
  rooms
423
433
  };
424
434
  }
425
- if (ext["io.element.msc4308.thread_subscriptions"]?.enabled) {
435
+ if (ext[UNSTABLE.IO.ELEMENT.MSC4308.THREAD_SUBSCRIPTIONS]?.enabled) {
426
436
  const { threadSubscriptionsExtension } = await import("./threadsubs.js");
427
- out["io.element.msc4308.thread_subscriptions"] = await threadSubscriptionsExtension(hs, userId, ext["io.element.msc4308.thread_subscriptions"], since, to);
437
+ out[UNSTABLE.IO.ELEMENT.MSC4308.THREAD_SUBSCRIPTIONS] = await threadSubscriptionsExtension(hs, userId, ext[UNSTABLE.IO.ELEMENT.MSC4308.THREAD_SUBSCRIPTIONS], since, to);
428
438
  }
429
439
  if (ext.receipts?.enabled || ext.typing?.enabled) {
430
440
  const p = {
@@ -444,8 +454,8 @@ async function buildExtensions(hs, s, ext, since, to, roomIds, next) {
444
454
  for (const roomId of since === null ? roomIds : [...joined]) {
445
455
  if (!joined.has(roomId)) continue;
446
456
  for (const e of await roomEphemeral(hs, p, roomId)) {
447
- if (e.type === "m.receipt" && ext.receipts?.enabled) receipts[roomId] = e;
448
- if (e.type === "m.typing" && ext.typing?.enabled && (since !== null || e.content.user_ids.length)) typing[roomId] = e;
457
+ if (e.type === M.RECEIPT && ext.receipts?.enabled) receipts[roomId] = e;
458
+ if (e.type === M.TYPING && ext.typing?.enabled && (since !== null || e.content.user_ids.length)) typing[roomId] = e;
449
459
  }
450
460
  }
451
461
  if (ext.receipts?.enabled) out.receipts = { rooms: receipts };