matrix-nodejs-homeserver 0.2.0 → 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 (78) hide show
  1. package/README.md +29 -7
  2. package/dist/api/account.js +385 -351
  3. package/dist/api/admin.js +76 -60
  4. package/dist/api/appservice.js +37 -25
  5. package/dist/api/backup.js +73 -62
  6. package/dist/api/federation.js +172 -138
  7. package/dist/api/filters.js +4 -3
  8. package/dist/api/legacy.js +91 -78
  9. package/dist/api/media.js +70 -54
  10. package/dist/api/misc.js +46 -37
  11. package/dist/api/preview.js +4 -3
  12. package/dist/api/profile.js +108 -97
  13. package/dist/api/push.js +114 -99
  14. package/dist/api/relationships.js +64 -58
  15. package/dist/api/rooms.js +703 -580
  16. package/dist/api/rtc.d.ts +0 -1
  17. package/dist/api/rtc.js +48 -34
  18. package/dist/api/search.js +133 -127
  19. package/dist/api/slidingsync.js +41 -31
  20. package/dist/api/spaces.js +82 -77
  21. package/dist/api/sync.js +59 -50
  22. package/dist/api/threadsubs.js +72 -61
  23. package/dist/api/threepid.js +99 -66
  24. package/dist/api/uia.js +17 -16
  25. package/dist/api/user.js +221 -178
  26. package/dist/core/admin.d.ts +0 -1
  27. package/dist/core/admin.js +6 -5
  28. package/dist/core/adminroom.js +9 -8
  29. package/dist/core/appservices.d.ts +2 -1
  30. package/dist/core/appservices.js +5 -4
  31. package/dist/core/audit.d.ts +0 -1
  32. package/dist/core/authrules.js +26 -25
  33. package/dist/core/delayed.js +2 -1
  34. package/dist/core/events.js +11 -10
  35. package/dist/core/invitefilter.js +2 -1
  36. package/dist/core/keys.js +2 -1
  37. package/dist/core/m.d.ts +209 -0
  38. package/dist/core/m.js +182 -0
  39. package/dist/core/partialstate.js +2 -1
  40. package/dist/core/policy.js +4 -3
  41. package/dist/core/push.d.ts +0 -1
  42. package/dist/core/push.js +12 -11
  43. package/dist/core/pushrules.js +36 -35
  44. package/dist/core/rooms.js +76 -74
  45. package/dist/core/stateres.js +7 -6
  46. package/dist/federation/client.js +2 -1
  47. package/dist/federation/index.d.ts +1 -1
  48. package/dist/federation/index.js +30 -29
  49. package/dist/homeserver.d.ts +2 -1
  50. package/dist/homeserver.js +1 -0
  51. package/dist/http/handler.js +2 -23
  52. package/dist/http/input.js +2 -2
  53. package/dist/http/procedure.js +25 -0
  54. package/dist/http/router.d.ts +9 -26
  55. package/dist/http/router.js +10 -30
  56. package/dist/index.d.ts +2 -1
  57. package/dist/index.js +2 -1
  58. package/dist/vendor/zod/LICENSE +21 -0
  59. package/dist/vendor/zod/v4/classic/errors.js +54 -0
  60. package/dist/vendor/zod/v4/classic/parse.js +17 -0
  61. package/dist/vendor/zod/v4/classic/schemas.js +865 -0
  62. package/dist/vendor/zod/v4/core/api.js +512 -0
  63. package/dist/vendor/zod/v4/core/checks.js +300 -0
  64. package/dist/vendor/zod/v4/core/core.js +109 -0
  65. package/dist/vendor/zod/v4/core/doc.js +35 -0
  66. package/dist/vendor/zod/v4/core/errors.js +118 -0
  67. package/dist/vendor/zod/v4/core/json-schema-processors.js +428 -0
  68. package/dist/vendor/zod/v4/core/memoizer.js +255 -0
  69. package/dist/vendor/zod/v4/core/parse.js +206 -0
  70. package/dist/vendor/zod/v4/core/regexes.js +73 -0
  71. package/dist/vendor/zod/v4/core/registries.js +48 -0
  72. package/dist/vendor/zod/v4/core/schemas.js +1378 -0
  73. package/dist/vendor/zod/v4/core/to-json-schema.js +448 -0
  74. package/dist/vendor/zod/v4/core/util.js +537 -0
  75. package/dist/vendor/zod/v4/core/versions.js +8 -0
  76. package/dist/vendor/zod/v4/locales/en.js +110 -0
  77. package/package.json +3 -5
  78. package/dist/core/pushrules.d.ts +0 -1
