Skip to content

Conversation

@debdutdeb
Copy link
Member

@debdutdeb debdutdeb commented Sep 26, 2025

Summary by CodeRabbit

  • Performance
    • Reduced latency in event handling by using embedded room version data, eliminating extra lookups.
  • Reliability
    • Stricter validation for room creation and power level events, improving consistency and preventing malformed events from propagating.
  • Refactor
    • Unified event wrapper APIs across room versions and standardized access to auth/previous event IDs for more consistent behavior.
  • Tests
    • Updated tests to reflect constructor changes and stronger typing for IDs and event kinds, ensuring coverage aligns with the updated APIs.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 26, 2025

Walkthrough

The changes thread explicit room version from events into event wrappers and factory constructors, replace per-event async fetches with ID-based store lookups for auth/prev events, tighten several type signatures (notably power-level/create events), and update state resolution and authorization code to use the new helpers and generics.

Changes

Cohort / File(s) Summary
Federation state service
packages/federation-sdk/src/services/state.service.ts
Use event.version directly instead of async room-version lookups across add/persist paths; keep guards for missing versions.
Authorization rules
packages/room/src/authorizartion-rules/rules.ts
Replace per-event prev retrieval with store.getEvents(membershipEvent.getPreviousEventIds()); tighten validatePowerLevelEvent create-event param to PersistentEventBase<RoomVersion, 'm.room.create'>; import RoomVersion.
Event wrapper base
packages/room/src/manager/event-wrapper.ts
Make wrapper generic as Version extends RoomVersion; constructor now (event, version) and stores version; remove abstract fetchers; add getAuthEventIds() and getPreviousEventIds(); update type predicates and method params to use Version.
Event versions (v3/v6/v8/v9/v11)
packages/room/src/manager/v3.ts, .../v6.ts, .../v8.ts, .../v9.ts, .../v11.ts
Parameterize classes with <Type extends PduType = PduType>; update inheritance to forward generics; remove version-specific abstract fetchers; import PduType.
Event factory
packages/room/src/manager/factory.ts
createFromRawEvent now takes roomVersion: string; instantiate versioned events with (event, roomVersion).
Power-level wrapper
packages/room/src/manager/power-level-event-wrapper.ts
Narrow getPowerLevelForUser optional createEvent to PersistentEventBase<RoomVersion, 'm.room.create'>; behavior unchanged.
Room state util
packages/room/src/manager/room-state.ts
Cast room_version from create event to RoomVersion on return.
State resolution
packages/room/src/state_resolution/definitions/definitions.ts
Switch from per-event auth fetching to store.getEvents(event.getAuthEventIds()); allow getStateByMapKey to return undefined; parameterize power-level event as PersistentEventBase<RoomVersion, 'm.room.power_levels'>.
Tests
packages/room/src/manager/v9.spec.ts
Update constructor calls to pass room version; add EventID casts for auth_events, prev_events, and event_id.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor HS as Homeserver
  participant SS as StateService
  participant PF as PersistentEventFactory
  participant EV as PersistentEvent*
  participant ES as EventStore

  HS->>SS: persistTimelineEvent(rawEvent)
  SS->>SS: derive roomVersion = rawEvent.version
  SS->>PF: createFromRawEvent(rawEvent, roomVersion)
  PF-->>HS: EV (constructed with version)

  SS->>EV: getAuthEventIds()
  SS->>ES: getEvents(authEventIds)
  ES-->>SS: authEvents

  SS->>EV: getPreviousEventIds()
  SS->>ES: getEvents(prevEventIds)
  ES-->>SS: prevEvents

  SS->>SS: validate & persist with provided version
  SS-->>HS: result
Loading
sequenceDiagram
  autonumber
  participant SR as StateResolution
  participant ES as EventStore
  participant EV as PersistentEvent*

  SR->>EV: getAuthEventIds()
  SR->>ES: getEvents(authEventIds)
  ES-->>SR: authEvents
  SR->>SR: mainlineOrdering(..., powerLevelEvent: PersistentEventBase<RoomVersion,'m.room.power_levels'>)
  SR-->>SR: compute ordering
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • ggazzo
  • rodrigok

Poem

I nibble bytes and hop through time,
Threading versions, neat and prime.
Auth IDs found, prev paths align—
Store now serves the event vine.
With tidy types, I cheer and spin,
Thump-thump! The rabbits’ merge is in. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title succinctly conveys the primary refactor of propagating room version through the PersistentEventBase class, directly reflecting the updates to constructors, type parameters, and event handling across the codebase. It is specific, concise, and clearly related to the main change without extraneous detail.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch propagate-room-version

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@debdutdeb debdutdeb marked this pull request as ready for review September 26, 2025 11:04
@codecov-commenter
Copy link

