Lines 2-42javascript
2/** Custom event-content key tagging the logical Parley topic (shared-room isolation + provenance). */
3const TOPIC_KEY = 'app.parley.topic';
4const isMessageEvent = (e) => e.type === 'm.room.message';
6 * Matrix (Synapse) backend (DESIGN §6/§9) — first external-network backend, over the raw
7 * Client-Server HTTP API (no SDK; unencrypted rooms). By default a topic maps to its own room via
8 * the canonical alias `#parley_<topic>:<server_name>`. The Matrix `event_id` is globally unique and
9 * serves as BOTH `backendMsgId` (dedup key) AND `cursor` (order key). "Strictly after a cursor" is
10 * resolved server-side: `/context/<event_id>` → a forward pagination token → `/messages?dir=f`. The
11 * live path is a filtered `/sync` long-poll loop (timeline limit 0 skips history). Core never
12 * compares cursor values — the homeserver's stream ordering is the single source of order.
14 * `shared_room` mode (see {@link MatrixBackendConfig.shared_room}) folds all topics into one room,
15 * isolating them by an `app.parley.topic` content tag — the only practical way to run the suite
16 * under Synapse's strict per-user room-creation rate limit without an appservice.
18export class MatrixPlugin {
19 baseUrl = 'http://127.0.0.1:8008';
20 serverName = 'parley.local';
22 password = 'parleypass';
23 syncTimeoutMs = 25_000;
24 /** Set → shared-room mode: alias localpart every topic resolves to; else per-topic rooms. */
30 /** room cache key → room_id, deduped so concurrent first-posts share one create/resolve. */
32 /** In-flight sync long-polls, aborted on disconnect so teardown is immediate. */
33 controllers = new Set();
34 async connect(config) {
36 this.baseUrl = (cfg.homeserver_url ?? 'http://127.0.0.1:8008').replace(/\/+$/, '');
37 this.serverName = cfg.server_name ?? 'parley.local';
38 this.user = cfg.user ?? 'parley';
39 this.password = cfg.password ?? 'parleypass';
40 this.syncTimeoutMs = cfg.sync_timeout_ms ?? 25_000;
41 this.sharedLocalpart = cfg.shared_room;