@@ -1,16 +1,18 @@
1
+ import { M } from "../core/m.js";
1
2
  import { MatrixError, errors } from "../core/errors.js";
2
3
  import { canonicalAddress, isMedium } from "../core/threepid.js";
3
- import { requireUia } from "./uia.js";
4
+ import { enum as _enum, string } from "../vendor/zod/v4/classic/schemas.js";
4
5
  import { body, fail, lenient } from "../http/input.js";
5
- import { z } from "zod";
6
+ import { procedure } from "../http/procedure.js";
7
+ import { requireUia } from "./uia.js";
6
8
  //#region src/api/threepid.ts
7
- const submitField = z.string(fail("M_MISSING_PARAM", "sid, client_secret and token are required"));
9
+ const submitField = string(fail("M_MISSING_PARAM", "sid, client_secret and token are required"));
8
10
  const submitBody = body({
9
11
  sid: submitField,
10
12
  client_secret: submitField,
11
13
  token: submitField
12
14
  });
13
- const bindField = (name) => z.string(fail("M_MISSING_PARAM", `Missing ${name}`));
15
+ const bindField = (name) => string(fail("M_MISSING_PARAM", `Missing ${name}`));
14
16
  const bindBody = body({
15
17
  client_secret: bindField("client_secret"),
16
18
  id_access_token: bindField("id_access_token"),
@@ -18,9 +20,9 @@ const bindBody = body({
18
20
  sid: bindField("sid")
19
21
  });
20
22
  const threepidBody = body({
21
- medium: z.enum(["email", "msisdn"], fail("M_INVALID_PARAM", "Unsupported medium")),
22
- address: z.string(fail("M_INVALID_PARAM", "Invalid address")),
23
- id_server: z.string(fail("M_INVALID_PARAM", "id_server must be a string")).optional()
23
+ medium: _enum(["email", "msisdn"], fail("M_INVALID_PARAM", "Unsupported medium")),
24
+ address: string(fail("M_INVALID_PARAM", "Invalid address")),
25
+ id_server: string(fail("M_INVALID_PARAM", "id_server must be a string")).optional()
24
26
  });
25
27
  function notDelegated(ctx) {
26
28
  if (ctx.hs.oauth) throw errors.unrecognized("Managed by the OAuth provider");
@@ -32,12 +34,21 @@ function registerThreepidRoutes(r) {
32
34
  return ctx.hs.threepids.requestToken(purpose, medium, ctx.input);
33
35
  };
34
36
  for (const medium of ["email", "msisdn"]) {
35
- r.client("POST", `/register/${medium}/requestToken`, { input: body({}) }, requestToken("register", medium));
36
- r.client("POST", `/account/password/${medium}/requestToken`, { input: body({}) }, requestToken("password_reset", medium));
37
- r.client("POST", `/account/3pid/${medium}/requestToken`, { input: body({}) }, requestToken("add_threepid", medium));
37
+ r.client("POST", `/register/${medium}/requestToken`, procedure({
38
+ input: body({}),
39
+ handler: requestToken("register", medium)
40
+ }));
41
+ r.client("POST", `/account/password/${medium}/requestToken`, procedure({
42
+ input: body({}),
43
+ handler: requestToken("password_reset", medium)
44
+ }));
45
+ r.client("POST", `/account/3pid/${medium}/requestToken`, procedure({
46
+ input: body({}),
47
+ handler: requestToken("add_threepid", medium)
48
+ }));
38
49
  }
39
50
  const submitPath = "/_matrix/client/unstable/threepid/submit_token";
40
- r.get(submitPath, async (ctx) => {
51
+ r.get(submitPath, procedure({ handler: async (ctx) => {
41
52
  const q = ctx.query;
42
53
  let nextLink = null;
43
54
  let message = "Your address has been validated. You can return to your Matrix client.";
@@ -53,44 +64,57 @@ function registerThreepidRoutes(r) {
53
64
  status,
54
65
  headers: { "content-type": "text/html; charset=utf-8" }
55
66
  });
56
- });
57
- r.post(submitPath, { input: submitBody }, async (ctx) => {
58
- const body = ctx.input;
59
- await ctx.hs.threepids.submitToken(body.sid, body.client_secret, body.token);
60
- return { success: true };
61
- });
62
- r.client("GET", "/account/3pid", async (ctx) => {
63
- const s = await ctx.auth();
64
- return { threepids: await ctx.hs.threepids.list(s.userId) };
65
- });
66
- r.client("POST", "/account/3pid/add", { input: body({
67
- sid: lenient(z.string()),
68
- client_secret: lenient(z.string())
69
- }) }, async (ctx) => {
70
- notDelegated(ctx);
71
- const s = await ctx.auth();
72
- const body = ctx.input;
73
- await requireUia(ctx.hs, body, [["m.login.password"]], s.userId);
74
- const validated = await ctx.hs.threepids.validated({
75
- sid: body.sid,
76
- client_secret: body.client_secret
77
- });
78
- if (!validated) throw new MatrixError(400, "M_THREEPID_AUTH_FAILED", "The address has not been validated");
79
- await ctx.hs.threepids.add(s.userId, validated);
80
- return {};
81
- });
82
- r.client("POST", "/account/3pid/bind", { input: bindBody }, async (ctx) => {
83
- const s = await ctx.auth();
84
- const body = ctx.input;
85
- const res = await ctx.hs.identityServers.bind(body.id_server, body.id_access_token, body.sid, body.client_secret, s.userId);
86
- if (res && isMedium(res.medium) && typeof res.address === "string") await ctx.hs.db.upsert("threepid_bindings", {
87
- user_id: s.userId,
88
- medium: res.medium,
89
- address: res.address,
90
- id_server: body.id_server
91
- });
92
- return {};
93
- });
67
+ } }));
68
+ r.post(submitPath, procedure({
69
+ input: submitBody,
70
+ handler: async (ctx) => {
71
+ const body = ctx.input;
72
+ await ctx.hs.threepids.submitToken(body.sid, body.client_secret, body.token);
73
+ return { success: true };
74
+ }
75
+ }));
76
+ r.client("GET", "/account/3pid", procedure({
77
+ auth: "user",
78
+ handler: async (ctx) => {
79
+ const s = ctx.session;
80
+ return { threepids: await ctx.hs.threepids.list(s.userId) };
81
+ }
82
+ }));
83
+ r.client("POST", "/account/3pid/add", procedure({
84
+ input: body({
85
+ sid: lenient(string()),
86
+ client_secret: lenient(string())
87
+ }),
88
+ handler: async (ctx) => {
89
+ notDelegated(ctx);
90
+ const s = await ctx.auth();
91
+ const body = ctx.input;
92
+ await requireUia(ctx.hs, body, [[M.LOGIN.PASSWORD]], s.userId);
93
+ const validated = await ctx.hs.threepids.validated({
94
+ sid: body.sid,
95
+ client_secret: body.client_secret
96
+ });
97
+ if (!validated) throw new MatrixError(400, "M_THREEPID_AUTH_FAILED", "The address has not been validated");
98
+ await ctx.hs.threepids.add(s.userId, validated);
99
+ return {};
100
+ }
101
+ }));
102
+ r.client("POST", "/account/3pid/bind", procedure({
103
+ auth: "user",
104
+ input: bindBody,
105
+ handler: async (ctx) => {
106
+ const s = ctx.session;
107
+ const body = ctx.input;
108
+ const res = await ctx.hs.identityServers.bind(body.id_server, body.id_access_token, body.sid, body.client_secret, s.userId);
109
+ if (res && isMedium(res.medium) && typeof res.address === "string") await ctx.hs.db.upsert("threepid_bindings", {
110
+ user_id: s.userId,
111
+ medium: res.medium,
112
+ address: res.address,
113
+ id_server: body.id_server
114
+ });
115
+ return {};
116
+ }
117
+ }));
94
118
  const parseThreepid = (body) => {
95
119
  const address = canonicalAddress(body.medium, body.address);
96
120
  if (!address) throw errors.invalidParam("Invalid address");
@@ -100,23 +124,32 @@ function registerThreepidRoutes(r) {
100
124
  idServer: body.id_server
101
125
  };
102
126
  };
103
- r.client("POST", "/account/3pid/delete", { input: threepidBody }, async (ctx) => {
104
- notDelegated(ctx);
105
- const s = await ctx.auth();
106
- const { medium, address, idServer } = parseThreepid(ctx.input);
107
- const result = await ctx.hs.threepids.unbindAll(s.userId, medium, address, idServer);
108
- await ctx.hs.threepids.remove(s.userId, medium, address);
109
- return { id_server_unbind_result: result };
110
- });
111
- r.client("POST", "/account/3pid/unbind", { input: threepidBody }, async (ctx) => {
112
- const s = await ctx.auth();
113
- const { medium, address, idServer } = parseThreepid(ctx.input);
114
- return { id_server_unbind_result: await ctx.hs.threepids.unbindAll(s.userId, medium, address, idServer) };
115
- });
116
- r.client("POST", "/account/3pid", async (ctx) => {
117
- await ctx.auth();
118
- throw errors.threepidDenied();
119
- });
127
+ r.client("POST", "/account/3pid/delete", procedure({
128
+ input: threepidBody,
129
+ handler: async (ctx) => {
130
+ notDelegated(ctx);
131
+ const s = await ctx.auth();
132
+ const { medium, address, idServer } = parseThreepid(ctx.input);
133
+ const result = await ctx.hs.threepids.unbindAll(s.userId, medium, address, idServer);
134
+ await ctx.hs.threepids.remove(s.userId, medium, address);
135
+ return { id_server_unbind_result: result };
136
+ }
137
+ }));
138
+ r.client("POST", "/account/3pid/unbind", procedure({
139
+ auth: "user",
140
+ input: threepidBody,
141
+ handler: async (ctx) => {
142
+ const s = ctx.session;
143
+ const { medium, address, idServer } = parseThreepid(ctx.input);
144
+ return { id_server_unbind_result: await ctx.hs.threepids.unbindAll(s.userId, medium, address, idServer) };
145
+ }
146
+ }));
147
+ r.client("POST", "/account/3pid", procedure({
148
+ auth: "user",
149
+ handler: async () => {
150
+ throw errors.threepidDenied();
151
+ }
152
+ }));
120
153
  }
121
154
  //#endregion
122
155
  export { registerThreepidRoutes };
package/dist/api/uia.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { isObject } from "../core/json.js";
2
2
  import { randomString } from "../core/crypto.js";
3
+ import { M, UNSTABLE } from "../core/m.js";
3
4
  import { MatrixError, errors } from "../core/errors.js";
4
5
  //#region src/api/uia.ts
5
6
  const UIA_TTL_MS = 6e5;
@@ -9,7 +10,7 @@ const UIA_TTL_MS = 6e5;
9
10
  * `userId` binds password stages to an already-authenticated user.
10
11
  */
11
12
  async function requireUia(hs, body, flows, userId) {
12
- if (hs.oauth && flows.some((f) => f.includes("m.login.password"))) flows = [["m.oauth"], ["org.matrix.cross_signing_reset"]];
13
+ if (hs.oauth && flows.some((f) => f.includes(M.LOGIN.PASSWORD))) flows = [[M.OAUTH], [UNSTABLE.ORG.MATRIX.CROSS_SIGNING_RESET]];
13
14
  const auth = isObject(body.auth) ? body.auth : null;
14
15
  let sessionId = typeof auth?.session === "string" ? auth.session : void 0;
15
16
  const stored = sessionId ? await hs.cluster.get(`uia:${sessionId}`) : null;
@@ -26,8 +27,8 @@ async function requireUia(hs, body, flows, userId) {
26
27
  }, UIA_TTL_MS);
27
28
  const oauthUrl = hs.oauth?.options.accountManagementUrl ?? hs.oauth?.options.issuer;
28
29
  const params = hs.oauth ? {
29
- "m.oauth": { url: oauthUrl },
30
- "org.matrix.cross_signing_reset": { url: oauthUrl }
30
+ [M.OAUTH]: { url: oauthUrl },
31
+ [UNSTABLE.ORG.MATRIX.CROSS_SIGNING_RESET]: { url: oauthUrl }
31
32
  } : {};
32
33
  const challenge = (extra = {}) => new MatrixError(401, extra.errcode ? String(extra.errcode) : "M_FORBIDDEN", String(extra.error ?? "Authentication required"), {
33
34
  flows: flows.map((stages) => ({ stages })),
@@ -36,36 +37,36 @@ async function requireUia(hs, body, flows, userId) {
36
37
  completed: [...session.completed],
37
38
  ...extra
38
39
  });
39
- if (auth?.type === "m.login.dummy" && !auth.session && typeof body.username === "string" && !userId) {
40
+ if (auth?.type === M.LOGIN.DUMMY && !auth.session && typeof body.username === "string" && !userId) {
40
41
  if (await hs.cluster.get(`uia-register:${body.username.toLowerCase()}`)) throw challenge({
41
42
  errcode: "M_FORBIDDEN",
42
43
  error: "Missing session"
43
44
  });
44
45
  }
45
46
  if (auth?.type) switch (auth.type) {
46
- case "m.login.dummy":
47
- session.completed.add("m.login.dummy");
47
+ case M.LOGIN.DUMMY:
48
+ session.completed.add(M.LOGIN.DUMMY);
48
49
  break;
49
- case "m.oauth":
50
- case "org.matrix.cross_signing_reset":
50
+ case M.OAUTH:
51
+ case UNSTABLE.ORG.MATRIX.CROSS_SIGNING_RESET:
51
52
  if (!hs.oauth) throw challenge({
52
53
  errcode: "M_UNRECOGNIZED",
53
54
  error: "OAuth is not enabled"
54
55
  });
55
56
  session.completed.add(auth.type);
56
57
  break;
57
- case "m.login.registration_token": {
58
+ case M.LOGIN.REGISTRATION_TOKEN: {
58
59
  const { registrationTokenValid } = await import("./account.js");
59
60
  if (typeof auth.token !== "string" || !await registrationTokenValid(hs, auth.token)) throw challenge({
60
61
  errcode: "M_FORBIDDEN",
61
62
  error: "Invalid registration token"
62
63
  });
63
- session.completed.add("m.login.registration_token");
64
+ session.completed.add(M.LOGIN.REGISTRATION_TOKEN);
64
65
  break;
65
66
  }
66
- case "m.login.password": {
67
+ case M.LOGIN.PASSWORD: {
67
68
  const identifier = isObject(auth.identifier) ? auth.identifier : {
68
- type: "m.id.user",
69
+ type: M.ID.USER,
69
70
  user: auth.user
70
71
  };
71
72
  const user = typeof identifier.user === "string" ? identifier.user : "";
@@ -84,13 +85,13 @@ async function requireUia(hs, body, flows, userId) {
84
85
  errcode: "M_FORBIDDEN",
85
86
  error: "Invalid password"
86
87
  });
87
- session.completed.add("m.login.password");
88
+ session.completed.add(M.LOGIN.PASSWORD);
88
89
  break;
89
90
  }
90
- case "m.login.email.identity":
91
- case "m.login.msisdn": {
91
+ case M.LOGIN.EMAIL.IDENTITY:
92
+ case M.LOGIN.MSISDN: {
92
93
  const validated = await hs.threepids.validated(auth.threepid_creds ?? auth.threepidCreds);
93
- const medium = auth.type === "m.login.email.identity" ? "email" : "msisdn";
94
+ const medium = auth.type === M.LOGIN.EMAIL.IDENTITY ? "email" : "msisdn";
94
95
  if (!validated || validated.medium !== medium) throw challenge({
95
96
  errcode: "M_UNAUTHORIZED",
96
97
  error: "The address has not been validated"