Codecov Report

❌ Patch coverage is 82.75862% with 10 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.74%. Comparing base (94cf2b2) to head (d14abed).

Files with missing lines Patch % Lines
packages/room/src/manager/event-wrapper.ts 58.33% 10 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #230      +/-   ##
==========================================
+ Coverage   81.67%   81.74%   +0.07%     
==========================================
  Files          63       63              
  Lines        4682     4695      +13     
==========================================
+ Hits         3824     3838      +14     
+ Misses        858      857       -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/room/src/state_resolution/definitions/definitions.ts (2)

120-142: Auth-chain memoization is incomplete; cache result for non-leaf events.

You only cache when there are zero auth events. This defeats the intended memoization for typical events and can cause redundant store calls.

Apply this diff to cache the computed chain for all events:

-    const authEvents = await store.getEvents(event.getAuthEventIds());
+    const authEvents = await store.getEvents(event.getAuthEventIds());
     if (authEvents.length === 0) {
       eventIdToAuthChainMap.set(eventId, existingAuthChainPart);
       return existingAuthChainPart;
     }
 
     const authEventIdsSet = new Set(authEvents.map((e) => e.eventId));
 
     let newAuthChainPart = existingAuthChainPart.union(authEventIdsSet);
 
     for (const authEvent of authEvents) {
       const nextAuthChainPart = await _getAuthChain(
         authEvent,
         newAuthChainPart,
       );
       if (!nextAuthChainPart) {
         continue;
       }
       newAuthChainPart = newAuthChainPart.union(nextAuthChainPart);
     }
 
-    return newAuthChainPart;
+    eventIdToAuthChainMap.set(eventId, newAuthChainPart);
+    return newAuthChainPart;

350-371: Missing await in recursive graph build causes race conditions.

buildIndegreeGraph is async but recursive calls aren’t awaited. Graph and power-level maps may be incomplete when used.

Apply this diff:

-      if (conflictedSet.has(authEvent.eventId)) {
-        graph.get(event.eventId)?.add(authEvent.eventId); // add this as an edge
-
-        buildIndegreeGraph(graph, authEvent);
-      }
+      if (conflictedSet.has(authEvent.eventId)) {
+        graph.get(event.eventId)?.add(authEvent.eventId); // add this as an edge
+        await buildIndegreeGraph(graph, authEvent);
+      }
🧹 Nitpick comments (8)
packages/room/src/manager/room-state.ts (1)

146-155: Prefer using the wrapper’s version over content cast

Use the strongly-typed event wrapper field instead of reading and casting from content.

Apply this diff:

-		return createEvent.getContent().room_version as RoomVersion;
+		return createEvent.version;
packages/federation-sdk/src/services/state.service.ts (3)

85-95: Narrow the return type to RoomVersion

Align with types by casting the create event’s room_version to RoomVersion.

Apply this diff:

-		return createEvent.event.content.room_version;
+		return createEvent.event.content.room_version as RoomVersion;

441-445: Redundant undefined check on event.version

event.version is non-optional on PersistentEventBase; the guard can be dropped.

Apply this diff:

-		const roomVersion = event.version;
-		if (!roomVersion) {
-			throw new Error('Room version not found while filling prev events');
-		}
+		const roomVersion = event.version;

591-596: Redundant undefined check on event.version

Same note as above; simplify.

Apply this diff:

-		const roomVersion = event.version;
-
-		if (!roomVersion) {
-			throw new Error('Room version not found');
-		}
+		const roomVersion = event.version;
packages/room/src/manager/event-wrapper.ts (1)

423-434: Harden addPrevEvents/authedBy: empty arrays, dedupe, and depth from max

  • addPrevEvents assumes non-empty events; calling with [] will crash.
  • prev_events/auth_events may be undefined on raw input; initialize before push.
  • Depth should use max depth of provided events (order not guaranteed).

Apply this diff:

-	addPrevEvents(events: PersistentEventBase<Version>[]) {
-		this.rawEvent.prev_events.push(...events.map((e) => e.eventId));
-		if (this.rawEvent.depth <= events[events.length - 1].depth) {
-			this.rawEvent.depth = events[events.length - 1].depth + 1;
-		}
-		return this;
-	}
+	addPrevEvents(events: PersistentEventBase<Version>[]) {
+		if (!events?.length) {
+			return this;
+		}
+		this.rawEvent.prev_events ??= [];
+		this.rawEvent.prev_events.push(...events.map((e) => e.eventId));
+		const maxDepth = Math.max(...events.map((e) => e.depth));
+		if (this.rawEvent.depth <= maxDepth) {
+			this.rawEvent.depth = maxDepth + 1;
+		}
+		return this;
+	}
 
