Skip to content

Conversation

@ggazzo
Copy link
Member

@ggazzo ggazzo commented Dec 2, 2025

Summary by CodeRabbit

  • New Features

    • Added a notification API that emits Matrix protocol events for room lifecycle, messages, encryption, reactions, redactions, membership, and power-level changes.
  • Refactor

    • Unified room identifier types across federation services for stronger type consistency and safer API surfaces.
    • Simplified event processing to route notifications through the new notify flow.
  • Improvements

    • Added informational logging when processing joins for missing rooms.
  • Tests

    • Lightweight EventService stub added to state service tests.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 2, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Replaces plain string room identifiers with the branded RoomID across federation-sdk queues, listeners, repositories, and services; adds EventService.notify() and routes staging-area notifications to it; injects EventService into StateService and updates related method signatures and types.

Changes

Cohort / File(s) Change Summary
Staging Area Queue & Listener
packages/federation-sdk/src/queues/staging-area.queue.ts, packages/federation-sdk/src/listeners/staging-area.listener.ts
Queue item types and handler signatures changed from string to RoomID; internal queue storage and dequeue casts updated.
Repositories
packages/federation-sdk/src/repositories/event-staging.repository.ts, packages/federation-sdk/src/repositories/upload.repository.ts
getDistinctStagedRooms() return type changed to Promise<RoomID[]>; Upload.federation.mrid field type changed from string to RoomID.
Edu & Send-Join Services
packages/federation-sdk/src/services/edu.service.ts, packages/federation-sdk/src/services/send-join.service.ts
Method parameters updated to accept RoomID (e.g., typing for typing/presence APIs and sendJoin).
Event Authorization Service
packages/federation-sdk/src/services/event-authorization.service.ts
Room-related parameters now use RoomID; canAccessResource made generic to map entity types to `EventID
Event Service
packages/federation-sdk/src/services/event.service.ts
Added new public notify(event: { eventId: EventID; event: Pdu }) method; getState and internal PDU grouping now use RoomID; imports for RoomState/State added.
Staging Area Service
packages/federation-sdk/src/services/staging-area.service.ts
processEventForRoom now accepts RoomID; removed processNotificationStage and replaced per-type emission logic with a single eventService.notify call.
State Service & Spec
packages/federation-sdk/src/services/state.service.ts, packages/federation-sdk/src/services/state.service.spec.ts
Multiple methods converted to accept RoomID; EventService injected into StateService (test stub added); runtime guard added in _mergeDivergentBranches; redaction casting refined to Pdu.
Room Service
packages/federation-sdk/src/services/room.service.ts
When join returns UnknownRoomError, now logs roomId info before processing initial state.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

  • Review the new EventService.notify implementation and ensure its event-to-emission mappings are correct.
  • Verify generic typing and casts in EventAuthorizationService.canAccessResource for sound type narrowing and no runtime issues.
  • Check StateService injection and that tests use proper stubs/mocks (state.service.spec.ts).
  • Confirm no runtime impact from replacing string keys with RoomID in maps/queues (serialization, DB queries, distinct calls).

Possibly related PRs

Suggested reviewers

  • ggazzo
  • rodrigok

Poem

🐇
I hopped through types with gentle cheer,
Swapped plain strings for RoomID here,
I rang notify, sent events to play,
State learned a friend to help the day,
A small rabbit claps — the code is clear!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 describes moving event notification handling to EventService, which aligns with the main changes showing notification logic consolidation and new notify method added to EventService.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/emit-events

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.

@ggazzo ggazzo force-pushed the chore/emit-events branch 2 times, most recently from db03e4d to a0761b7 Compare December 2, 2025 13:35
@codecov-commenter
Copy link

codecov-commenter commented Dec 2, 2025

Codecov Report

❌ Patch coverage is 7.03125% with 238 lines in your changes missing coverage. Please review.
✅ Project coverage is 52.81%. Comparing base (4729e3e) to head (8ae4e09).

Files with missing lines Patch % Lines
...kages/federation-sdk/src/services/event.service.ts 0.46% 212 Missing ⚠️
...ederation-sdk/src/services/staging-area.service.ts 0.00% 9 Missing ⚠️
...on-sdk/src/services/event-authorization.service.ts 0.00% 7 Missing ⚠️
...kages/federation-sdk/src/services/state.service.ts 78.57% 3 Missing ⚠️
...es/federation-sdk/src/queues/staging-area.queue.ts 33.33% 2 Missing ⚠️
...ackages/federation-sdk/src/services/edu.service.ts 0.00% 2 Missing ⚠️
...eration-sdk/src/listeners/staging-area.listener.ts 0.00% 1 Missing ⚠️
...n-sdk/src/repositories/event-staging.repository.ts 50.00% 1 Missing ⚠️
...s/federation-sdk/src/services/send-join.service.ts 0.00% 1 Missing ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main     #314   +/-   ##
=======================================
  Coverage   52.80%   52.81%           
=======================================
  Files          96       96           
  Lines       12589    12597    +8     
=======================================
+ Hits         6648     6653    +5     
- Misses       5941     5944    +3     

☔ 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.

@ggazzo ggazzo force-pushed the chore/emit-events branch from a0761b7 to 80de435 Compare December 2, 2025 13:37
@ggazzo ggazzo marked this pull request as ready for review December 2, 2025 13:37
@ggazzo ggazzo changed the title feat: move event notification handling in EventService chore: move event notification handling in EventService Dec 2, 2025
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: 1

🧹 Nitpick comments (2)
packages/federation-sdk/src/queues/staging-area.queue.ts (1)

31-32: Redundant type assertion.

Since queue is typed as RoomID[], shift() already returns RoomID | undefined. The explicit cast as RoomID is unnecessary and could mask issues if the queue type changes in the future.

-				const roomId = this.queue.shift() as RoomID;
+				const roomId = this.queue.shift();
packages/federation-sdk/src/services/event.service.ts (1)

980-989: Consider documenting the power level to role mapping.

The getRole helper only maps 100 → 'owner' and 50 → 'moderator'; all other levels default to 'user'. If this is intentional (e.g., mapping to a specific downstream system), a brief comment would clarify the design choice.

📜 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 4729e3e and 80de435.

📒 Files selected for processing (12)
  • packages/federation-sdk/src/listeners/staging-area.listener.ts (2 hunks)
  • packages/federation-sdk/src/queues/staging-area.queue.ts (2 hunks)
  • packages/federation-sdk/src/repositories/event-staging.repository.ts (2 hunks)
  • packages/federation-sdk/src/repositories/upload.repository.ts (1 hunks)
  • packages/federation-sdk/src/services/edu.service.ts (3 hunks)
  • packages/federation-sdk/src/services/event-authorization.service.ts (4 hunks)
  • packages/federation-sdk/src/services/event.service.ts (6 hunks)
  • packages/federation-sdk/src/services/room.service.ts (1 hunks)
  • packages/federation-sdk/src/services/send-join.service.ts (2 hunks)
  • packages/federation-sdk/src/services/staging-area.service.ts (3 hunks)
  • packages/federation-sdk/src/services/state.service.spec.ts (2 hunks)
  • packages/federation-sdk/src/services/state.service.ts (10 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: sampaiodiego
Repo: RocketChat/homeserver PR: 224
File: packages/federation-sdk/src/services/event-authorization.service.ts:261-268
Timestamp: 2025-10-10T22:18:31.655Z
Learning: In packages/federation-sdk/src/services/state.service.ts, the method `StateService.getLatestRoomState(roomId: string)` has return type `Promise<State>` and never returns undefined. If the state is not found, it throws an error with message "No state found for room ${roomId}" instead of returning undefined.
📚 Learning: 2025-10-10T22:18:31.655Z
Learnt from: sampaiodiego
Repo: RocketChat/homeserver PR: 224
File: packages/federation-sdk/src/services/event-authorization.service.ts:261-268
Timestamp: 2025-10-10T22:18:31.655Z
Learning: In packages/federation-sdk/src/services/state.service.ts, the method `StateService.getLatestRoomState(roomId: string)` has return type `Promise<State>` and never returns undefined. If the state is not found, it throws an error with message "No state found for room ${roomId}" instead of returning undefined.

Applied to files:

  • packages/federation-sdk/src/services/room.service.ts
  • packages/federation-sdk/src/services/send-join.service.ts
  • packages/federation-sdk/src/services/edu.service.ts
  • packages/federation-sdk/src/services/event-authorization.service.ts
  • packages/federation-sdk/src/repositories/event-staging.repository.ts
  • packages/federation-sdk/src/services/event.service.ts
  • packages/federation-sdk/src/services/state.service.ts
  • packages/federation-sdk/src/services/staging-area.service.ts
📚 Learning: 2025-09-14T13:34:12.448Z
Learnt from: ricardogarim
Repo: RocketChat/homeserver PR: 184
File: packages/federation-sdk/src/services/event-authorization.service.ts:213-223
Timestamp: 2025-09-14T13:34:12.448Z
Learning: ACL events (m.room.server_acl) are not yet implemented in the homeserver codebase, so ACL-related code currently handles the simpler case where no ACL events exist in room state.

Applied to files:

  • packages/federation-sdk/src/services/event-authorization.service.ts
🧬 Code graph analysis (6)
packages/federation-sdk/src/services/send-join.service.ts (3)
packages/room/src/manager/event-wrapper.ts (1)
  • roomId (111-113)
packages/room/src/types/_common.ts (1)
  • RoomID (16-16)
packages/federation-sdk/src/index.ts (1)
  • RoomID (33-33)
packages/federation-sdk/src/listeners/staging-area.listener.ts (1)
packages/federation-sdk/src/index.ts (1)
  • RoomID (33-33)
packages/federation-sdk/src/queues/staging-area.queue.ts (2)
packages/federation-sdk/src/queues/base.queue.ts (1)
  • QueueHandler (3-3)
packages/federation-sdk/src/index.ts (1)
  • RoomID (33-33)
packages/federation-sdk/src/services/event-authorization.service.ts (2)
packages/room/src/types/_common.ts (2)
  • RoomID (16-16)
  • EventID (8-8)
packages/federation-sdk/src/index.ts (2)
  • RoomID (33-33)
  • EventID (31-31)
packages/federation-sdk/src/services/event.service.ts (5)
packages/room/src/types/_common.ts (2)
  • RoomID (16-16)
  • EventID (8-8)
packages/federation-sdk/src/index.ts (4)
  • RoomID (33-33)
  • Pdu (25-25)
  • State (49-49)
  • EventID (31-31)
packages/room/src/types/v3-11.ts (1)
  • Pdu (828-828)
packages/room/src/manager/v3.ts (1)
  • eventId (17-29)
packages/room/src/manager/room-state.ts (1)
  • RoomState (11-179)
packages/federation-sdk/src/services/state.service.ts (5)
packages/room/src/types/_common.ts (1)
  • RoomID (16-16)
packages/federation-sdk/src/index.ts (4)
  • RoomID (33-33)
  • RoomVersion (30-30)
  • State (49-49)
  • Pdu (25-25)
packages/room/src/manager/type.ts (1)
  • RoomVersion (14-14)
packages/federation-sdk/src/specs/federation-api.ts (1)
  • State (171-171)
packages/room/src/types/v3-11.ts (1)
  • Pdu (828-828)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Agent
🔇 Additional comments (18)
packages/federation-sdk/src/listeners/staging-area.listener.ts (1)

2-2: LGTM!

The type change from string to RoomID is consistent with the broader PR migration and aligns with the StagingAreaQueue changes.

Also applies to: 18-18

packages/federation-sdk/src/services/room.service.ts (1)

946-950: LGTM!

Good addition of informational logging before processing the initial state. This improves observability when a join targets a room not yet in the local state.

packages/federation-sdk/src/repositories/upload.repository.ts (1)

1-1: LGTM!

Type update for federation.mrid to RoomID is consistent with the PR-wide migration to branded types.

Also applies to: 9-9

packages/federation-sdk/src/services/send-join.service.ts (1)

4-4: LGTM!

The sendJoin method signature update to accept RoomID is consistent with the broader type-safety improvements across the federation-sdk.

Also applies to: 24-25

packages/federation-sdk/src/repositories/event-staging.repository.ts (1)

71-72: Type assertion is reasonable but relies on data integrity.

The cast to RoomID[] assumes all stored roomId values are valid Matrix room IDs. Since the data originates from event.room_id (Line 36), this should hold true. Consider adding runtime validation if this assumption could be violated by corrupted data.

packages/federation-sdk/src/services/edu.service.ts (1)

7-7: LGTM!

Type updates for roomId: RoomID and roomIds: RoomID[] parameters are consistent with the PR-wide migration.

Also applies to: 23-24, 53-55

packages/federation-sdk/src/services/event.service.ts (3)

23-26: LGTM!

The RoomState and State imports support the new notify() method, and the Map<RoomID, Pdu[]> type change aligns with the PR-wide RoomID migration.

Also applies to: 157-157


767-784: LGTM!

The getState method signature update to RoomID is consistent with the PR migration. The explicit State type annotation at Line 781 is redundant given the immediate assignment but doesn't cause issues.


898-974: LGTM!

The event notification switch statement correctly routes events to the appropriate emitter channels. The switch(true) pattern with case expressions is a valid approach for type-based dispatch.

packages/federation-sdk/src/services/staging-area.service.ts (2)

126-130: Consider error handling for notify() call.

If eventService.notify() throws, the event will not be marked as unstaged and will be reprocessed. Verify this is the intended behavior. If notification failures should not block event processing, consider wrapping in try-catch or handling errors separately.


76-84: LGTM!

The RoomID type usage is consistent. The helper function correctly caches room versions using the RoomID as key, which works since branded types are still strings at runtime.

packages/federation-sdk/src/services/event-authorization.service.ts (2)

369-387: LGTM!

The generic canAccessResource method with conditional types provides good type safety while routing to the appropriate typed methods. The casts at lines 375 and 379 are necessary given TypeScript's limitations with narrowing generic types.


355-363: No action requiredrcUpload.federation.mrid is already correctly typed as RoomID in the UploadRepository. The type safety is maintained and serverHasAccessToResource receives the correct type.

packages/federation-sdk/src/services/state.service.spec.ts (1)

152-157: LGTM!

The minimal EventService stub is appropriate for tests focused on StateService behavior. The notify stub returns a resolved promise, preventing tests from failing due to the new dependency.

packages/federation-sdk/src/services/state.service.ts (4)

79-80: Dynamic require pattern for circular dependency.

Using require('./event.service').EventService inside @inject(delay(...)) is a valid approach to handle circular dependencies with tsyringe. However, this pattern can be fragile with bundlers or when the file is renamed.

Consider documenting this circular dependency or exploring alternative architectures to break the cycle if this pattern becomes common across multiple services.


1077-1091: Good defensive guard added.

The explicit check for empty stateIdArray at lines 1080-1082 prevents a potential undefined access that could occur if stateIds was unexpectedly empty despite the size check at line 1077.


344-346: LGTM!

Casting to Pdu instead of any improves type safety while maintaining the same runtime behavior.


106-116: LGTM!

The method signature correctly uses RoomID and throws UnknownRoomError directly with the typed parameter. The room_version access at line 115 is safe because if no create event exists, the method throws at line 112.

Based on learnings, StateService.getLatestRoomState throws rather than returning undefined, which aligns with the error-throwing pattern here.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR refactors event notification handling by moving the processNotificationStage logic from StagingAreaService to a new notify method in EventService. Additionally, it improves type safety across the codebase by replacing generic string types with more specific RoomID types.

  • Moved 200+ lines of event notification logic from StagingAreaService to EventService
  • Enhanced type safety by using RoomID branded type instead of generic string throughout the codebase
  • Added StateService dependency to EventService and EventService dependency to StateService (creating managed circular dependencies)

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/federation-sdk/src/services/event.service.ts Added new notify() method containing moved notification logic; added StateService dependency; improved types in processIncomingPDUs and getState
packages/federation-sdk/src/services/staging-area.service.ts Removed processNotificationStage() method (200+ lines); replaced with call to eventService.notify(); improved getRoomVersion logic
packages/federation-sdk/src/services/state.service.ts Added EventService dependency; changed parameter types from string to RoomID; improved type casting; added defensive error check
packages/federation-sdk/src/services/state.service.spec.ts Added mock EventService to test setup
packages/federation-sdk/src/services/send-join.service.ts Changed sendJoin roomId parameter type from string to RoomID
packages/federation-sdk/src/services/room.service.ts Added logging for unknown room scenarios
packages/federation-sdk/src/services/event-authorization.service.ts Improved canAccessResource with generic types; updated method signatures to use RoomID
packages/federation-sdk/src/services/edu.service.ts Changed method signatures to use RoomID type
packages/federation-sdk/src/repositories/upload.repository.ts Changed mrid field type from string to RoomID
packages/federation-sdk/src/repositories/event-staging.repository.ts Changed return type of getDistinctStagedRooms to RoomID[]
packages/federation-sdk/src/queues/staging-area.queue.ts Changed queue and handler types from string to RoomID
packages/federation-sdk/src/listeners/staging-area.listener.ts Updated handleQueueItem parameter type to RoomID

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 79 to 80
@inject(delay(() => require('./event.service').EventService))
private readonly eventService: EventService,
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

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

The eventService dependency is injected but never used in StateService. Consider removing this unused dependency to avoid unnecessary circular dependency complexity and reduce coupling.

Copilot uses AI. Check for mistakes.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
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

♻️ Duplicate comments (1)
packages/federation-sdk/src/services/event.service.ts (1)

975-1098: Users removed from power levels are not notified of role reset

In the m.room.power_levels case, the logic only considers:

  • Users present in the new users map but not in the old (new \ old) via setOrUnsetPowerLevels.
  • Users present in both with changed values.

Users that were present in oldPowerLevels but removed from changedUserPowers (i.e., old \ new) never get a homeserver.matrix.room.role event resetting them back to the default 'user' role. Their effective power level changes, but no notification is emitted.

You can fix this by computing and iterating the removed set as well, emitting a reset role event:

 					const usersInOldPowerLevelEvent = Object.keys(oldPowerLevels);
 					const usersInNewPowerLevelEvent = Object.keys(changedUserPowers);

 					const setOrUnsetPowerLevels = new Set(
 						usersInNewPowerLevelEvent,
 					).difference(new Set(usersInOldPowerLevelEvent));
+
+					// Users removed from explicit power levels (in old but not in new)
+					const removedFromPowerLevels = new Set(
+						usersInOldPowerLevelEvent,
+					).difference(new Set(usersInNewPowerLevelEvent));
+
+					for (const userId of removedFromPowerLevels) {
+						this.logger.debug(
+							`Emitting role reset for ${userId} removed from power levels`,
+						);
+						this.eventEmitterService.emit(
+							'homeserver.matrix.room.role',
+							{
+								sender_id: event.event.sender,
+								user_id: userId,
+								room_id: roomId,
+								role: 'user',
+							},
+						);
+					}

This ensures all users whose effective role changes (including removals) get a corresponding role event.

🧹 Nitpick comments (5)
packages/federation-sdk/src/repositories/event-staging.repository.ts (3)

3-3: Import RoomID as a type-only symbol

RoomID is only used in type positions here; you can mark it as a type-only import to avoid unnecessary runtime import from @rocket.chat/federation-room.

-import { type EventID, Pdu, RoomID } from '@rocket.chat/federation-room';
+import { type EventID, Pdu, type RoomID } from '@rocket.chat/federation-room';

51-54: Align roomId parameter type with RoomID

getLeastDepthEventForRoom still accepts roomId: string, while the rest of the PR is moving to RoomID. For consistency and stronger typing, consider updating the signature:

-	getLeastDepthEventForRoom(roomId: string): Promise<EventStagingStore | null> {
+	getLeastDepthEventForRoom(roomId: RoomID): Promise<EventStagingStore | null> {

This helps keep the repository API aligned with the domain-specific RoomID type.


71-72: Avoid unchecked cast from distinct() result to RoomID[]

collection.distinct('roomId') is being asserted directly to Promise<RoomID[]>, which is an unchecked cast and can hide mismatches between stored values and the RoomID contract.

Consider either:

  • Keeping the DB result as strings and letting callers wrap/validate, or
  • Explicitly mapping and asserting/validating per element:
async getDistinctStagedRooms(): Promise<RoomID[]> {
	const roomIds = await this.collection.distinct<string>('roomId');
	return roomIds.map((id) => id as RoomID);
	// or, if available:
	// return roomIds.map((id) => roomIdSchema.parse(id));
}

This makes the assumption about stored data more explicit and avoids a blanket cast on the whole promise type.

packages/federation-sdk/src/services/event-authorization.service.ts (1)

285-289: Dead code: getLatestRoomState throws on missing state, never returns undefined.

Based on learnings, StateService.getLatestRoomState() throws an error if state is not found rather than returning undefined. This if (!state) check is unreachable.

Consider either:

  1. Removing the dead check, or
  2. Wrapping the call in try-catch if you want to return false on missing state instead of propagating the error
packages/federation-sdk/src/services/state.service.ts (1)

29-82: Injected EventService is unused and introduces extra circular coupling

StateService now injects EventService via:

import type { EventService } from './event.service';

@inject(delay(() => require('./event.service').EventService))
private readonly eventService: EventService,

but this.eventService isn’t referenced anywhere in this class. This adds an unnecessary circular dependency between StateService and EventService.

Unless you’re about to use eventService here, consider removing this dependency:

-import type { EventService } from './event.service';
@@
-		@inject(delay(() => require('./event.service').EventService))
-		private readonly eventService: EventService,

This keeps the dependency graph simpler and avoids potential DI/container surprises.

📜 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 80de435 and 319b3e0.

📒 Files selected for processing (12)
  • packages/federation-sdk/src/listeners/staging-area.listener.ts (2 hunks)
  • packages/federation-sdk/src/queues/staging-area.queue.ts (2 hunks)
  • packages/federation-sdk/src/repositories/event-staging.repository.ts (2 hunks)
  • packages/federation-sdk/src/repositories/upload.repository.ts (1 hunks)
  • packages/federation-sdk/src/services/edu.service.ts (3 hunks)
  • packages/federation-sdk/src/services/event-authorization.service.ts (4 hunks)
  • packages/federation-sdk/src/services/event.service.ts (6 hunks)
  • packages/federation-sdk/src/services/room.service.ts (1 hunks)
  • packages/federation-sdk/src/services/send-join.service.ts (2 hunks)
  • packages/federation-sdk/src/services/staging-area.service.ts (3 hunks)
  • packages/federation-sdk/src/services/state.service.spec.ts (2 hunks)
  • packages/federation-sdk/src/services/state.service.ts (10 hunks)
🚧 Files skipped from review as they are similar to previous changes (7)
  • packages/federation-sdk/src/services/state.service.spec.ts
  • packages/federation-sdk/src/services/room.service.ts
  • packages/federation-sdk/src/queues/staging-area.queue.ts
  • packages/federation-sdk/src/listeners/staging-area.listener.ts
  • packages/federation-sdk/src/services/edu.service.ts
  • packages/federation-sdk/src/services/send-join.service.ts
  • packages/federation-sdk/src/services/staging-area.service.ts
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: sampaiodiego
Repo: RocketChat/homeserver PR: 224
File: packages/federation-sdk/src/services/event-authorization.service.ts:261-268
Timestamp: 2025-10-10T22:18:31.655Z
Learning: In packages/federation-sdk/src/services/state.service.ts, the method `StateService.getLatestRoomState(roomId: string)` has return type `Promise<State>` and never returns undefined. If the state is not found, it throws an error with message "No state found for room ${roomId}" instead of returning undefined.
📚 Learning: 2025-10-10T22:18:31.655Z
Learnt from: sampaiodiego
Repo: RocketChat/homeserver PR: 224
File: packages/federation-sdk/src/services/event-authorization.service.ts:261-268
Timestamp: 2025-10-10T22:18:31.655Z
Learning: In packages/federation-sdk/src/services/state.service.ts, the method `StateService.getLatestRoomState(roomId: string)` has return type `Promise<State>` and never returns undefined. If the state is not found, it throws an error with message "No state found for room ${roomId}" instead of returning undefined.

Applied to files:

  • packages/federation-sdk/src/services/event.service.ts
  • packages/federation-sdk/src/repositories/event-staging.repository.ts
  • packages/federation-sdk/src/services/state.service.ts
  • packages/federation-sdk/src/services/event-authorization.service.ts
📚 Learning: 2025-09-14T13:34:12.448Z
Learnt from: ricardogarim
Repo: RocketChat/homeserver PR: 184
File: packages/federation-sdk/src/services/event-authorization.service.ts:213-223
Timestamp: 2025-09-14T13:34:12.448Z
Learning: ACL events (m.room.server_acl) are not yet implemented in the homeserver codebase, so ACL-related code currently handles the simpler case where no ACL events exist in room state.

Applied to files:

  • packages/federation-sdk/src/services/event-authorization.service.ts
🧬 Code graph analysis (3)
packages/federation-sdk/src/repositories/event-staging.repository.ts (2)
packages/room/src/types/_common.ts (1)
  • RoomID (16-16)
packages/federation-sdk/src/index.ts (1)
  • RoomID (33-33)
packages/federation-sdk/src/services/state.service.ts (6)
packages/room/src/manager/event-wrapper.ts (3)
  • roomId (111-113)
  • State (47-55)
  • event (147-156)
packages/room/src/types/_common.ts (1)
  • RoomID (16-16)
packages/federation-sdk/src/index.ts (4)
  • RoomID (33-33)
  • RoomVersion (30-30)
  • State (49-49)
  • Pdu (25-25)
packages/room/src/manager/type.ts (1)
  • RoomVersion (14-14)
packages/federation-sdk/src/specs/federation-api.ts (1)
  • State (171-171)
packages/room/src/types/v3-11.ts (1)
  • Pdu (828-828)
packages/federation-sdk/src/services/event-authorization.service.ts (2)
packages/room/src/types/_common.ts (2)
  • RoomID (16-16)
  • EventID (8-8)
packages/federation-sdk/src/index.ts (2)
  • RoomID (33-33)
  • EventID (31-31)
🔇 Additional comments (14)
packages/federation-sdk/src/services/event-authorization.service.ts (5)

7-12: LGTM!

The RoomID import is correctly added to support the branded type usage in the updated method signatures.


266-283: LGTM!

The migration from string to RoomID branded type for checkAclForInvite and serverHasAccessToResource parameters improves type safety and aligns with the codebase-wide RoomID adoption.


369-379: LGTM!

The generic conditional type design for canAccessResource provides excellent type safety, ensuring the correct ID type is enforced based on the entity type at compile time.


355-363: rcUpload.federation.mrid is correctly typed as RoomID.

The Upload type definition confirms that federation.mrid is typed as RoomID (packages/federation-sdk/src/repositories/upload.repository.ts, line 9), matching the parameter type expected by serverHasAccessToResource. The upload repository/entity has been properly updated.


345-353: The room_id property is already correctly typed as RoomID.

The event.event.room_id returned by eventService.getEventById() is properly typed as RoomID through the EventStore structure from @rocket.chat/federation-core. The code at line 352 passes it directly to serverHasAccessToResource(), which expects a RoomID parameter, and TypeScript validates this type safety automatically. No changes are required.

packages/federation-sdk/src/repositories/upload.repository.ts (1)

1-13: RoomID typing for Upload.federation.mrid looks correct

Using RoomID for federation.mrid keeps the persisted shape as a string while tightening types across the federation SDK. No issues spotted.

packages/federation-sdk/src/services/event.service.ts (3)

51-64: Lazy injection of StateService is appropriate

Using @inject(delay(() => StateService)) for stateService is consistent with the other delayed injections and is a good way to avoid DI timing issues. No changes needed here.


155-167: Room-scoped PDU grouping keyed by RoomID is consistent

Switching eventsByRoomId to Map<RoomID, Pdu[]> aligns this flow with the branded RoomID type used elsewhere, without changing runtime behavior.


767-843: getState(roomId: RoomID, eventId: EventID) implementation looks sound

The method now:

  • Verifies the event belongs to the requested roomId.
  • Uses StateService.getStateBeforeEvent and the strongly-typed State to derive PDUs and auth chain.
  • Logs and rethrows on failure.

This matches the intended federation /state semantics and the new RoomID-based typing.

packages/federation-sdk/src/services/state.service.ts (5)

106-116: getRoomVersion(roomId: RoomID) refactor is consistent

Using RoomID for the parameter and throwing UnknownRoomError when the create event is missing matches the intended contract and uses the room_version from the stored create event directly. Behavior is clear and type-safe.


171-197: RoomID-aware state accessors preserve previous semantics

getLatestRoomState, getLatestRoomState2, and getStrippedRoomState now take RoomID but still:

  • Resolve state via _mergeDivergentBranches.
  • Throw if no state is found (No state found for room ${roomId}), rather than returning undefined.

This matches the documented behavior for getLatestRoomState from earlier work and simply tightens the types. Based on learnings, this looks correct.


334-356: Type cast in signEvent improves correctness of typings

Passing event.redactedEvent as Pdu into signEvent matches the expected shape and keeps the runtime behavior identical (signing the redacted form). This is a safe typing improvement.


787-817: RoomID-typed server discovery helpers are aligned with the rest of the API

Updating getServerSetInRoom and getServersInRoom to accept RoomID is a type-only change; the internal logic (iterating membership events and extracting domains) is unchanged and consistent with existing usage.


1063-1103: Defensive guard in _mergeDivergentBranches is acceptable

The added check:

const stateIdArray = stateIds.values().toArray();
if (stateIdArray.length === 0) {
	throw new Error('StateService: expected state ID but found none');
}

inside the stateIds.size === 1 branch is a defensive assertion against an unexpected empty set. It doesn’t alter the normal path (single stateId) and yields a clearer error in edge cases. No change needed.

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: 1

♻️ Duplicate comments (1)
packages/federation-sdk/src/services/event.service.ts (1)

1047-1073: Missing handling for users removed from power levels.

The diff logic only handles users newly added to power_levels.users (users in NEW but not in OLD). Users who were in the old power levels but removed in the new event are not notified of their role change to 'user'.

Apply this diff to handle removed users:

 				const setOrUnsetPowerLevels = new Set(
 					usersInNewPowerLevelEvent,
 				).difference(new Set(usersInOldPowerLevelEvent));

+				// Users removed from power levels (in old but not in new)
+				const removedFromPowerLevels = new Set(
+					usersInOldPowerLevelEvent,
+				).difference(new Set(usersInNewPowerLevelEvent));
+
+				for (const userId of removedFromPowerLevels) {
+					this.logger.debug(
+						`User ${userId} removed from power levels, resetting to user`,
+					);
+					this.eventEmitterService.emit('homeserver.matrix.room.role', {
+						sender_id: event.event.sender,
+						user_id: userId,
+						room_id: roomId,
+						role: 'user',
+					});
+				}
+
 				this.logger.debug(
🧹 Nitpick comments (5)
packages/federation-sdk/src/repositories/upload.repository.ts (1)

5-13: Branded RoomID for federation.mrid improves type safety; consider aligning rid

Switching federation.mrid to RoomID tightens typing without affecting persistence, which is good. If rid is also a Matrix/federation room identifier of the same shape, consider migrating it to RoomID as well for consistency; otherwise, maybe clarify the distinction via naming or a separate branded type.

packages/federation-sdk/src/services/event.service.ts (3)

939-944: Consider using isRedactedEvent for consistency with original logic.

The original processNotificationStage in StagingAreaService used isRedactedEvent(event.event) which checks both type === 'm.room.redaction' and 'redacts' in event. This implementation only checks the type.

+import { isRedactedEvent } from '@rocket.chat/federation-core';
...
-			case event.event.type === 'm.room.redaction': {
+			case isRedactedEvent(event.event): {

1002-1002: Add type cast for power levels content.

The event.event.content.users access lacks a type cast. Since event.event is typed as Pdu with generic content, TypeScript won't properly type users. Consider using PduPowerLevelsEventContent which is already imported elsewhere in this file context.

-				const changedUserPowers = event.event.content.users;
+				const changedUserPowers = (event.event.content as PduPowerLevelsEventContent).users;

You'll need to add the import:

import { PduPowerLevelsEventContent } from '@rocket.chat/federation-room';

897-1107: Large method with high cyclomatic complexity.

The notify() method is ~210 lines with a large switch statement and nested conditionals for power levels handling. Consider extracting the power levels logic into a separate private method to improve readability and testability.

private async handlePowerLevelsNotification(event: { eventId: EventID; event: Pdu }, roomId: RoomID): Promise<void> {
  // Move lines 974-1099 here
}
packages/federation-sdk/src/services/event-authorization.service.ts (1)

285-289: Unreachable code: getLatestRoomState throws on missing state.

Based on learnings, StateService.getLatestRoomState(roomId) throws an error with message "No state found for room ${roomId}" if the state is not found - it never returns undefined. This null check is dead code.

 	async serverHasAccessToResource(
 		roomId: RoomID,
 		serverName: string,
 	): Promise<boolean> {
-		const state = await this.stateService.getLatestRoomState(roomId);
-		if (!state) {
-			this.logger.debug(`Room ${roomId} not found`);
-			return false;
-		}
+		let state: State;
+		try {
+			state = await this.stateService.getLatestRoomState(roomId);
+		} catch (error) {
+			this.logger.debug(`Room ${roomId} not found`);
+			return false;
+		}

Or if the intent is to propagate the error, simply remove the null check:

 	async serverHasAccessToResource(
 		roomId: RoomID,
 		serverName: string,
 	): Promise<boolean> {
 		const state = await this.stateService.getLatestRoomState(roomId);
-		if (!state) {
-			this.logger.debug(`Room ${roomId} not found`);
-			return false;
-		}
📜 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 319b3e0 and 8ae4e09.

📒 Files selected for processing (11)
  • packages/federation-sdk/src/listeners/staging-area.listener.ts (2 hunks)
  • packages/federation-sdk/src/queues/staging-area.queue.ts (2 hunks)
  • packages/federation-sdk/src/repositories/event-staging.repository.ts (2 hunks)
  • packages/federation-sdk/src/repositories/upload.repository.ts (1 hunks)
  • packages/federation-sdk/src/services/edu.service.ts (3 hunks)
  • packages/federation-sdk/src/services/event-authorization.service.ts (4 hunks)
  • packages/federation-sdk/src/services/event.service.ts (5 hunks)
  • packages/federation-sdk/src/services/room.service.ts (1 hunks)
  • packages/federation-sdk/src/services/send-join.service.ts (2 hunks)
  • packages/federation-sdk/src/services/staging-area.service.ts (3 hunks)
  • packages/federation-sdk/src/services/state.service.ts (9 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
  • packages/federation-sdk/src/services/room.service.ts
  • packages/federation-sdk/src/listeners/staging-area.listener.ts
  • packages/federation-sdk/src/queues/staging-area.queue.ts
  • packages/federation-sdk/src/services/edu.service.ts
  • packages/federation-sdk/src/repositories/event-staging.repository.ts
  • packages/federation-sdk/src/services/send-join.service.ts
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: sampaiodiego
Repo: RocketChat/homeserver PR: 224
File: packages/federation-sdk/src/services/event-authorization.service.ts:261-268
Timestamp: 2025-10-10T22:18:31.655Z
Learning: In packages/federation-sdk/src/services/state.service.ts, the method `StateService.getLatestRoomState(roomId: string)` has return type `Promise<State>` and never returns undefined. If the state is not found, it throws an error with message "No state found for room ${roomId}" instead of returning undefined.
📚 Learning: 2025-09-14T13:34:12.448Z
Learnt from: ricardogarim
Repo: RocketChat/homeserver PR: 184
File: packages/federation-sdk/src/services/event-authorization.service.ts:213-223
Timestamp: 2025-09-14T13:34:12.448Z
Learning: ACL events (m.room.server_acl) are not yet implemented in the homeserver codebase, so ACL-related code currently handles the simpler case where no ACL events exist in room state.

Applied to files:

  • packages/federation-sdk/src/services/event-authorization.service.ts
📚 Learning: 2025-10-10T22:18:31.655Z
Learnt from: sampaiodiego
Repo: RocketChat/homeserver PR: 224
File: packages/federation-sdk/src/services/event-authorization.service.ts:261-268
Timestamp: 2025-10-10T22:18:31.655Z
Learning: In packages/federation-sdk/src/services/state.service.ts, the method `StateService.getLatestRoomState(roomId: string)` has return type `Promise<State>` and never returns undefined. If the state is not found, it throws an error with message "No state found for room ${roomId}" instead of returning undefined.

Applied to files:

  • packages/federation-sdk/src/services/event-authorization.service.ts
  • packages/federation-sdk/src/services/event.service.ts
  • packages/federation-sdk/src/services/staging-area.service.ts
  • packages/federation-sdk/src/services/state.service.ts
🧬 Code graph analysis (2)
packages/federation-sdk/src/services/staging-area.service.ts (6)
packages/room/src/manager/event-wrapper.ts (2)
  • roomId (111-113)
  • event (147-156)
packages/room/src/types/_common.ts (1)
  • RoomID (16-16)
packages/federation-sdk/src/index.ts (2)
  • RoomID (33-33)
  • RoomVersion (30-30)
packages/federation-sdk/src/services/event.service.ts (1)
  • getRoomVersion (466-471)
packages/federation-sdk/src/repositories/room.repository.ts (1)
  • getRoomVersion (54-60)
packages/room/src/manager/room-state.ts (1)
  • version (142-151)
packages/federation-sdk/src/services/state.service.ts (5)
packages/room/src/types/_common.ts (1)
  • RoomID (16-16)
packages/federation-sdk/src/index.ts (4)
  • RoomID (33-33)
  • RoomVersion (30-30)
  • State (49-49)
  • Pdu (25-25)
packages/room/src/manager/type.ts (1)
  • RoomVersion (14-14)
packages/federation-sdk/src/specs/federation-api.ts (1)
  • State (171-171)
packages/room/src/types/v3-11.ts (1)
  • Pdu (828-828)
🔇 Additional comments (15)
packages/federation-sdk/src/repositories/upload.repository.ts (1)

1-1: Import of RoomID looks correct

The new RoomID import integrates cleanly with this file and matches the usage in the Upload type below.

packages/federation-sdk/src/services/staging-area.service.ts (3)

18-18: LGTM - Import aligned with RoomID migration.

The addition of RoomID type import is consistent with the PR-wide migration from plain string to the branded RoomID type.


76-84: Type signature updated correctly for RoomID.

The processEventForRoom method now correctly accepts RoomID, and the internal getRoomVersion helper properly propagates this type. The cache map keyed by string is acceptable since RoomID is a branded string type.


126-129: Notification logic consolidated into EventService.

The call to eventService.notify() replaces the previously removed processNotificationStage method, centralizing event notification handling. This aligns with the PR objective of moving event notification handling into EventService.

packages/federation-sdk/src/services/event.service.ts (3)

23-25: LGTM - Added required type imports.

The RoomState and State imports are necessary for the new notify() method's power levels handling logic.


156-156: Map key type updated to RoomID.

Aligns with the PR-wide migration to use branded RoomID type for room identifiers.


767-767: Type signature and variable updated for RoomID consistency.

The getState method now correctly uses RoomID for the roomId parameter and State for the internal state variable, improving type safety.

Also applies to: 780-780

packages/federation-sdk/src/services/event-authorization.service.ts (3)

11-11: LGTM - Import added for RoomID type.

Consistent with the PR-wide RoomID migration.


266-266: Method signatures updated to use RoomID.

The checkAclForInvite, serverHasAccessToResource, and canAccessRoom methods now correctly use the branded RoomID type instead of plain string.

Also applies to: 282-282, 365-365


369-379: Well-designed generic type constraint for entity IDs.

The conditional type T extends 'event' ? EventID : T extends 'room' ? RoomID : string provides compile-time type safety for the entityId parameter based on the entityType.

packages/federation-sdk/src/services/state.service.ts (5)

104-113: LGTM - Type signature and return updated for RoomID.

The method now properly uses RoomID and the return statement accesses room_version directly. This is safe because if createEvent is found (line 109 check passed), the event content will have room_version as per the m.room.create event schema.


169-169: Method signatures consistently updated to use RoomID.

All public methods that accept room identifiers now use the branded RoomID type, providing better type safety across the codebase.

Also applies to: 180-180, 185-185, 785-785, 813-813


342-343: Type cast improved from any to Pdu.

Casting event.redactedEvent to Pdu instead of any improves type safety for the signEvent function call.


1061-1061: LGTM - Method signature updated for RoomID.

Consistent with other method updates in this file.


1077-1079: Simplified array access is correct.

The explicit array conversion and indexed access at stateIdArray[0] is clearer than using .pop() from the iterator. This correctly handles the single-state case.

import { EventRepository } from '../repositories/event.repository';
import { StateGraphRepository } from '../repositories/state-graph.repository';
import { ConfigService } from './config.service';
import type { EventService } from './event.service';
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

rg -n 'EventService|eventService' packages/federation-sdk/src/services/state.service.ts

Repository: RocketChat/homeserver

Length of output: 120


Remove unused EventService import.

The EventService type is imported at line 29 but has no usages in the file. It is not injected in the constructor nor referenced anywhere else. Remove the dead import.

🤖 Prompt for AI Agents
In packages/federation-sdk/src/services/state.service.ts around line 29, the
type import "EventService" is unused; remove the dead import statement to clean
up the file and avoid lint/type warnings by deleting the line "import type {
EventService } from './event.service';".

@sampaiodiego sampaiodiego merged commit 2d00b4c into main Dec 2, 2025
3 checks passed
@sampaiodiego sampaiodiego deleted the chore/emit-events branch December 2, 2025 14:00
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