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.
- package/README.md +29 -7
- package/dist/api/account.js +385 -351
- package/dist/api/admin.js +76 -60
- package/dist/api/appservice.js +37 -25
- package/dist/api/backup.js +73 -62
- package/dist/api/federation.js +172 -138
- package/dist/api/filters.js +4 -3
- package/dist/api/legacy.js +91 -78
- package/dist/api/media.js +70 -54
- package/dist/api/misc.js +46 -37
- package/dist/api/preview.js +4 -3
- package/dist/api/profile.js +108 -97
- package/dist/api/push.js +114 -99
- package/dist/api/relationships.js +64 -58
- package/dist/api/rooms.js +703 -580
- package/dist/api/rtc.d.ts +0 -1
- package/dist/api/rtc.js +48 -34
- package/dist/api/search.js +133 -127
- package/dist/api/slidingsync.js +41 -31
- package/dist/api/spaces.js +82 -77
- package/dist/api/sync.js +59 -50
- package/dist/api/threadsubs.js +72 -61
- package/dist/api/threepid.js +99 -66
- package/dist/api/uia.js +17 -16
- package/dist/api/user.js +221 -178
- package/dist/core/admin.d.ts +0 -1
- package/dist/core/admin.js +6 -5
- package/dist/core/adminroom.js +9 -8
- package/dist/core/appservices.d.ts +2 -1
- package/dist/core/appservices.js +5 -4
- package/dist/core/audit.d.ts +0 -1
- package/dist/core/authrules.js +26 -25
- package/dist/core/delayed.js +2 -1
- package/dist/core/events.js +11 -10
- package/dist/core/invitefilter.js +2 -1
- package/dist/core/keys.js +2 -1
- package/dist/core/m.d.ts +209 -0
- package/dist/core/m.js +182 -0
- package/dist/core/partialstate.js +2 -1
- package/dist/core/policy.js +4 -3
- package/dist/core/push.d.ts +0 -1
- package/dist/core/push.js +12 -11
- package/dist/core/pushrules.js +36 -35
- package/dist/core/rooms.js +76 -74
- package/dist/core/stateres.js +7 -6
- package/dist/federation/client.js +2 -1
- package/dist/federation/index.d.ts +1 -1
- package/dist/federation/index.js +30 -29
- package/dist/homeserver.d.ts +2 -1
- package/dist/homeserver.js +1 -0
- package/dist/http/handler.js +2 -23
- package/dist/http/input.js +2 -2
- package/dist/http/procedure.js +25 -0
- package/dist/http/router.d.ts +9 -26
- package/dist/http/router.js +10 -30
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/vendor/zod/LICENSE +21 -0
- package/dist/vendor/zod/v4/classic/errors.js +54 -0
- package/dist/vendor/zod/v4/classic/parse.js +17 -0
- package/dist/vendor/zod/v4/classic/schemas.js +865 -0
- package/dist/vendor/zod/v4/core/api.js +512 -0
- package/dist/vendor/zod/v4/core/checks.js +300 -0
- package/dist/vendor/zod/v4/core/core.js +109 -0
- package/dist/vendor/zod/v4/core/doc.js +35 -0
- package/dist/vendor/zod/v4/core/errors.js +118 -0
- package/dist/vendor/zod/v4/core/json-schema-processors.js +428 -0
- package/dist/vendor/zod/v4/core/memoizer.js +255 -0
- package/dist/vendor/zod/v4/core/parse.js +206 -0
- package/dist/vendor/zod/v4/core/regexes.js +73 -0
- package/dist/vendor/zod/v4/core/registries.js +48 -0
- package/dist/vendor/zod/v4/core/schemas.js +1378 -0
- package/dist/vendor/zod/v4/core/to-json-schema.js +448 -0
- package/dist/vendor/zod/v4/core/util.js +537 -0
- package/dist/vendor/zod/v4/core/versions.js +8 -0
- package/dist/vendor/zod/v4/locales/en.js +110 -0
- package/package.json +3 -5
- package/dist/core/pushrules.d.ts +0 -1
package/dist/core/rooms.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isObject } from "./json.js";
|
|
2
|
+
import { M, UNSTABLE } from "./m.js";
|
|
2
3
|
import { SUPPORTED_ROOM_VERSIONS, eventIdOf, finalisePdu, redact, roomVersionFeatures, splitStateKey, stateKey, toClientEvent } from "./events.js";
|
|
3
4
|
import { MatrixError, errors } from "./errors.js";
|
|
4
5
|
import { generateRoomId, isValidUserId, parseId } from "./ids.js";
|
|
@@ -280,7 +281,7 @@ var RoomService = class {
|
|
|
280
281
|
return state;
|
|
281
282
|
}
|
|
282
283
|
async createEventOf(roomId) {
|
|
283
|
-
return getState(await this.currentState(roomId),
|
|
284
|
+
return getState(await this.currentState(roomId), M.ROOM.CREATE);
|
|
284
285
|
}
|
|
285
286
|
/**
|
|
286
287
|
* State before an event with the given prev_events: the snapshot of a single
|
|
@@ -384,7 +385,7 @@ var RoomService = class {
|
|
|
384
385
|
pdu.auth_events = pdu.auth_events.map((id) => [id, {}]);
|
|
385
386
|
}
|
|
386
387
|
if (!skipAuth) {
|
|
387
|
-
if (features.v12) before.set(stateKey(
|
|
388
|
+
if (features.v12) before.set(stateKey(M.ROOM.CREATE, ""), await this.createEventOf(roomId));
|
|
388
389
|
checkAuth(pdu, before, version);
|
|
389
390
|
}
|
|
390
391
|
if (Buffer.byteLength(JSON.stringify(pdu)) > MAX_EVENT_SIZE) throw errors.tooLarge("Event too large");
|
|
@@ -401,7 +402,7 @@ var RoomService = class {
|
|
|
401
402
|
eventId,
|
|
402
403
|
status: "persisted"
|
|
403
404
|
};
|
|
404
|
-
const createEvent = features.v12 ? pdu.type ===
|
|
405
|
+
const createEvent = features.v12 ? pdu.type === M.ROOM.CREATE ? void 0 : opts.createEvent ?? await this.createEventOf(roomId) ?? await this.findCreateEvent(roomId) : void 0;
|
|
405
406
|
const prevIds = (pdu.prev_events ?? []).map(eventIdOf);
|
|
406
407
|
if (!opts.local) {
|
|
407
408
|
const reject = async (reason) => {
|
|
@@ -436,7 +437,7 @@ var RoomService = class {
|
|
|
436
437
|
};
|
|
437
438
|
else before = await this.stateBefore(roomId, version, prevIds, createEvent);
|
|
438
439
|
const beforeMap = await this.stateIdsToMap(before.ids);
|
|
439
|
-
if (createEvent) beforeMap.set(stateKey(
|
|
440
|
+
if (createEvent) beforeMap.set(stateKey(M.ROOM.CREATE, ""), createEvent);
|
|
440
441
|
const partial = !opts.local && !opts.backfill && await this.hs.partialState.isPartial(roomId);
|
|
441
442
|
if (partial) {
|
|
442
443
|
const authPdus = await this.getPdus((pdu.auth_events ?? []).map(eventIdOf));
|
|
@@ -469,7 +470,7 @@ var RoomService = class {
|
|
|
469
470
|
}
|
|
470
471
|
if (!opts.backfill && !opts.stateBefore && !partial) {
|
|
471
472
|
const current = new Map(await this.currentState(roomId));
|
|
472
|
-
if (createEvent) current.set(stateKey(
|
|
473
|
+
if (createEvent) current.set(stateKey(M.ROOM.CREATE, ""), createEvent);
|
|
473
474
|
const softFail = async () => {
|
|
474
475
|
const ids = pdu.state_key !== void 0 ? {
|
|
475
476
|
...before.ids,
|
|
@@ -539,7 +540,7 @@ var RoomService = class {
|
|
|
539
540
|
}], { state_snapshot: snapshot });
|
|
540
541
|
this.eventCache.delete(eventId);
|
|
541
542
|
};
|
|
542
|
-
const joinKey = stateKey(
|
|
543
|
+
const joinKey = stateKey(M.ROOM.MEMBER, join.pdu.state_key);
|
|
543
544
|
await setSnapshot(joinEventId, await writeSnapshot({
|
|
544
545
|
...fullBefore,
|
|
545
546
|
[joinKey]: joinEventId
|
|
@@ -586,7 +587,7 @@ var RoomService = class {
|
|
|
586
587
|
continue;
|
|
587
588
|
}
|
|
588
589
|
const beforeMap = await this.stateIdsToMap(before.ids);
|
|
589
|
-
if (createEvent) beforeMap.set(stateKey(
|
|
590
|
+
if (createEvent) beforeMap.set(stateKey(M.ROOM.CREATE, ""), createEvent);
|
|
590
591
|
let allowed = true;
|
|
591
592
|
let reason = "";
|
|
592
593
|
if (!this.hs.isLocalUser(pdu.sender)) try {
|
|
@@ -627,7 +628,7 @@ var RoomService = class {
|
|
|
627
628
|
touched.push(pdu.event_id);
|
|
628
629
|
continue;
|
|
629
630
|
}
|
|
630
|
-
if (pdu.type ===
|
|
631
|
+
if (pdu.type === M.ROOM.MEMBER && pdu.content?.membership === "join") joinedServers.add(pdu.state_key.slice(pdu.state_key.indexOf(":") + 1));
|
|
631
632
|
const isState = pdu.state_key !== void 0;
|
|
632
633
|
const snapshot = !isState && before.snapshot !== null && before.snapshot !== 0 ? before.snapshot : await writeSnapshot(isState ? {
|
|
633
634
|
...before.ids,
|
|
@@ -698,7 +699,7 @@ var RoomService = class {
|
|
|
698
699
|
];
|
|
699
700
|
if (!newIds[k]) {
|
|
700
701
|
await db.delete("current_state", where);
|
|
701
|
-
if (type ===
|
|
702
|
+
if (type === M.ROOM.MEMBER) await db.delete("memberships", [{
|
|
702
703
|
field: "room_id",
|
|
703
704
|
value: roomId
|
|
704
705
|
}, {
|
|
@@ -713,7 +714,7 @@ var RoomService = class {
|
|
|
713
714
|
state_key: sk,
|
|
714
715
|
event_id: newIds[k]
|
|
715
716
|
});
|
|
716
|
-
if (type ===
|
|
717
|
+
if (type === M.ROOM.MEMBER) await db.upsert("memberships", {
|
|
717
718
|
room_id: roomId,
|
|
718
719
|
user_id: sk,
|
|
719
720
|
membership: pdus.get(newIds[k])?.content?.membership ?? "leave",
|
|
@@ -732,7 +733,7 @@ var RoomService = class {
|
|
|
732
733
|
});
|
|
733
734
|
for (const k of Object.keys(oldIds)) {
|
|
734
735
|
const [type, userId] = splitStateKey(k);
|
|
735
|
-
if (type !==
|
|
736
|
+
if (type !== M.ROOM.MEMBER || this.hs.isLocalUser(userId) || oldIds[k] === newIds[k]) continue;
|
|
736
737
|
if (!await this.hs.keys.isTracked(userId)) await this.hs.keys.dropRemoteCache(userId);
|
|
737
738
|
}
|
|
738
739
|
for (const s of this.hs.federation.serversInRoom(await this.currentState(roomId), false)) joinedServers.add(s);
|
|
@@ -784,7 +785,7 @@ var RoomService = class {
|
|
|
784
785
|
unsigned.prev_sender = prevState.sender;
|
|
785
786
|
unsigned.replaces_state = prevState.event_id;
|
|
786
787
|
}
|
|
787
|
-
if (pdu.type ===
|
|
788
|
+
if (pdu.type === M.ROOM.MEMBER && ["invite", "knock"].includes(pdu.content.membership) && opts.local) unsigned[pdu.content.membership === "invite" ? "invite_room_state" : "knock_room_state"] = this.strippedState(beforeMap);
|
|
788
789
|
const stored = Object.keys(unsigned).length ? {
|
|
789
790
|
...pdu,
|
|
790
791
|
unsigned
|
|
@@ -794,9 +795,9 @@ var RoomService = class {
|
|
|
794
795
|
room_id: roomId
|
|
795
796
|
};
|
|
796
797
|
const txnToken = opts.txnId && opts.deviceId ? txnTokenOf(sender, opts.deviceId) : null;
|
|
797
|
-
const relation = pdu.content?.[
|
|
798
|
+
const relation = pdu.content?.[M.RELATES_TO];
|
|
798
799
|
const redactsId = features.v11 ? pdu.content?.redacts : pdu.redacts;
|
|
799
|
-
const redaction = pdu.type ===
|
|
800
|
+
const redaction = pdu.type === M.ROOM.REDACTION && typeof redactsId === "string" ? await this.prepareRedaction(redactsId, view, version) : null;
|
|
800
801
|
let newExtremities = null;
|
|
801
802
|
let stateChanges = [];
|
|
802
803
|
let newCurrentIds = null;
|
|
@@ -812,7 +813,7 @@ var RoomService = class {
|
|
|
812
813
|
const snap = await this.snapshotOf(ext);
|
|
813
814
|
if (snap !== null) sets.push(await this.snapshotIds(snap));
|
|
814
815
|
}
|
|
815
|
-
const createEvent = features.v12 ? beforeMap.get(stateKey(
|
|
816
|
+
const createEvent = features.v12 ? beforeMap.get(stateKey(M.ROOM.CREATE, "")) ?? (pdu.type === M.ROOM.CREATE ? pdu : void 0) : void 0;
|
|
816
817
|
newCurrentIds = await resolveState(sets, version, async (ids) => {
|
|
817
818
|
const found = await this.getPdus(ids);
|
|
818
819
|
if (ids.includes(eventId)) found.set(eventId, view);
|
|
@@ -847,7 +848,7 @@ var RoomService = class {
|
|
|
847
848
|
field: "event_id",
|
|
848
849
|
value: eventId
|
|
849
850
|
}]);
|
|
850
|
-
const edge = pdu.content?.[
|
|
851
|
+
const edge = pdu.content?.[M.RELATIONSHIP];
|
|
851
852
|
if (isObject(edge) && typeof edge.event_id === "string" && typeof edge.rel_type === "string") await db.upsert("event_edges", {
|
|
852
853
|
event_id: eventId,
|
|
853
854
|
room_id: roomId,
|
|
@@ -896,7 +897,7 @@ var RoomService = class {
|
|
|
896
897
|
];
|
|
897
898
|
if (change.eventId === null) {
|
|
898
899
|
await db.delete("current_state", where);
|
|
899
|
-
if (type ===
|
|
900
|
+
if (type === M.ROOM.MEMBER) await db.delete("memberships", [{
|
|
900
901
|
field: "room_id",
|
|
901
902
|
value: roomId
|
|
902
903
|
}, {
|
|
@@ -911,7 +912,7 @@ var RoomService = class {
|
|
|
911
912
|
state_key: sk,
|
|
912
913
|
event_id: change.eventId
|
|
913
914
|
});
|
|
914
|
-
if (type ===
|
|
915
|
+
if (type === M.ROOM.MEMBER) {
|
|
915
916
|
const member = changedPdus.get(change.eventId);
|
|
916
917
|
await db.upsert("memberships", {
|
|
917
918
|
room_id: roomId,
|
|
@@ -1106,15 +1107,15 @@ var RoomService = class {
|
|
|
1106
1107
|
*/
|
|
1107
1108
|
strippedState(state) {
|
|
1108
1109
|
const types = [
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1110
|
+
M.ROOM.CREATE,
|
|
1111
|
+
M.ROOM.JOIN_RULES,
|
|
1112
|
+
M.ROOM.NAME,
|
|
1113
|
+
M.ROOM.AVATAR,
|
|
1114
|
+
M.ROOM.CANONICAL_ALIAS,
|
|
1115
|
+
M.ROOM.ENCRYPTION,
|
|
1116
|
+
M.ROOM.TOPIC
|
|
1116
1117
|
];
|
|
1117
|
-
const create = getState(state,
|
|
1118
|
+
const create = getState(state, M.ROOM.CREATE);
|
|
1118
1119
|
const version = typeof create?.content?.room_version === "string" ? create.content.room_version : "1";
|
|
1119
1120
|
const f = roomVersionFeatures(version);
|
|
1120
1121
|
const out = [];
|
|
@@ -1124,7 +1125,7 @@ var RoomService = class {
|
|
|
1124
1125
|
const { unsigned, ...full } = ev;
|
|
1125
1126
|
const wire = { ...full };
|
|
1126
1127
|
if (!f.legacyEventIds) delete wire.event_id;
|
|
1127
|
-
if (f.v12 && t ===
|
|
1128
|
+
if (f.v12 && t === M.ROOM.CREATE) delete wire.room_id;
|
|
1128
1129
|
out.push(wire);
|
|
1129
1130
|
}
|
|
1130
1131
|
return out;
|
|
@@ -1143,7 +1144,7 @@ var RoomService = class {
|
|
|
1143
1144
|
},
|
|
1144
1145
|
{
|
|
1145
1146
|
field: "type",
|
|
1146
|
-
value:
|
|
1147
|
+
value: M.ROOM.MEMBER
|
|
1147
1148
|
},
|
|
1148
1149
|
{
|
|
1149
1150
|
field: "state_key",
|
|
@@ -1160,18 +1161,18 @@ var RoomService = class {
|
|
|
1160
1161
|
const ids = await this.snapshotIds(raw.snapshot);
|
|
1161
1162
|
let membership = "leave";
|
|
1162
1163
|
if (userId) {
|
|
1163
|
-
const memId = ids[stateKey(
|
|
1164
|
+
const memId = ids[stateKey(M.ROOM.MEMBER, userId)];
|
|
1164
1165
|
membership = memId ? (await this.getEvent(memId))?.pdu.content?.membership ?? "leave" : "leave";
|
|
1165
1166
|
}
|
|
1166
1167
|
const ev = userId ? {
|
|
1167
1168
|
...raw,
|
|
1168
1169
|
viewerMembership: membership
|
|
1169
1170
|
} : raw;
|
|
1170
|
-
if (userId && ev.pdu.type ===
|
|
1171
|
+
if (userId && ev.pdu.type === M.ROOM.MEMBER && ev.pdu.state_key === userId) {
|
|
1171
1172
|
out.push(ev);
|
|
1172
1173
|
continue;
|
|
1173
1174
|
}
|
|
1174
|
-
const hvId = ids[stateKey(
|
|
1175
|
+
const hvId = ids[stateKey(M.ROOM.HISTORY_VISIBILITY, "")];
|
|
1175
1176
|
const hv = hvId ? (await this.getEvent(hvId))?.pdu.content?.history_visibility ?? "shared" : "shared";
|
|
1176
1177
|
if (hv === "world_readable") {
|
|
1177
1178
|
out.push(ev);
|
|
@@ -1200,7 +1201,7 @@ var RoomService = class {
|
|
|
1200
1201
|
async canPeek(roomId, userId) {
|
|
1201
1202
|
const state = await this.currentState(roomId);
|
|
1202
1203
|
const membership = membershipOf(state, userId);
|
|
1203
|
-
const hv = getState(state,
|
|
1204
|
+
const hv = getState(state, M.ROOM.HISTORY_VISIBILITY)?.content?.history_visibility ?? "shared";
|
|
1204
1205
|
return {
|
|
1205
1206
|
joined: membership === "join",
|
|
1206
1207
|
worldReadable: hv === "world_readable",
|
|
@@ -1210,7 +1211,7 @@ var RoomService = class {
|
|
|
1210
1211
|
/** The state snapshot visible to a user who is no longer joined (state at their leave). */
|
|
1211
1212
|
async membershipEvent(roomId, userId) {
|
|
1212
1213
|
const state = await this.currentState(roomId);
|
|
1213
|
-
const ev = getState(state,
|
|
1214
|
+
const ev = getState(state, M.ROOM.MEMBER, userId);
|
|
1214
1215
|
return ev?.event_id ? this.getEvent(ev.event_id) : null;
|
|
1215
1216
|
}
|
|
1216
1217
|
formatEvent(ev, viewer, opts = {}) {
|
|
@@ -1278,7 +1279,7 @@ var RoomService = class {
|
|
|
1278
1279
|
if (features.v12) {
|
|
1279
1280
|
prebuiltCreate = finalisePdu({
|
|
1280
1281
|
sender,
|
|
1281
|
-
type:
|
|
1282
|
+
type: M.ROOM.CREATE,
|
|
1282
1283
|
state_key: "",
|
|
1283
1284
|
content: createContent,
|
|
1284
1285
|
origin_server_ts: Date.now(),
|
|
@@ -1301,30 +1302,31 @@ var RoomService = class {
|
|
|
1301
1302
|
const pdu = prebuiltCreate;
|
|
1302
1303
|
await this.withLock(roomId, () => this.persistEvent(roomId, version, pdu, { local: true }));
|
|
1303
1304
|
} else await send({
|
|
1304
|
-
type:
|
|
1305
|
+
type: M.ROOM.CREATE,
|
|
1305
1306
|
state_key: "",
|
|
1306
1307
|
content: createContent
|
|
1307
1308
|
});
|
|
1309
|
+
const profile = await this.hs.profile(sender);
|
|
1308
1310
|
await send({
|
|
1309
|
-
type:
|
|
1311
|
+
type: M.ROOM.MEMBER,
|
|
1310
1312
|
state_key: sender,
|
|
1311
1313
|
content: {
|
|
1312
1314
|
membership: "join",
|
|
1313
|
-
...
|
|
1315
|
+
...profile
|
|
1314
1316
|
}
|
|
1315
1317
|
});
|
|
1316
1318
|
const pl = {
|
|
1317
1319
|
users: features.v12 ? {} : { [sender]: 100 },
|
|
1318
1320
|
users_default: 0,
|
|
1319
1321
|
events: {
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1322
|
+
[M.ROOM.NAME]: 50,
|
|
1323
|
+
[M.ROOM.POWER_LEVELS]: 100,
|
|
1324
|
+
[M.ROOM.HISTORY_VISIBILITY]: 100,
|
|
1325
|
+
[M.ROOM.CANONICAL_ALIAS]: 50,
|
|
1326
|
+
[M.ROOM.AVATAR]: 50,
|
|
1327
|
+
[M.ROOM.TOMBSTONE]: features.v12 ? 150 : 100,
|
|
1328
|
+
[M.ROOM.SERVER_ACL]: 100,
|
|
1329
|
+
[M.ROOM.ENCRYPTION]: 100
|
|
1328
1330
|
},
|
|
1329
1331
|
events_default: 0,
|
|
1330
1332
|
state_default: 50,
|
|
@@ -1338,7 +1340,7 @@ var RoomService = class {
|
|
|
1338
1340
|
const override = body.power_level_content_override;
|
|
1339
1341
|
if (override !== void 0 && !isObject(override)) throw errors.badJson("power_level_content_override must be an object");
|
|
1340
1342
|
const initialState = body.initial_state ?? [];
|
|
1341
|
-
const initialPl = initialState.find((e) => e?.type ===
|
|
1343
|
+
const initialPl = initialState.find((e) => e?.type === M.ROOM.POWER_LEVELS && (e.state_key ?? "") === "");
|
|
1342
1344
|
const predecessor = createContent.predecessor?.room_id;
|
|
1343
1345
|
if (typeof predecessor === "string") await copyRoomPushRules(this.hs, sender, predecessor, roomId);
|
|
1344
1346
|
const plContent = {
|
|
@@ -1348,7 +1350,7 @@ var RoomService = class {
|
|
|
1348
1350
|
};
|
|
1349
1351
|
validatePowerLevelContent(plContent, creators);
|
|
1350
1352
|
await send({
|
|
1351
|
-
type:
|
|
1353
|
+
type: M.ROOM.POWER_LEVELS,
|
|
1352
1354
|
state_key: "",
|
|
1353
1355
|
content: plContent
|
|
1354
1356
|
});
|
|
@@ -1359,13 +1361,13 @@ var RoomService = class {
|
|
|
1359
1361
|
creator: sender
|
|
1360
1362
|
});
|
|
1361
1363
|
await send({
|
|
1362
|
-
type:
|
|
1364
|
+
type: M.ROOM.CANONICAL_ALIAS,
|
|
1363
1365
|
state_key: "",
|
|
1364
1366
|
content: { alias }
|
|
1365
1367
|
});
|
|
1366
1368
|
}
|
|
1367
|
-
const presetState = [[
|
|
1368
|
-
if (preset !== "public_chat") presetState.push([
|
|
1369
|
+
const presetState = [[M.ROOM.JOIN_RULES, { join_rule: preset === "public_chat" ? "public" : "invite" }], [M.ROOM.HISTORY_VISIBILITY, { history_visibility: "shared" }]];
|
|
1370
|
+
if (preset !== "public_chat") presetState.push([M.ROOM.GUEST_ACCESS, { guest_access: "can_join" }]);
|
|
1369
1371
|
const initialKeys = new Set(initialState.map((e) => stateKey(e?.type, e?.state_key ?? "")));
|
|
1370
1372
|
for (const [type, content] of presetState) if (!initialKeys.has(stateKey(type, ""))) await send({
|
|
1371
1373
|
type,
|
|
@@ -1374,7 +1376,7 @@ var RoomService = class {
|
|
|
1374
1376
|
});
|
|
1375
1377
|
for (const ev of initialState) {
|
|
1376
1378
|
if (!isObject(ev) || typeof ev.type !== "string" || !isObject(ev.content)) throw errors.badJson("Invalid initial_state event");
|
|
1377
|
-
if (ev.type ===
|
|
1379
|
+
if (ev.type === M.ROOM.POWER_LEVELS || ev.type === M.ROOM.CREATE || ev.type === M.ROOM.MEMBER) continue;
|
|
1378
1380
|
await send({
|
|
1379
1381
|
type: ev.type,
|
|
1380
1382
|
state_key: ev.state_key ?? "",
|
|
@@ -1382,16 +1384,16 @@ var RoomService = class {
|
|
|
1382
1384
|
});
|
|
1383
1385
|
}
|
|
1384
1386
|
if (body.name !== void 0) await send({
|
|
1385
|
-
type:
|
|
1387
|
+
type: M.ROOM.NAME,
|
|
1386
1388
|
state_key: "",
|
|
1387
1389
|
content: { name: body.name }
|
|
1388
1390
|
});
|
|
1389
1391
|
if (body.topic !== void 0) await send({
|
|
1390
|
-
type:
|
|
1392
|
+
type: M.ROOM.TOPIC,
|
|
1391
1393
|
state_key: "",
|
|
1392
1394
|
content: {
|
|
1393
1395
|
topic: body.topic,
|
|
1394
|
-
|
|
1396
|
+
[M.TOPIC]: { [M.TEXT]: [{
|
|
1395
1397
|
mimetype: "text/plain",
|
|
1396
1398
|
body: body.topic
|
|
1397
1399
|
}] }
|
|
@@ -1451,7 +1453,7 @@ var RoomService = class {
|
|
|
1451
1453
|
if (!await this.exists(roomId)) return false;
|
|
1452
1454
|
for (const [key, ev] of await this.currentState(roomId)) {
|
|
1453
1455
|
const [type, userId] = splitStateKey(key);
|
|
1454
|
-
if (type ===
|
|
1456
|
+
if (type === M.ROOM.MEMBER && ev.content?.membership === "join" && this.hs.isLocalUser(userId)) return true;
|
|
1455
1457
|
}
|
|
1456
1458
|
return false;
|
|
1457
1459
|
}
|
|
@@ -1536,9 +1538,9 @@ var RoomService = class {
|
|
|
1536
1538
|
...await this.hs.profile(userId)
|
|
1537
1539
|
};
|
|
1538
1540
|
if (extra.reason) content.reason = extra.reason;
|
|
1539
|
-
const existing = getState(state,
|
|
1541
|
+
const existing = getState(state, M.ROOM.MEMBER, userId);
|
|
1540
1542
|
if (existing?.content?.membership === "join" && JSON.stringify(existing.content) === JSON.stringify(content)) return roomId;
|
|
1541
|
-
const joinRule = getState(state,
|
|
1543
|
+
const joinRule = getState(state, M.ROOM.JOIN_RULES)?.content?.join_rule;
|
|
1542
1544
|
const current = membershipOf(state, userId);
|
|
1543
1545
|
if ((joinRule === "restricted" || joinRule === "knock_restricted") && current !== "join" && current !== "invite") {
|
|
1544
1546
|
const via = await this.findRestrictedAuthoriser(userId, state, version);
|
|
@@ -1548,7 +1550,7 @@ var RoomService = class {
|
|
|
1548
1550
|
const servers = /* @__PURE__ */ new Set();
|
|
1549
1551
|
for (const [k, ev] of state) {
|
|
1550
1552
|
const [type, member] = splitStateKey(k);
|
|
1551
|
-
if (type ===
|
|
1553
|
+
if (type === M.ROOM.MEMBER && ev.content?.membership === "join" && !this.hs.isLocalUser(member) && userLevel(pl, member) >= pl.invite) servers.add(member.slice(member.indexOf(":") + 1));
|
|
1552
1554
|
}
|
|
1553
1555
|
if (servers.size) {
|
|
1554
1556
|
await this.hs.federation.joinRemoteRoom(userId, roomId, [...servers], content);
|
|
@@ -1557,30 +1559,30 @@ var RoomService = class {
|
|
|
1557
1559
|
}
|
|
1558
1560
|
}
|
|
1559
1561
|
await this.sendEvent(roomId, userId, {
|
|
1560
|
-
type:
|
|
1562
|
+
type: M.ROOM.MEMBER,
|
|
1561
1563
|
state_key: userId,
|
|
1562
1564
|
content
|
|
1563
1565
|
});
|
|
1564
|
-
const predecessor = getState(state,
|
|
1566
|
+
const predecessor = getState(state, M.ROOM.CREATE)?.content?.predecessor?.room_id;
|
|
1565
1567
|
if (typeof predecessor === "string") await copyRoomPushRules(this.hs, userId, predecessor, roomId);
|
|
1566
1568
|
return roomId;
|
|
1567
1569
|
}
|
|
1568
1570
|
/** Whether an allowed room exists that we can't check (or the user is joined to), so another server might authorise. */
|
|
1569
1571
|
async mayQualifyRemotely(userId, state) {
|
|
1570
|
-
const allow = getState(state,
|
|
1572
|
+
const allow = getState(state, M.ROOM.JOIN_RULES)?.content?.allow;
|
|
1571
1573
|
if (!Array.isArray(allow)) return false;
|
|
1572
1574
|
for (const rule of allow) {
|
|
1573
|
-
if (rule?.type !==
|
|
1575
|
+
if (rule?.type !== M.ROOM_MEMBERSHIP || typeof rule.room_id !== "string") continue;
|
|
1574
1576
|
if (!await this.serverIsJoined(rule.room_id)) return true;
|
|
1575
1577
|
if (await this.membership(rule.room_id, userId) === "join") return true;
|
|
1576
1578
|
}
|
|
1577
1579
|
return false;
|
|
1578
1580
|
}
|
|
1579
1581
|
async findRestrictedAuthoriser(userId, state, version) {
|
|
1580
|
-
const allow = getState(state,
|
|
1582
|
+
const allow = getState(state, M.ROOM.JOIN_RULES)?.content?.allow;
|
|
1581
1583
|
if (!Array.isArray(allow)) return null;
|
|
1582
1584
|
let permitted = false;
|
|
1583
|
-
for (const rule of allow) if (rule?.type ===
|
|
1585
|
+
for (const rule of allow) if (rule?.type === M.ROOM_MEMBERSHIP && typeof rule.room_id === "string" && await this.exists(rule.room_id)) {
|
|
1584
1586
|
if (await this.membership(rule.room_id, userId) === "join") {
|
|
1585
1587
|
permitted = true;
|
|
1586
1588
|
break;
|
|
@@ -1590,7 +1592,7 @@ var RoomService = class {
|
|
|
1590
1592
|
const pl = powerLevels(state, version);
|
|
1591
1593
|
for (const [k, ev] of state) {
|
|
1592
1594
|
const [type, key] = splitStateKey(k);
|
|
1593
|
-
if (type ===
|
|
1595
|
+
if (type === M.ROOM.MEMBER && ev.content?.membership === "join" && this.hs.isLocalUser(key) && userLevel(pl, key) >= pl.invite) return key;
|
|
1594
1596
|
}
|
|
1595
1597
|
return null;
|
|
1596
1598
|
}
|
|
@@ -1601,7 +1603,7 @@ var RoomService = class {
|
|
|
1601
1603
|
field: "user_id",
|
|
1602
1604
|
value: target
|
|
1603
1605
|
}]) && !await this.hs.appservices.queryUser(target)) throw errors.notFound("Unknown user");
|
|
1604
|
-
if (local && await inviteDecision(this.hs, target, sender) === "block") throw new MatrixError(403, "M_FORBIDDEN", "This user does not accept invites from you", {
|
|
1606
|
+
if (local && await inviteDecision(this.hs, target, sender) === "block") throw new MatrixError(403, "M_FORBIDDEN", "This user does not accept invites from you", { [UNSTABLE.ORG.MATRIX.MSC4155.ERRCODE]: "M_INVITE_BLOCKED" });
|
|
1605
1607
|
await this.requireJoined(roomId, sender);
|
|
1606
1608
|
const state = await this.currentState(roomId);
|
|
1607
1609
|
if (membershipOf(state, target) === "invite") return;
|
|
@@ -1612,7 +1614,7 @@ var RoomService = class {
|
|
|
1612
1614
|
if (opts.thirdPartyInvite) content.third_party_invite = opts.thirdPartyInvite;
|
|
1613
1615
|
if (local) {
|
|
1614
1616
|
await this.sendEvent(roomId, sender, {
|
|
1615
|
-
type:
|
|
1617
|
+
type: M.ROOM.MEMBER,
|
|
1616
1618
|
state_key: target,
|
|
1617
1619
|
content
|
|
1618
1620
|
});
|
|
@@ -1622,12 +1624,12 @@ var RoomService = class {
|
|
|
1622
1624
|
await this.withLock(roomId, async () => {
|
|
1623
1625
|
const version = await this.roomVersion(roomId);
|
|
1624
1626
|
const pdu = await this.buildLocalEvent(roomId, version, sender, {
|
|
1625
|
-
type:
|
|
1627
|
+
type: M.ROOM.MEMBER,
|
|
1626
1628
|
state_key: target,
|
|
1627
1629
|
content
|
|
1628
1630
|
});
|
|
1629
1631
|
const inviteState = [...this.strippedState(await this.currentState(roomId))];
|
|
1630
|
-
const senderMember = getState(await this.currentState(roomId),
|
|
1632
|
+
const senderMember = getState(await this.currentState(roomId), M.ROOM.MEMBER, sender);
|
|
1631
1633
|
if (senderMember) {
|
|
1632
1634
|
const { unsigned, ...full } = senderMember;
|
|
1633
1635
|
inviteState.push(this.hs.federation.toFederationPdu(full, version));
|
|
@@ -1646,11 +1648,11 @@ var RoomService = class {
|
|
|
1646
1648
|
const content = { membership };
|
|
1647
1649
|
if (reason) content.reason = reason;
|
|
1648
1650
|
if (membership === "ban" || membership === "leave" && sender !== target) {
|
|
1649
|
-
const prev = getState(await this.currentState(roomId),
|
|
1651
|
+
const prev = getState(await this.currentState(roomId), M.ROOM.MEMBER, target);
|
|
1650
1652
|
if (prev?.content?.displayname) content.displayname = prev.content.displayname;
|
|
1651
1653
|
}
|
|
1652
1654
|
await this.sendEvent(roomId, sender, {
|
|
1653
|
-
type:
|
|
1655
|
+
type: M.ROOM.MEMBER,
|
|
1654
1656
|
state_key: target,
|
|
1655
1657
|
content
|
|
1656
1658
|
});
|
|
@@ -1689,7 +1691,7 @@ var RoomService = class {
|
|
|
1689
1691
|
leave = finalisePdu({
|
|
1690
1692
|
room_id: roomId,
|
|
1691
1693
|
sender: userId,
|
|
1692
|
-
type:
|
|
1694
|
+
type: M.ROOM.MEMBER,
|
|
1693
1695
|
state_key: userId,
|
|
1694
1696
|
content,
|
|
1695
1697
|
origin_server_ts: Date.now(),
|
|
@@ -1704,7 +1706,7 @@ var RoomService = class {
|
|
|
1704
1706
|
async propagateProfile(userId) {
|
|
1705
1707
|
const profile = await this.hs.profile(userId);
|
|
1706
1708
|
for (const roomId of await this.roomsForUser(userId)) {
|
|
1707
|
-
const prev = getState(await this.currentState(roomId),
|
|
1709
|
+
const prev = getState(await this.currentState(roomId), M.ROOM.MEMBER, userId);
|
|
1708
1710
|
if (!prev) continue;
|
|
1709
1711
|
const content = {
|
|
1710
1712
|
...prev.content,
|
|
@@ -1715,7 +1717,7 @@ var RoomService = class {
|
|
|
1715
1717
|
if (profile.avatar_url === void 0) delete content.avatar_url;
|
|
1716
1718
|
try {
|
|
1717
1719
|
await this.sendEvent(roomId, userId, {
|
|
1718
|
-
type:
|
|
1720
|
+
type: M.ROOM.MEMBER,
|
|
1719
1721
|
state_key: userId,
|
|
1720
1722
|
content
|
|
1721
1723
|
});
|
package/dist/core/stateres.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { M } from "./m.js";
|
|
1
2
|
import { eventIdOf, roomVersionFeatures, stateKey } from "./events.js";
|
|
2
3
|
import { authEventKeys, checkAuth, powerLevels, userLevel } from "./authrules.js";
|
|
3
4
|
//#region src/core/stateres.ts
|
|
4
|
-
const isPowerEvent = (ev) => ev.type ===
|
|
5
|
+
const isPowerEvent = (ev) => ev.type === M.ROOM.POWER_LEVELS && ev.state_key === "" || ev.type === M.ROOM.JOIN_RULES && ev.state_key === "" || ev.type === M.ROOM.CREATE && ev.state_key === "" || ev.type === M.ROOM.MEMBER && ["leave", "ban"].includes(ev.content?.membership) && ev.sender !== ev.state_key;
|
|
5
6
|
const authIds = (ev) => (ev.auth_events ?? []).map(eventIdOf);
|
|
6
7
|
var EventCache = class {
|
|
7
8
|
events = /* @__PURE__ */ new Map();
|
|
@@ -66,7 +67,7 @@ async function resolveState(stateSets, roomVersion, source, createEvent) {
|
|
|
66
67
|
for (const id of await cache.authChain(powerEvents)) if (full.has(id)) control.add(id);
|
|
67
68
|
let resolved = await iterativeAuthChecks(cache, await reverseTopologicalPowerSort(cache, control, roomVersion, createEvent), v21 ? {} : { ...unconflicted }, roomVersion, createEvent);
|
|
68
69
|
const remaining = [...full].filter((id) => !control.has(id));
|
|
69
|
-
const mainline = await mainlineOf(cache, resolved[stateKey(
|
|
70
|
+
const mainline = await mainlineOf(cache, resolved[stateKey(M.ROOM.POWER_LEVELS, "")]);
|
|
70
71
|
const positions = /* @__PURE__ */ new Map();
|
|
71
72
|
for (const id of remaining) positions.set(id, await mainlinePosition(cache, id, mainline));
|
|
72
73
|
remaining.sort((a, b) => {
|
|
@@ -107,7 +108,7 @@ async function senderPowerLevel(cache, ev, roomVersion, createEvent) {
|
|
|
107
108
|
const a = cache.get(id);
|
|
108
109
|
if (a && a.state_key !== void 0) state.set(stateKey(a.type, a.state_key), a);
|
|
109
110
|
}
|
|
110
|
-
if (createEvent) state.set(stateKey(
|
|
111
|
+
if (createEvent) state.set(stateKey(M.ROOM.CREATE, ""), createEvent);
|
|
111
112
|
const level = userLevel(powerLevels(state, roomVersion), ev.sender);
|
|
112
113
|
return Number.isFinite(level) ? level : Number.MAX_SAFE_INTEGER;
|
|
113
114
|
}
|
|
@@ -160,7 +161,7 @@ async function iterativeAuthChecks(cache, ids, base, roomVersion, createEvent) {
|
|
|
160
161
|
const sev = sid ? cache.get(sid) : void 0;
|
|
161
162
|
if (sev) authState.set(k, sev);
|
|
162
163
|
}
|
|
163
|
-
if (createEvent) authState.set(stateKey(
|
|
164
|
+
if (createEvent) authState.set(stateKey(M.ROOM.CREATE, ""), createEvent);
|
|
164
165
|
try {
|
|
165
166
|
checkAuth(ev, authState, roomVersion);
|
|
166
167
|
state[stateKey(ev.type, ev.state_key)] = id;
|
|
@@ -179,7 +180,7 @@ async function mainlineOf(cache, plId) {
|
|
|
179
180
|
await cache.load(authIds(ev));
|
|
180
181
|
current = authIds(ev).find((a) => {
|
|
181
182
|
const e = cache.get(a);
|
|
182
|
-
return e?.type ===
|
|
183
|
+
return e?.type === M.ROOM.POWER_LEVELS && e.state_key === "";
|
|
183
184
|
});
|
|
184
185
|
}
|
|
185
186
|
const positions = /* @__PURE__ */ new Map();
|
|
@@ -199,7 +200,7 @@ async function mainlinePosition(cache, id, mainline) {
|
|
|
199
200
|
await cache.load(authIds(ev));
|
|
200
201
|
current = authIds(ev).find((a) => {
|
|
201
202
|
const e = cache.get(a);
|
|
202
|
-
return e?.type ===
|
|
203
|
+
return e?.type === M.ROOM.POWER_LEVELS && e.state_key === "";
|
|
203
204
|
});
|
|
204
205
|
}
|
|
205
206
|
return 0;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { canonicalJson } from "../core/json.js";
|
|
2
2
|
import { unpaddedBase64 } from "../core/crypto.js";
|
|
3
|
+
import { M } from "../core/m.js";
|
|
3
4
|
import { MatrixError } from "../core/errors.js";
|
|
4
5
|
import { sign } from "node:crypto";
|
|
5
6
|
import { resolveSrv } from "node:dns/promises";
|
|
@@ -149,7 +150,7 @@ var FederationClient = class {
|
|
|
149
150
|
}, "GET", "/.well-known/matrix/server", void 0, {}, 5e3);
|
|
150
151
|
if (res.status !== 200) return null;
|
|
151
152
|
const body = JSON.parse(res.body.toString("utf8"));
|
|
152
|
-
return typeof body[
|
|
153
|
+
return typeof body[M.SERVER] === "string" && body[M.SERVER] ? body[M.SERVER] : null;
|
|
153
154
|
} catch {
|
|
154
155
|
return null;
|
|
155
156
|
}
|
|
@@ -7,7 +7,7 @@ import { TransactionSender } from "./sender.js";
|
|
|
7
7
|
import { Homeserver } from "../homeserver.js";
|
|
8
8
|
//#region src/federation/index.d.ts
|
|
9
9
|
export interface FederationOptions {
|
|
10
|
-
/** Enable federation. Default:
|
|
10
|
+
/** Enable federation (talking to other homeservers). Default: false, so that a server only joins the network on purpose. */
|
|
11
11
|
enabled?: boolean;
|
|
12
12
|
/** Join remote rooms with partial state first (MSC3902 "faster joins"). Default true. */
|
|
13
13
|
fasterJoins?: boolean;
|