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,24 @@
1
+ import { MatrixError } from "../core/errors.js";
2
+ import { z } from "zod";
3
+ //#region src/http/input.ts
4
+ /** A request body: a JSON object whose unknown keys are kept. */
5
+ const body = (shape) => z.looseObject(shape);
6
+ /**
7
+ * Errors carry their Matrix errcode in the message, as `M_CODE: text`: `fail` builds the zod
8
+ * error option for a field. Without one, a missing field is M_MISSING_PARAM and any other
9
+ * problem M_BAD_JSON.
10
+ */
11
+ const fail = (errcode, message, missing) => ({ error: (issue) => issue.input === void 0 && missing ? `M_MISSING_PARAM: ${missing}` : `${errcode}: ${message}` });
12
+ /** A field that, when its value has the wrong type, is ignored (treated as absent), as Matrix clients expect for optional fields. */
13
+ const lenient = (schema) => schema.optional().catch(void 0);
14
+ /** Turn a failed body parse into the Matrix error the client sees. */
15
+ function inputError(error) {
16
+ const issue = error.issues[0];
17
+ const path = issue?.path.join(".") ?? "";
18
+ const tagged = /^(M_[A-Z_]+): ([\s\S]*)$/.exec(issue?.message ?? "");
19
+ if (tagged) return new MatrixError(400, tagged[1], tagged[2]);
20
+ if (issue?.code === "invalid_type" && issue.input === void 0) return new MatrixError(400, "M_MISSING_PARAM", `Missing ${path || "parameter"}`);
21
+ return new MatrixError(400, "M_BAD_JSON", path ? `Invalid ${path}: ${issue.message}` : issue?.message ?? "Bad JSON");
22
+ }
23
+ //#endregion
24
+ export { body, fail, inputError, lenient };
@@ -0,0 +1,60 @@
1
+ import { Session } from "../core/auth.js";
2
+ import { RateLimitAction } from "../core/ratelimit.js";
3
+ import { Homeserver } from "../homeserver.js";
4
+ import { z } from "zod";
5
+ //#region src/http/router.d.ts
6
+ export type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
7
+ export interface RequestContext<I = unknown> {
8
+ hs: Homeserver;
9
+ request: Request;
10
+ url: URL;
11
+ method: string;
12
+ params: Record<string, string>;
13
+ query: URLSearchParams;
14
+ /** Remote address, if known. */
15
+ ip: string | null;
16
+ /** Parsed JSON body (empty object when no body was sent). */
17
+ json<T = any>(): Promise<T>;
18
+ bytes(): Promise<Uint8Array>;
19
+ /** Authenticate the request; throws M_MISSING_TOKEN / M_UNKNOWN_TOKEN. */
20
+ auth(): Promise<Session>;
21
+ /** Authenticate if a token was supplied. */
22
+ optionalAuth(): Promise<Session | null>;
23
+ accessToken(): string | null;
24
+ /** Apply the rate limit for `action` to the requesting user (or IP when unauthenticated). */
25
+ rateLimit(action: RateLimitAction): Promise<void>;
26
+ /**
27
+ * The request body parsed with the route's `input` schema (undefined for routes without one).
28
+ * Validated on first access, which throws the Matrix error for an invalid body: read it where
29
+ * the handler needs the body, after authenticating.
30
+ */
31
+ input: I;
32
+ }
33
+ export type HandlerResult = Response | object | void;
34
+ export type Handler<I = any> = (ctx: RequestContext<I>) => Promise<HandlerResult> | HandlerResult;
35
+ /** Per-route options. `input` validates and parses the JSON body before the handler runs. */
36
+ export interface RouteOptions<S extends z.ZodType = z.ZodType> {
37
+ input?: S;
38
+ }
39
+ /** A route's handler, with the body type its `input` schema produces. */
40
+ type Routed<S> = S extends z.ZodType ? Handler<z.output<S>> : Handler;
41
+ export declare class Router {
42
+ private routes;
43
+ add<S extends z.ZodType | undefined = undefined>(method: HttpMethod, path: string, handler: Routed<S>, options?: RouteOptions<NonNullable<S>>): void;
44
+ get(path: string, h: Handler): void;
45
+ post<S extends z.ZodType>(path: string, options: RouteOptions<S>, h: Routed<S>): void;
46
+ post(path: string, h: Handler): void;
47
+ put<S extends z.ZodType>(path: string, options: RouteOptions<S>, h: Routed<S>): void;
48
+ put(path: string, h: Handler): void;
49
+ delete(path: string, h: Handler): void;
50
+ /** Register a client-server API route under both `r0` and `v3` (or the given versions). */
51
+ client<S extends z.ZodType>(method: HttpMethod, path: string, options: RouteOptions<S>, handler: Routed<S>, versions?: string[]): void;
52
+ client(method: HttpMethod, path: string, handler: Handler, versions?: string[]): void;
53
+ match(method: string, pathname: string): {
54
+ handler: Handler;
55
+ params: Record<string, string>;
56
+ input?: z.ZodType;
57
+ } | "method" | null;
58
+ }
59
+ //#endregion
60
+ export {};
@@ -0,0 +1,76 @@
1
+ //#region src/http/router.ts
2
+ var Router = class {
3
+ routes = [];
4
+ add(method, path, handler, options = {}) {
5
+ this.routes.push({
6
+ method,
7
+ segments: path.split("/").filter(Boolean),
8
+ handler,
9
+ input: options.input
10
+ });
11
+ }
12
+ get(path, h) {
13
+ this.add("GET", path, h);
14
+ }
15
+ post(path, a, b) {
16
+ if (typeof a === "function") this.add("POST", path, a);
17
+ else this.add("POST", path, b, a);
18
+ }
19
+ put(path, a, b) {
20
+ if (typeof a === "function") this.add("PUT", path, a);
21
+ else this.add("PUT", path, b, a);
22
+ }
23
+ delete(path, h) {
24
+ this.add("DELETE", path, h);
25
+ }
26
+ client(method, path, a, b, c) {
27
+ const [options, handler, versions] = typeof a === "function" ? [
28
+ {},
29
+ a,
30
+ b ?? ["r0", "v3"]
31
+ ] : [
32
+ a,
33
+ b,
34
+ c ?? ["r0", "v3"]
35
+ ];
36
+ for (const v of versions) this.add(method, `/_matrix/client/${v}${path}`, handler, options);
37
+ }
38
+ match(method, pathname) {
39
+ const parts = pathname.split("/").filter(Boolean);
40
+ let methodMismatch = false;
41
+ for (const route of this.routes) {
42
+ const params = matchSegments(route.segments, parts);
43
+ if (!params) continue;
44
+ if (route.method !== method) {
45
+ methodMismatch = true;
46
+ continue;
47
+ }
48
+ return {
49
+ handler: route.handler,
50
+ params,
51
+ input: route.input
52
+ };
53
+ }
54
+ return methodMismatch ? "method" : null;
55
+ }
56
+ };
57
+ function matchSegments(segments, parts) {
58
+ const params = {};
59
+ for (let i = 0; i < segments.length; i++) {
60
+ const seg = segments[i];
61
+ if (seg.startsWith("*")) {
62
+ params[seg.slice(1)] = parts.slice(i).map(decodeURIComponent).join("/");
63
+ return params;
64
+ }
65
+ if (i >= parts.length) return null;
66
+ if (seg.startsWith(":")) try {
67
+ params[seg.slice(1)] = decodeURIComponent(parts[i]);
68
+ } catch {
69
+ return null;
70
+ }
71
+ else if (seg !== parts[i]) return null;
72
+ }
73
+ return segments.length === parts.length ? params : null;
74
+ }
75
+ //#endregion
76
+ export { Router };
@@ -0,0 +1,25 @@
1
+ import { ThreepidEmail, ThreepidOptions, ThreepidPurpose, ThreepidSms } from "./core/threepid.js";
2
+ import { AppServiceRegistration, loadAppServiceRegistrations, parseAppServiceRegistration } from "./core/appservices.js";
3
+ import { ClusterAdapter, localCluster } from "./core/cluster.js";
4
+ import { DatabaseAdapter, FieldDef, FindManyOptions, ModelDef, Operator, Row, Schema, WhereClause } from "./storage/types.js";
5
+ import { AdminService, ServerNoticesOptions } from "./core/admin.js";
6
+ import { OAuthDelegationOptions } from "./core/oauth.js";
7
+ import { SsoOptions, SsoProvider } from "./core/sso.js";
8
+ import { MatrixError } from "./core/errors.js";
9
+ import { FederationOptions } from "./federation/index.js";
10
+ import { DelayedEventsOptions } from "./core/delayed.js";
11
+ import { RateLimitAction, RateLimitRule, RateLimiter, memoryRateLimiter } from "./core/ratelimit.js";
12
+ import { RtcOptions, TurnOptions } from "./api/rtc.js";
13
+ import { AuditAction, AuditEntry, AuditOptions } from "./core/audit.js";
14
+ import { AdminRoomOptions } from "./core/adminroom.js";
15
+ import { PasswordCheckContext, UserAuthAdapter } from "./adapters/auth.js";
16
+ import { MediaAdapter, MediaMetadata, ThumbnailRequest, Thumbnailer, filesystemMedia, memoryMedia, sharpThumbnailer } from "./adapters/media.js";
17
+ import { Homeserver, HomeserverOptions, Logger, createHomeserver } from "./homeserver.js";
18
+ import { NodeHandler, getExpressHandler, getFastifyPlugin, getFetchHandler, getHonoHandler, getNodeHandler, isMatrixPath } from "./http/adapters.js";
19
+ import { handleRequest } from "./http/handler.js";
20
+ import { memoryAdapter } from "./storage/memory.js";
21
+ import { MysqlPoolLike, PgPoolLike, SqlAdapterOptions, SqlDialect, SqlExecutor, SqliteDatabaseLike, mysqlAdapter, postgresAdapter, sqlAdapter, sqliteAdapter } from "./storage/sql.js";
22
+ import { schema } from "./storage/schema.js";
23
+ import { S3MediaOptions, s3Media } from "./adapters/s3.js";
24
+ import { PgClusterPoolLike, RedisLike, RedisSubscriberLike, postgresCluster, redisCluster } from "./adapters/cluster.js";
25
+ export { type AdminRoomOptions, type AdminService, type AppServiceRegistration, type AuditAction, type AuditEntry, type AuditOptions, type ClusterAdapter, type DatabaseAdapter, type DelayedEventsOptions, type FederationOptions, type FieldDef, type FindManyOptions, Homeserver, type HomeserverOptions, type Logger, MatrixError, type MediaAdapter, type MediaMetadata, type ModelDef, type MysqlPoolLike, type NodeHandler, type OAuthDelegationOptions, type Operator, type PasswordCheckContext, type PgClusterPoolLike, type PgPoolLike, type RateLimitAction, type RateLimitRule, type RateLimiter, type RedisLike, type RedisSubscriberLike, type Row, type RtcOptions, type S3MediaOptions, type Schema, type ServerNoticesOptions, type SqlAdapterOptions, type SqlDialect, type SqlExecutor, type SqliteDatabaseLike, type SsoOptions, type SsoProvider, type ThreepidEmail, type ThreepidOptions, type ThreepidPurpose, type ThreepidSms, type ThumbnailRequest, type Thumbnailer, type TurnOptions, type UserAuthAdapter, type WhereClause, createHomeserver, filesystemMedia, getExpressHandler, getFastifyPlugin, getFetchHandler, getHonoHandler, getNodeHandler, handleRequest, isMatrixPath, loadAppServiceRegistrations, localCluster, memoryAdapter, memoryMedia, memoryRateLimiter, mysqlAdapter, parseAppServiceRegistration, postgresAdapter, postgresCluster, redisCluster, s3Media, schema, sharpThumbnailer, sqlAdapter, sqliteAdapter };
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ import { MatrixError } from "./core/errors.js";
2
+ import { localCluster } from "./core/cluster.js";
3
+ import { loadAppServiceRegistrations, parseAppServiceRegistration } from "./core/appservices.js";
4
+ import { memoryRateLimiter } from "./core/ratelimit.js";
5
+ import { filesystemMedia, memoryMedia, sharpThumbnailer } from "./adapters/media.js";
6
+ import { memoryAdapter } from "./storage/memory.js";
7
+ import { schema } from "./storage/schema.js";
8
+ import { handleRequest } from "./http/handler.js";
9
+ import { Homeserver, createHomeserver } from "./homeserver.js";
10
+ import { getExpressHandler, getFastifyPlugin, getFetchHandler, getHonoHandler, getNodeHandler, isMatrixPath } from "./http/adapters.js";
11
+ import { mysqlAdapter, postgresAdapter, sqlAdapter, sqliteAdapter } from "./storage/sql.js";
12
+ import { s3Media } from "./adapters/s3.js";
13
+ import { postgresCluster, redisCluster } from "./adapters/cluster.js";
14
+ export { Homeserver, MatrixError, createHomeserver, filesystemMedia, getExpressHandler, getFastifyPlugin, getFetchHandler, getHonoHandler, getNodeHandler, handleRequest, isMatrixPath, loadAppServiceRegistrations, localCluster, memoryAdapter, memoryMedia, memoryRateLimiter, mysqlAdapter, parseAppServiceRegistration, postgresAdapter, postgresCluster, redisCluster, s3Media, schema, sharpThumbnailer, sqlAdapter, sqliteAdapter };
@@ -0,0 +1,5 @@
1
+ import { DatabaseAdapter } from "./types.js";
2
+ //#region src/storage/memory.d.ts
3
+ /** In-memory adapter. Data is lost on restart; useful for tests and development. */
4
+ export declare function memoryAdapter(): DatabaseAdapter;
5
+ //#endregion
@@ -0,0 +1,122 @@
1
+ //#region src/storage/memory.ts
2
+ function matches(row, where = []) {
3
+ for (const w of where) {
4
+ const v = row[w.field] ?? null;
5
+ const target = w.value ?? null;
6
+ switch (w.operator ?? "eq") {
7
+ case "eq":
8
+ if (v !== target) return false;
9
+ break;
10
+ case "ne":
11
+ if (v === target) return false;
12
+ break;
13
+ case "lt":
14
+ if (!(v < target)) return false;
15
+ break;
16
+ case "lte":
17
+ if (!(v <= target)) return false;
18
+ break;
19
+ case "gt":
20
+ if (!(v > target)) return false;
21
+ break;
22
+ case "gte":
23
+ if (!(v >= target)) return false;
24
+ break;
25
+ case "in": if (!target.includes(v)) return false;
26
+ }
27
+ }
28
+ return true;
29
+ }
30
+ /** In-memory adapter. Data is lost on restart; useful for tests and development. */
31
+ function memoryAdapter() {
32
+ const tables = /* @__PURE__ */ new Map();
33
+ const pks = /* @__PURE__ */ new Map();
34
+ const table = (model) => {
35
+ let t = tables.get(model);
36
+ if (!t) tables.set(model, t = /* @__PURE__ */ new Map());
37
+ return t;
38
+ };
39
+ const keyOf = (model, row) => JSON.stringify((pks.get(model) ?? Object.keys(row)).map((k) => row[k]));
40
+ const build = (journal) => {
41
+ const set = (model, key, row) => {
42
+ const t = table(model);
43
+ journal?.push({
44
+ model,
45
+ key,
46
+ row: t.get(key)
47
+ });
48
+ if (row === void 0) t.delete(key);
49
+ else t.set(key, row);
50
+ };
51
+ return {
52
+ async migrate(schema) {
53
+ for (const [name, def] of Object.entries(schema)) pks.set(name, def.primaryKey);
54
+ },
55
+ async create(model, data) {
56
+ const key = keyOf(model, data);
57
+ if (table(model).has(key)) throw new Error(`Duplicate primary key in ${model}`);
58
+ set(model, key, structuredClone(data));
59
+ },
60
+ async upsert(model, data) {
61
+ set(model, keyOf(model, data), structuredClone(data));
62
+ },
63
+ async findOne(model, where) {
64
+ for (const row of table(model).values()) if (matches(row, where)) return structuredClone(row);
65
+ return null;
66
+ },
67
+ async findMany(model, opts = {}) {
68
+ let rows = [...table(model).values()].filter((r) => matches(r, opts.where));
69
+ if (opts.sortBy) {
70
+ const { field, direction } = opts.sortBy;
71
+ const dir = direction === "asc" ? 1 : -1;
72
+ rows.sort((a, b) => a[field] < b[field] ? -dir : a[field] > b[field] ? dir : 0);
73
+ }
74
+ if (opts.limit !== void 0) rows = rows.slice(0, opts.limit);
75
+ return rows.map((r) => structuredClone(r));
76
+ },
77
+ async update(model, where, data) {
78
+ let count = 0;
79
+ for (const [key, row] of [...table(model).entries()]) {
80
+ if (!matches(row, where)) continue;
81
+ const updated = {
82
+ ...row,
83
+ ...structuredClone(data)
84
+ };
85
+ set(model, key, void 0);
86
+ set(model, keyOf(model, updated), updated);
87
+ count++;
88
+ }
89
+ return count;
90
+ },
91
+ async delete(model, where) {
92
+ let count = 0;
93
+ for (const [key, row] of [...table(model).entries()]) if (matches(row, where)) {
94
+ set(model, key, void 0);
95
+ count++;
96
+ }
97
+ return count;
98
+ },
99
+ async max(model, field) {
100
+ let max = null;
101
+ for (const row of table(model).values()) {
102
+ const v = row[field];
103
+ if (typeof v === "number" && (max === null || v > max)) max = v;
104
+ }
105
+ return max;
106
+ },
107
+ async transaction(fn) {
108
+ const log = [];
109
+ try {
110
+ return await fn(build(log));
111
+ } catch (err) {
112
+ for (const entry of log.reverse()) if (entry.row === void 0) table(entry.model).delete(entry.key);
113
+ else table(entry.model).set(entry.key, entry.row);
114
+ throw err;
115
+ }
116
+ }
117
+ };
118
+ };
119
+ return build(null);
120
+ }
121
+ //#endregion
122
+ export { matches, memoryAdapter };
@@ -0,0 +1,4 @@
1
+ import { Schema } from "./types.js";
2
+ //#region src/storage/schema.d.ts
3
+ export declare const schema: Schema;
4
+ //#endregion