-	authedBy(event: PersistentEventBase<Version>) {
-		this.rawEvent.auth_events.push(event.eventId);
-		return this;
-	}
+	authedBy(event: PersistentEventBase<Version>) {
+		this.rawEvent.auth_events ??= [];
+		if (!this.rawEvent.auth_events.includes(event.eventId)) {
+			this.rawEvent.auth_events.push(event.eventId);
+		}
+		return this;
+	}
packages/room/src/manager/v9.ts (1)

1-6: Prefer a type-only import for PduType.

PduType is exported purely as a type alias, so we can lean on import type here to keep the emitted JS tidy.

-import { PduType } from '../types/v3-11';
+import type { PduType } from '../types/v3-11';
packages/room/src/manager/v6.ts (1)

1-6: Import PduType as a type-only dependency.

Same rationale as in v9.ts: using import type avoids pulling in a non-existent runtime export.

-import { PduType } from '../types/v3-11';
+import type { PduType } from '../types/v3-11';
packages/room/src/authorizartion-rules/rules.ts (1)

6-6: Import RoomVersion as a type.

RoomVersion is also a type alias; switching to a type-only import keeps the emitted module surface clean.

-import { RoomVersion } from '../manager/type';
+import type { RoomVersion } from '../manager/type';
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 94cf2b2 and d14abed.

📒 Files selected for processing (13)
  • packages/federation-sdk/src/services/state.service.ts (5 hunks)
  • packages/room/src/authorizartion-rules/rules.ts (3 hunks)
  • packages/room/src/manager/event-wrapper.ts (5 hunks)
  • packages/room/src/manager/factory.ts (3 hunks)
  • packages/room/src/manager/power-level-event-wrapper.ts (1 hunks)
  • packages/room/src/manager/room-state.ts (1 hunks)
  • packages/room/src/manager/v11.ts (1 hunks)
  • packages/room/src/manager/v3.ts (2 hunks)
  • packages/room/src/manager/v6.ts (1 hunks)
  • packages/room/src/manager/v8.ts (1 hunks)
  • packages/room/src/manager/v9.spec.ts (1 hunks)
  • packages/room/src/manager/v9.ts (1 hunks)
  • packages/room/src/state_resolution/definitions/definitions.ts (7 hunks)
🧰 Additional context used
🧬 Code graph analysis (11)
packages/room/src/manager/v8.ts (2)
packages/room/src/types/v3-11.ts (1)
  • PduType (47-47)
packages/room/src/manager/v6.ts (1)
  • PersistentEventV6 (4-15)
packages/room/src/manager/power-level-event-wrapper.ts (2)
packages/federation-sdk/src/index.ts (1)
  • PersistentEventBase (21-21)
packages/room/src/manager/type.ts (1)
  • RoomVersion (14-14)
packages/room/src/authorizartion-rules/rules.ts (2)
packages/core/src/events/m.room.create.ts (1)
  • roomCreateEvent (26-43)
packages/room/src/manager/type.ts (1)
  • RoomVersion (14-14)
packages/room/src/manager/v11.ts (2)
packages/room/src/types/v3-11.ts (1)
  • PduType (47-47)
packages/room/src/manager/v9.ts (1)
  • PersistentEventV9 (4-16)
packages/room/src/manager/factory.ts (5)
packages/room/src/manager/v3.ts (1)
  • PersistentEventV3 (12-72)
packages/room/src/manager/v6.ts (1)
  • PersistentEventV6 (4-15)
packages/room/src/manager/v8.ts (1)
  • PersistentEventV8 (4-14)
packages/room/src/manager/v9.ts (1)
  • PersistentEventV9 (4-16)
packages/room/src/manager/v11.ts (1)
  • PersistentEventV11 (5-46)
packages/room/src/manager/event-wrapper.ts (2)
packages/room/src/manager/type.ts (1)
  • RoomVersion (14-14)
packages/room/src/manager/room-state.ts (1)
  • version (146-155)
packages/room/src/state_resolution/definitions/definitions.ts (1)
packages/room/src/manager/type.ts (1)
  • RoomVersion (14-14)
packages/room/src/manager/v9.ts (2)
packages/room/src/types/v3-11.ts (1)
  • PduType (47-47)
packages/room/src/manager/v8.ts (1)
  • PersistentEventV8 (4-14)
packages/room/src/manager/v6.ts (2)
packages/room/src/types/v3-11.ts (1)
  • PduType (47-47)
