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.
- package/README.md +89 -11
- package/dist/adapters/cluster.d.ts +1 -26
- package/dist/adapters/cluster.js +50 -103
- package/dist/api/account.js +407 -354
- package/dist/api/admin.js +82 -61
- 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 +71 -55
- package/dist/api/misc.js +46 -37
- package/dist/api/preview.js +37 -73
- 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 +325 -135
- 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/cluster.d.ts +12 -5
- package/dist/core/cluster.js +46 -29
- package/dist/core/delayed.js +2 -1
- package/dist/core/events.js +11 -10
- package/dist/core/html.js +13 -0
- package/dist/core/identity.js +1 -1
- package/dist/core/invitefilter.js +2 -1
- package/dist/core/json.js +10 -1
- package/dist/core/keys.d.ts +10 -1
- package/dist/core/keys.js +62 -42
- package/dist/core/m.d.ts +209 -0
- package/dist/core/m.js +182 -0
- package/dist/core/outbound.d.ts +16 -0
- package/dist/core/outbound.js +172 -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 +41 -28
- package/dist/core/pushrules.js +36 -35
- package/dist/core/ratelimit.d.ts +1 -1
- package/dist/core/rooms.d.ts +10 -1
- package/dist/core/rooms.js +101 -74
- package/dist/core/semaphore.js +23 -0
- package/dist/core/sso.d.ts +15 -1
- package/dist/core/sso.js +38 -2
- package/dist/core/stateres.js +7 -6
- package/dist/core/threepid.js +1 -9
- package/dist/federation/client.js +9 -1
- package/dist/federation/index.d.ts +2 -2
- package/dist/federation/index.js +43 -35
- package/dist/homeserver.d.ts +23 -4
- package/dist/homeserver.js +12 -3
- 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 +4 -3
- package/dist/index.js +3 -2
- package/dist/storage/batch.js +11 -0
- package/dist/storage/memory.js +1 -0
- package/dist/storage/schema.js +1 -1
- package/dist/storage/sql.js +20 -1
- package/dist/storage/types.d.ts +16 -0
- 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 +8 -6
- package/dist/core/pushrules.d.ts +0 -1
package/dist/core/push.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isObject } from "./json.js";
|
|
2
|
+
import { M, UNSTABLE } from "./m.js";
|
|
2
3
|
import { splitStateKey, toClientEvent } from "./events.js";
|
|
3
4
|
import { getState, powerLevels, userLevel } from "./authrules.js";
|
|
4
5
|
import { globMatch, globSearchWords } from "./glob.js";
|
|
@@ -47,7 +48,7 @@ function conditionMatches(cond, ctx) {
|
|
|
47
48
|
}
|
|
48
49
|
return false;
|
|
49
50
|
}
|
|
50
|
-
case
|
|
51
|
+
case UNSTABLE.IO.ELEMENT.MSC4306.THREAD_SUBSCRIPTION: return ctx.threadSubscribed !== void 0 && ctx.threadSubscribed !== null && ctx.threadSubscribed === cond.subscribed;
|
|
51
52
|
case "sender_notification_permission": return typeof cond.key === "string" && ctx.senderLevel >= (ctx.notificationLevels[cond.key] ?? 50);
|
|
52
53
|
default: return false;
|
|
53
54
|
}
|
|
@@ -80,8 +81,8 @@ function tweaksOf(actions) {
|
|
|
80
81
|
}
|
|
81
82
|
/** Thread an event belongs to for notification purposes ("main" for the main timeline). */
|
|
82
83
|
function threadIdOf(event) {
|
|
83
|
-
const rel = event.content?.[
|
|
84
|
-
return isObject(rel) && rel.rel_type ===
|
|
84
|
+
const rel = event.content?.[M.RELATES_TO];
|
|
85
|
+
return isObject(rel) && rel.rel_type === M.THREAD && typeof rel.event_id === "string" ? rel.event_id : "main";
|
|
85
86
|
}
|
|
86
87
|
/**
|
|
87
88
|
* Push rule evaluation and push gateway delivery. Every new event is evaluated for
|
|
@@ -97,20 +98,21 @@ var PushService = class {
|
|
|
97
98
|
const members = [];
|
|
98
99
|
for (const [key, member] of state) {
|
|
99
100
|
const [type, userId] = splitStateKey(key);
|
|
100
|
-
if (type !==
|
|
101
|
+
if (type !== M.ROOM.MEMBER) continue;
|
|
101
102
|
const m = member.content?.membership;
|
|
102
|
-
if (m === "join" || m === "invite" && event.type ===
|
|
103
|
+
if (m === "join" || m === "invite" && event.type === M.ROOM.MEMBER && event.state_key === userId) members.push(userId);
|
|
103
104
|
}
|
|
104
|
-
const joined = members.filter((u) => getState(state,
|
|
105
|
+
const joined = members.filter((u) => getState(state, M.ROOM.MEMBER, u)?.content?.membership === "join");
|
|
105
106
|
const pl = powerLevels(state, version);
|
|
106
107
|
const senderLevel = userLevel(pl, event.sender);
|
|
107
108
|
const thread = threadIdOf(event);
|
|
109
|
+
const notified = [];
|
|
108
110
|
for (const userId of members) {
|
|
109
111
|
if (userId === event.sender || !this.hs.isLocalUser(userId)) continue;
|
|
110
112
|
const actions = evaluateRules(await loadRules(this.hs, userId), {
|
|
111
113
|
event,
|
|
112
114
|
userId,
|
|
113
|
-
displayName: getState(state,
|
|
115
|
+
displayName: getState(state, M.ROOM.MEMBER, userId)?.content?.displayname ?? null,
|
|
114
116
|
memberCount: joined.length,
|
|
115
117
|
senderLevel: Number.isFinite(senderLevel) ? senderLevel : Number.MAX_SAFE_INTEGER,
|
|
116
118
|
notificationLevels: pl.notifications,
|
|
@@ -118,17 +120,33 @@ var PushService = class {
|
|
|
118
120
|
});
|
|
119
121
|
if (!actions || !actions.includes("notify")) continue;
|
|
120
122
|
const tweaks = tweaksOf(actions);
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
123
|
+
notified.push({
|
|
124
|
+
userId,
|
|
125
|
+
tweaks,
|
|
126
|
+
row: {
|
|
127
|
+
user_id: userId,
|
|
128
|
+
room_id: event.room_id,
|
|
129
|
+
event_id: event.event_id,
|
|
130
|
+
stream: ev.stream,
|
|
131
|
+
highlight: tweaks.highlight === true,
|
|
132
|
+
thread_id: thread,
|
|
133
|
+
actions,
|
|
134
|
+
ts: Date.now()
|
|
135
|
+
}
|
|
130
136
|
});
|
|
131
|
-
|
|
137
|
+
}
|
|
138
|
+
if (notified.length === 0) return;
|
|
139
|
+
await this.hs.transaction(async (db) => {
|
|
140
|
+
for (const n of notified) await db.upsert("notifications", n.row);
|
|
141
|
+
});
|
|
142
|
+
const pushers = await this.hs.db.findMany("pushers", { where: [{
|
|
143
|
+
field: "user_id",
|
|
144
|
+
operator: "in",
|
|
145
|
+
value: notified.map((n) => n.userId)
|
|
146
|
+
}] });
|
|
147
|
+
for (const n of notified) {
|
|
148
|
+
const own = pushers.filter((p) => p.user_id === n.userId);
|
|
149
|
+
if (own.length) this.deliver(n.userId, ev, state, n.tweaks, own).catch((err) => this.hs.logger.warn("Push delivery failed", err));
|
|
132
150
|
}
|
|
133
151
|
}
|
|
134
152
|
/** Unread notification counts since the user's read receipts. */
|
|
@@ -144,7 +162,7 @@ var PushService = class {
|
|
|
144
162
|
let unthreaded = 0;
|
|
145
163
|
const threadRead = /* @__PURE__ */ new Map();
|
|
146
164
|
for (const r of receipts) {
|
|
147
|
-
if (r.receipt_type !==
|
|
165
|
+
if (r.receipt_type !== M.READ._ && r.receipt_type !== M.READ.PRIVATE) continue;
|
|
148
166
|
const target = await this.hs.rooms.getEvent(r.event_id);
|
|
149
167
|
if (!target) continue;
|
|
150
168
|
if (r.thread_id === "") unthreaded = Math.max(unthreaded, target.stream);
|
|
@@ -195,12 +213,7 @@ var PushService = class {
|
|
|
195
213
|
threads
|
|
196
214
|
};
|
|
197
215
|
}
|
|
198
|
-
async deliver(userId, ev, state, tweaks) {
|
|
199
|
-
const pushers = await this.hs.db.findMany("pushers", { where: [{
|
|
200
|
-
field: "user_id",
|
|
201
|
-
value: userId
|
|
202
|
-
}] });
|
|
203
|
-
if (pushers.length === 0) return;
|
|
216
|
+
async deliver(userId, ev, state, tweaks, pushers) {
|
|
204
217
|
const event = ev.pdu;
|
|
205
218
|
const unread = (await this.unreadCounts(userId, event.room_id)).room.notification_count;
|
|
206
219
|
for (const row of pushers) {
|
|
@@ -224,16 +237,16 @@ var PushService = class {
|
|
|
224
237
|
notification.type = event.type;
|
|
225
238
|
notification.sender = event.sender;
|
|
226
239
|
notification.content = event.content;
|
|
227
|
-
const name = getState(state,
|
|
240
|
+
const name = getState(state, M.ROOM.NAME)?.content?.name;
|
|
228
241
|
if (typeof name === "string") notification.room_name = name;
|
|
229
|
-
const senderName = getState(state,
|
|
242
|
+
const senderName = getState(state, M.ROOM.MEMBER, event.sender)?.content?.displayname;
|
|
230
243
|
if (typeof senderName === "string") notification.sender_display_name = senderName;
|
|
231
|
-
if (event.type ===
|
|
244
|
+
if (event.type === M.ROOM.MEMBER) {
|
|
232
245
|
notification.membership = event.content?.membership;
|
|
233
246
|
notification.user_is_target = event.state_key === userId;
|
|
234
247
|
}
|
|
235
248
|
}
|
|
236
|
-
const res = await this.hs.
|
|
249
|
+
const res = await this.hs.publicFetch(url, {
|
|
237
250
|
method: "POST",
|
|
238
251
|
headers: { "content-type": "application/json" },
|
|
239
252
|
body: JSON.stringify({ notification }),
|
package/dist/core/pushrules.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isObject } from "./json.js";
|
|
2
|
+
import { M, UNSTABLE } from "./m.js";
|
|
2
3
|
//#region src/core/pushrules.ts
|
|
3
4
|
const KINDS = [
|
|
4
5
|
"override",
|
|
@@ -36,15 +37,15 @@ function defaultRules(userId) {
|
|
|
36
37
|
});
|
|
37
38
|
return {
|
|
38
39
|
override: [
|
|
39
|
-
r(
|
|
40
|
-
r(
|
|
41
|
-
r(
|
|
42
|
-
em("type",
|
|
40
|
+
r(M.RULE.MASTER, [], [], { enabled: false }),
|
|
41
|
+
r(M.RULE.SUPPRESS_NOTICES, [em("content.msgtype", M.NOTICE)], []),
|
|
42
|
+
r(M.RULE.INVITE_FOR_ME, [
|
|
43
|
+
em("type", M.ROOM.MEMBER),
|
|
43
44
|
em("content.membership", "invite"),
|
|
44
45
|
em("state_key", userId)
|
|
45
46
|
], [...notify, sound("default")]),
|
|
46
|
-
r(
|
|
47
|
-
r(
|
|
47
|
+
r(M.RULE.MEMBER_EVENT, [em("type", M.ROOM.MEMBER)], []),
|
|
48
|
+
r(M.RULE.IS_USER_MENTION, [{
|
|
48
49
|
kind: "event_property_contains",
|
|
49
50
|
key: "content.m\\.mentions.user_ids",
|
|
50
51
|
value: userId
|
|
@@ -53,61 +54,61 @@ function defaultRules(userId) {
|
|
|
53
54
|
sound("default"),
|
|
54
55
|
highlight
|
|
55
56
|
]),
|
|
56
|
-
r(
|
|
57
|
+
r(M.RULE.CONTAINS_DISPLAY_NAME, [{ kind: "contains_display_name" }], [
|
|
57
58
|
...notify,
|
|
58
59
|
sound("default"),
|
|
59
60
|
highlight
|
|
60
61
|
]),
|
|
61
|
-
r(
|
|
62
|
+
r(M.RULE.IS_ROOM_MENTION, [ep("content.m\\.mentions.room", true), {
|
|
62
63
|
kind: "sender_notification_permission",
|
|
63
64
|
key: "room"
|
|
64
65
|
}], [...notify, highlight]),
|
|
65
|
-
r(
|
|
66
|
+
r(M.RULE.ROOMNOTIF, [em("content.body", "@room"), {
|
|
66
67
|
kind: "sender_notification_permission",
|
|
67
68
|
key: "room"
|
|
68
69
|
}], [...notify, highlight]),
|
|
69
|
-
r(
|
|
70
|
-
r(
|
|
71
|
-
r(
|
|
72
|
-
r(
|
|
73
|
-
r(
|
|
70
|
+
r(M.RULE.TOMBSTONE, [em("type", M.ROOM.TOMBSTONE), em("state_key", "")], [...notify, highlight]),
|
|
71
|
+
r(M.RULE.REACTION, [em("type", M.REACTION)], []),
|
|
72
|
+
r(M.RULE.ROOM.SERVER_ACL, [em("type", M.ROOM.SERVER_ACL), em("state_key", "")], []),
|
|
73
|
+
r(M.RULE.SUPPRESS_EDITS, [ep("content.m\\.relates_to.rel_type", M.REPLACE)], []),
|
|
74
|
+
r(UNSTABLE.ORG.MATRIX.MSC3930.RULE.POLL_RESPONSE, [em("type", UNSTABLE.ORG.MATRIX.MSC3381.POLL.RESPONSE)], [])
|
|
74
75
|
],
|
|
75
|
-
content: [r(
|
|
76
|
+
content: [r(M.RULE.CONTAINS_USER_NAME, void 0, [
|
|
76
77
|
...notify,
|
|
77
78
|
sound("default"),
|
|
78
79
|
highlight
|
|
79
80
|
], { pattern: localpart })],
|
|
80
|
-
postcontent: [r(
|
|
81
|
-
kind:
|
|
81
|
+
postcontent: [r(UNSTABLE.IO.ELEMENT.MSC4306.RULE.UNSUBSCRIBED_THREAD, [{
|
|
82
|
+
kind: UNSTABLE.IO.ELEMENT.MSC4306.THREAD_SUBSCRIPTION,
|
|
82
83
|
subscribed: false
|
|
83
|
-
}], []), r(
|
|
84
|
-
kind:
|
|
84
|
+
}], []), r(UNSTABLE.IO.ELEMENT.MSC4306.RULE.SUBSCRIBED_THREAD, [{
|
|
85
|
+
kind: UNSTABLE.IO.ELEMENT.MSC4306.THREAD_SUBSCRIPTION,
|
|
85
86
|
subscribed: true
|
|
86
87
|
}], [...notify, sound("default")])],
|
|
87
88
|
room: [],
|
|
88
89
|
sender: [],
|
|
89
90
|
underride: [
|
|
90
|
-
r(
|
|
91
|
-
r(
|
|
91
|
+
r(M.RULE.CALL, [em("type", M.CALL.INVITE)], [...notify, sound("ring")]),
|
|
92
|
+
r(M.RULE.ENCRYPTED_ROOM_ONE_TO_ONE, [{
|
|
92
93
|
kind: "room_member_count",
|
|
93
94
|
is: "2"
|
|
94
|
-
}, em("type",
|
|
95
|
-
r(
|
|
95
|
+
}, em("type", M.ROOM.ENCRYPTED)], [...notify, sound("default")]),
|
|
96
|
+
r(M.RULE.ROOM_ONE_TO_ONE, [{
|
|
96
97
|
kind: "room_member_count",
|
|
97
98
|
is: "2"
|
|
98
|
-
}, em("type",
|
|
99
|
-
r(
|
|
100
|
-
r(
|
|
99
|
+
}, em("type", M.ROOM.MESSAGE)], [...notify, sound("default")]),
|
|
100
|
+
r(M.RULE.MESSAGE, [em("type", M.ROOM.MESSAGE)], notify),
|
|
101
|
+
r(UNSTABLE.ORG.MATRIX.MSC3930.RULE.POLL_START_ONE_TO_ONE, [{
|
|
101
102
|
kind: "room_member_count",
|
|
102
103
|
is: "2"
|
|
103
|
-
}, em("type",
|
|
104
|
-
r(
|
|
104
|
+
}, em("type", UNSTABLE.ORG.MATRIX.MSC3381.POLL.START)], [...notify, sound("default")]),
|
|
105
|
+
r(UNSTABLE.ORG.MATRIX.MSC3930.RULE.POLL_END_ONE_TO_ONE, [{
|
|
105
106
|
kind: "room_member_count",
|
|
106
107
|
is: "2"
|
|
107
|
-
}, em("type",
|
|
108
|
-
r(
|
|
109
|
-
r(
|
|
110
|
-
r(
|
|
108
|
+
}, em("type", UNSTABLE.ORG.MATRIX.MSC3381.POLL.END)], [...notify, sound("default")]),
|
|
109
|
+
r(UNSTABLE.ORG.MATRIX.MSC3930.RULE.POLL_START, [em("type", UNSTABLE.ORG.MATRIX.MSC3381.POLL.START)], notify),
|
|
110
|
+
r(UNSTABLE.ORG.MATRIX.MSC3930.RULE.POLL_END, [em("type", UNSTABLE.ORG.MATRIX.MSC3381.POLL.END)], notify),
|
|
111
|
+
r(M.RULE.ENCRYPTED, [em("type", M.ROOM.ENCRYPTED)], notify)
|
|
111
112
|
]
|
|
112
113
|
};
|
|
113
114
|
}
|
|
@@ -151,7 +152,7 @@ async function readRules(hs, userId) {
|
|
|
151
152
|
},
|
|
152
153
|
{
|
|
153
154
|
field: "type",
|
|
154
|
-
value:
|
|
155
|
+
value: M.PUSH_RULES
|
|
155
156
|
}
|
|
156
157
|
]);
|
|
157
158
|
if (!isObject(row?.content?.global)) return defaultRules(userId);
|
|
@@ -169,7 +170,7 @@ async function saveRules(hs, userId, rules) {
|
|
|
169
170
|
await hs.streams.write((stream) => hs.db.upsert("account_data", {
|
|
170
171
|
user_id: userId,
|
|
171
172
|
room_id: "",
|
|
172
|
-
type:
|
|
173
|
+
type: M.PUSH_RULES,
|
|
173
174
|
content: { global: rules },
|
|
174
175
|
stream
|
|
175
176
|
}));
|
|
@@ -191,7 +192,7 @@ async function copyRules(hs, userId, oldRoomId, newRoomId) {
|
|
|
191
192
|
},
|
|
192
193
|
{
|
|
193
194
|
field: "type",
|
|
194
|
-
value:
|
|
195
|
+
value: M.PUSH_RULES
|
|
195
196
|
}
|
|
196
197
|
]);
|
|
197
198
|
if (!isObject(row?.content?.global)) return;
|
package/dist/core/ratelimit.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/core/ratelimit.d.ts
|
|
2
|
-
/** A rate limiter keyed by an arbitrary string. Implement this to share limits across instances
|
|
2
|
+
/** A rate limiter keyed by an arbitrary string. Implement this to share limits across instances. */
|
|
3
3
|
export interface RateLimiter {
|
|
4
4
|
/** Consume one unit for `key` under `rule`; resolve with the delay to wait (0 when allowed). */
|
|
5
5
|
consume(key: string, rule: RateLimitRule): Promise<number>;
|
package/dist/core/rooms.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ClientEvent, PDU } from "./events.js";
|
|
2
2
|
import { StateMap } from "./authrules.js";
|
|
3
|
-
import { StateIds } from "./stateres.js";
|
|
4
3
|
import { Row } from "../storage/types.js";
|
|
4
|
+
import { StateIds } from "./stateres.js";
|
|
5
5
|
import { Homeserver } from "../homeserver.js";
|
|
6
6
|
//#region src/core/rooms.d.ts
|
|
7
7
|
export interface StoredEvent {
|
|
@@ -140,6 +140,15 @@ export declare class RoomService {
|
|
|
140
140
|
/** Users that should be woken up for events in this room. */
|
|
141
141
|
private interestedUsers;
|
|
142
142
|
roomsForUser(userId: string, membership?: string): Promise<string[]>;
|
|
143
|
+
/**
|
|
144
|
+
* Membership changes after `since` in the rooms where `userId` has a membership (in any state),
|
|
145
|
+
* given the user's own membership rows if the caller has them.
|
|
146
|
+
*
|
|
147
|
+
* https://github.com/k18k/matrix-nodejs-homeserver/issues/31: /sync and device-list tracking
|
|
148
|
+
* read every membership change on the server since their token and kept those in the user's
|
|
149
|
+
* rooms; while many users joined, each sync parsed thousands of rows. This reads those rooms only.
|
|
150
|
+
*/
|
|
151
|
+
membershipChangesSince(userId: string, since: number, mine?: Row[]): Promise<Row[]>;
|
|
143
152
|
/** All users sharing at least one joined room with `userId` (including themselves). */
|
|
144
153
|
usersSharingRooms(userId: string): Promise<Set<string>>;
|
|
145
154
|
private prepareRedaction;
|