packages/room/src/manager/v3.ts (1)
  • PersistentEventV3 (12-72)
packages/room/src/manager/v3.ts (3)
packages/room/src/types/v3-11.ts (1)
  • PduType (47-47)
packages/federation-sdk/src/index.ts (1)
  • PersistentEventBase (21-21)
packages/room/src/manager/type.ts (1)
  • RoomVersion3To11 (3-12)
packages/room/src/manager/v9.spec.ts (3)
packages/room/src/manager/event-wrapper.ts (1)
  • event (108-117)
packages/room/src/manager/v9.ts (1)
  • PersistentEventV9 (4-16)
packages/room/src/types/_common.ts (1)
  • EventID (8-8)
🔇 Additional comments (20)
packages/federation-sdk/src/services/state.service.ts (2)

481-482: LGTM: using event.version for store/state ops

Consistent with the PR’s direction to thread version via the wrapper.


743-744: LGTM: timeline path now uses event.version for store

Matches the new store/getEvents flow.

packages/room/src/manager/event-wrapper.ts (4)

44-46: Generic parameterization looks good

Switching to Version extends RoomVersion with a sensible default improves type safety across wrappers.


147-153: New helpers for IDs are clear and align with store-based retrieval

Naming reflects return values; no concerns.


167-216: Type guards updated to carry Version — good

Narrowing to PersistentEventBase<Version, ...> keeps downstream code precise.


53-56: Update direct instantiations to include the new version parameter

  • packages/room/src/manager/v9.spec.ts:7 – add the version argument to new PersistentEventV9(...)
  • Scan for any other new PersistentEvent*( calls (especially in tests) and pass the appropriate version
  • Verify each PersistentEventV* subclass constructor forwards (event, version) to super
packages/room/src/manager/v8.ts (1)

4-6: Generic Type parameterization: LGTM

Matches v3/v6/v9/v11 and keeps the event type precise.

packages/room/src/manager/v3.ts (2)

12-15: v3 wrapper now generic over PduType — LGTM

Extending PersistentEventBase<RoomVersion3To11, Type> aligns with the new API surface.


3-4: Importing PduType for the generic constraint is correct

No additional changes needed here.

packages/room/src/manager/factory.ts (1)

53-76: Explicit room version propagation in the factory looks solid.

Thanks for threading the checked roomVersion through to every concrete PersistentEventV* ctor—this keeps the wrappers’ hashing/ID paths consistent with the room schema. The dispatch table still covers the supported versions cleanly.

packages/room/src/manager/v9.spec.ts (1)

6-41: Test update covers the new constructor contract.

Nice to see the spec re-based on the two-arg ctor and the stricter ID typing—it keeps confidence that the hashed event ID remains stable across versions.

packages/room/src/authorizartion-rules/rules.ts (2)

172-198: Batching the membership prev-event lookup is a welcome improvement.

Fetching the previous PDUs in one getEvents call should trim latency on high-latency stores without altering the downstream logic—looks good.


352-356: Typed roomCreateEvent requirement is spot on.

Constraining the power-level validator to a concrete 'm.room.create' wrapper gives the helper the invariants it expects when reading defaults.

packages/room/src/manager/v11.ts (2)

1-1: Type-only import is correct.

Keeps runtime lean and aligns with TS best practices.


5-7: Generic parameterization looks good.

Extends the v9 generic correctly and matches the broader version‑aware refactor.

packages/room/src/manager/power-level-event-wrapper.ts (1)

71-74: Tightened type for createEvent is appropriate.

Constraining to PersistentEventBase<RoomVersion, 'm.room.create'> improves safety without changing behavior.

packages/room/src/state_resolution/definitions/definitions.ts (4)

24-27: Returning possibly undefined from getStateByMapKey is correct.

Matches real map semantics and prevents unsafe assumptions at call sites.

Please confirm all call sites now handle undefined (e.g., guards or non-null asserts removed).


445-445: Store-backed fetch in mainline traversal is fine.

Matches the refactor to getEvents().


500-502: Store-backed fetch for mainline position lookup is fine.


579-579: Store-backed fetch for iterative auth checks is fine.

Keeps a consistent data access path.

Consider confirming EventStore.getEvents is idempotent and efficiently batches duplicates to avoid N+1 patterns during state resolution.

@ggazzo ggazzo merged commit b52b385 into main Sep 26, 2025
3 checks passed
@ggazzo ggazzo deleted the propagate-room-version branch September 26, 2025 21:44
@coderabbitai coderabbitai bot mentioned this pull request Nov 5, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants