Skip to content

Conversation

@d-gubert
Copy link
Member

@d-gubert d-gubert commented Dec 26, 2025

Proposed changes (including videos or screenshots)

Refactor listeners.js to listeners.ts.

There were some adaptations that were required to properly type this correctly, both in the file itself and others.

Issue(s)

ARCH-1917

Steps to test or reproduce

Further comments

Summary by CodeRabbit

  • Refactor

    • Consolidated app event delivery behind a single trigger API and introduced a centralized event bridge for event routing.
  • Chores

    • Standardized event identifiers to enum values and exported listener contract types.
    • Updated server flows to use the unified trigger API.
  • Tests

    • Updated tests to simulate events via the unified trigger API.
  • Notes

    • No user-facing behavior changes expected.

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

@changeset-bot
Copy link

changeset-bot bot commented Dec 26, 2025

⚠️ No Changeset found

Latest commit: aa67c53

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@dionisio-bot
Copy link
Contributor

dionisio-bot bot commented Dec 26, 2025

Looks like this PR is ready to merge! 🎉
If you have any trouble, please check the PR guidelines

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 26, 2025

Walkthrough

Replaces bridge-based listener calls with a unified Apps.self?.triggerEvent surface across server call sites, removes legacy JS AppListenerBridge and adds a typed TS AppListenerBridge, adjusts orchestrator handleEvent call shape, exports IListenerExecutor, and updates tests to stub Apps.self.triggerEvent.

Changes

Cohort / File(s) Summary
Tests
apps/meteor/app/api/server/lib/eraseTeam.spec.ts
Replaced bridge/listener mocks with Apps.self.triggerEvent stubs; preserved isLoaded.
Room deletion / erase
apps/meteor/app/api/server/lib/eraseTeam.ts, apps/meteor/server/lib/eraseRoom.ts, .../eraseRoomLooseValidation*
Switched roomEvent(...) bridge calls to Apps.self?.triggerEvent(AppEvents.*, ...) for pre/post room-delete flows; semantics preserved.
Message lifecycle
apps/meteor/app/lib/server/functions/deleteMessage.ts, apps/meteor/app/lib/server/functions/sendMessage.ts, apps/meteor/app/lib/server/functions/updateMessage.ts, apps/meteor/server/services/messages/service.ts
Replaced bridge-based message events with Apps.self?.triggerEvent(AppEvents.*, ...); sendMessage signature updated to (..., upsert = false, previewUrls?: string[]).
Livechat flows
apps/meteor/app/livechat/server/lib/RoutingManager.ts, apps/meteor/app/livechat/server/lib/closeRoom.ts, apps/meteor/app/livechat/server/lib/departmentsLib.ts
Replaced livechatEvent(...) bridge calls with Apps.self?.triggerEvent(AppEvents.*, ...).
Bridge migration
apps/meteor/app/apps/server/bridges/listeners.js (removed), apps/meteor/app/apps/server/bridges/listeners.ts (added)
Deleted legacy JS AppListenerBridge; added typed TS AppListenerBridge with handleEvent and specialized handlers (message/room/livechat/user/default) and converter/orchestrator integration.
Orchestrator / call shape
apps/meteor/ee/server/apps/orchestrator.js
triggerEvent now invokes bridge handleEvent({ event, payload }) (single-object arg) instead of multiple args.
UIKit / API typing
apps/meteor/ee/server/apps/communication/uikit.ts
Orchestrator type changed to IAppServerOrchestrator; event string literals replaced with AppEvents enums; conditional conversions added.
Type visibility
packages/apps-engine/src/server/managers/AppListenerManager.ts
Exported previously internal IListenerExecutor interface.
Misc call-site updates
various apps/meteor/... files
Multiple call sites updated to use Apps.self?.triggerEvent with null-safe checks and AppEvents enum values.

Sequence Diagram(s)

sequenceDiagram
  participant Client as Client
  participant Server as Rocket.Chat Server
  participant AppsSelf as Apps.self
  participant Orchestrator as App Orchestrator
  participant Converters as Converters
  participant ListenerMgr as AppListenerManager

  Client->>Server: action (send/update/delete room/message)
  Server->>AppsSelf: Apps.self?.triggerEvent(AppEvents.X, payload)
  AppsSelf->>Orchestrator: handleEvent({ event, payload })
  Orchestrator->>Converters: convert domain -> app payload
  Orchestrator->>ListenerMgr: execute listener
  ListenerMgr-->>Orchestrator: result (prevent/modify/ok)
  Orchestrator-->>AppsSelf: return result
  AppsSelf-->>Server: propagate result
  alt prevented
    Server->>Client: abort / throw
  else allowed/modified
    Server->>Server: continue (persist, notify)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Suggested labels

stat: ready to merge, stat: QA assured

Suggested reviewers

  • lucas-a-pelegrino
  • ggazzo

Poem

🐇 I hopped where bridges used to creak,
Apps.self now hums the event we seek.
Types and handlers spring in line,
Payloads convert, listeners align.
The rabbit cheers — it's merge time!

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: refactoring listeners.js to TypeScript. This is the primary objective reflected throughout the changeset.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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/convert-app-listeners-ts

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.

@codecov
Copy link

codecov bot commented Dec 26, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 70.70%. Comparing base (7fd6b3e) to head (aa67c53).
⚠️ Report is 11 commits behind head on develop.

Additional details and impacted files

Impacted file tree graph

@@             Coverage Diff             @@
##           develop   #37985      +/-   ##
===========================================
+ Coverage    70.68%   70.70%   +0.02%     
===========================================
  Files         3148     3147       -1     
  Lines       108850   108850              
  Branches     19547    19607      +60     
===========================================
+ Hits         76937    76961      +24     
+ Misses       29916    29886      -30     
- Partials      1997     2003       +6     
Flag Coverage Δ
e2e 60.21% <ø> (-0.02%) ⬇️
e2e-api 47.30% <100.00%> (-1.15%) ⬇️
unit 71.85% <100.00%> (+0.08%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

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

@github-actions
Copy link
Contributor

github-actions bot commented Dec 26, 2025

📦 Docker Image Size Report

➡️ Changes

Service Current Baseline Change Percent
sum of all images 0B 0B 0B
account-service 0B 0B 0B
authorization-service 0B 0B 0B
ddp-streamer-service 0B 0B 0B
omnichannel-transcript-service 0B 0B 0B
presence-service 0B 0B 0B
queue-worker-service 0B 0B 0B
rocketchat 0B 0B 0B

📊 Historical Trend

---
config:
  theme: "dark"
  xyChart:
    width: 900
    height: 400
---
xychart
  title "Image Size Evolution by Service (Last 30 Days + This PR)"
  x-axis ["11/18 22:53", "11/19 23:02", "11/21 16:49", "11/24 17:34", "11/27 22:32", "11/28 19:05", "12/01 23:01", "12/02 21:57", "12/03 21:00", "12/04 18:17", "12/05 21:56", "12/08 20:15", "12/09 22:17", "12/10 23:26", "12/11 21:56", "12/12 22:45", "12/13 01:34", "12/15 22:31", "12/16 22:18", "12/17 21:04", "12/18 23:12", "12/19 23:27", "12/20 21:03", "12/22 18:54", "12/23 16:16", "12/24 19:38", "12/25 17:51", "12/26 13:18", "12/29 19:01", "12/30 20:52", "01/14 14:33 (PR)"]
  y-axis "Size (GB)" 0 --> 0.5
  line "account-service" [0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.00]
  line "authorization-service" [0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.00]
  line "ddp-streamer-service" [0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.12, 0.00]
  line "omnichannel-transcript-service" [0.14, 0.14, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.00]
  line "presence-service" [0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.11, 0.00]
  line "queue-worker-service" [0.14, 0.14, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.00]
  line "rocketchat" [0.35, 0.35, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.34, 0.00]
Loading

Statistics (last 30 days):

  • 📊 Average: 1.5GiB
  • ⬇️ Minimum: 1.4GiB
  • ⬆️ Maximum: 1.6GiB
  • 🎯 Current PR: 0B
ℹ️ About this report

This report compares Docker image sizes from this build against the develop baseline.

  • Tag: pr-37985
  • Baseline: develop
  • Timestamp: 2026-01-14 14:33:57 UTC
  • Historical data points: 30

Updated: Wed, 14 Jan 2026 14:33:57 GMT

@d-gubert d-gubert force-pushed the chore/convert-app-listeners-ts branch 4 times, most recently from c7fad72 to b7a9aca Compare December 30, 2025 19:10
@d-gubert d-gubert marked this pull request as ready for review December 30, 2025 23:56
@d-gubert d-gubert requested review from a team as code owners December 30, 2025 23:56
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

No issues found across 14 files

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

🧹 Nitpick comments (2)
apps/meteor/app/livechat/server/lib/departmentsLib.ts (2)

140-143: Consider adding isLoaded() guard for consistency.

Other files (e.g., eraseRoom.ts, sendMessage.ts) guard triggerEvent calls with Apps.self?.isLoaded(). While optional chaining handles the null case, the isLoaded() check ensures apps are actually loaded before triggering events, which may have performance implications.

🔎 Proposed fix
 	// Disable event
 	if (department?.enabled && !departmentDB?.enabled) {
 		await callbacks.run('livechat.afterDepartmentDisabled', departmentDB);
-		void Apps.self?.triggerEvent(AppEvents.IPostLivechatDepartmentDisabled, { department: departmentDB });
+		if (Apps.self?.isLoaded()) {
+			void Apps.self?.triggerEvent(AppEvents.IPostLivechatDepartmentDisabled, { department: departmentDB });
+		}
 	}

271-273: Same consistency concern as above.

Consider adding an isLoaded() guard here as well for consistency with other event dispatch sites.

🔎 Proposed fix
 	await callbacks.run('livechat.afterRemoveDepartment', { department, agentsIds: removedAgents.map(({ agentId }) => agentId) });
-	void Apps.self?.triggerEvent(AppEvents.IPostLivechatDepartmentRemoved, { department });
+	if (Apps.self?.isLoaded()) {
+		void Apps.self?.triggerEvent(AppEvents.IPostLivechatDepartmentRemoved, { department });
+	}

 	return ret;
📜 Review details

Configuration used: Organization 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 e1e6f7b and b7a9aca.

📒 Files selected for processing (14)
  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/app/apps/server/bridges/listeners.js
  • apps/meteor/app/apps/server/bridges/listeners.ts
  • apps/meteor/app/lib/server/functions/deleteMessage.ts
  • apps/meteor/app/lib/server/functions/sendMessage.ts
  • apps/meteor/app/lib/server/functions/updateMessage.ts
  • apps/meteor/app/livechat/server/lib/RoutingManager.ts
  • apps/meteor/app/livechat/server/lib/closeRoom.ts
  • apps/meteor/app/livechat/server/lib/departmentsLib.ts
  • apps/meteor/ee/server/apps/communication/uikit.ts
  • apps/meteor/server/lib/eraseRoom.ts
  • apps/meteor/server/services/messages/service.ts
  • packages/apps-engine/src/server/managers/AppListenerManager.ts
💤 Files with no reviewable changes (1)
  • apps/meteor/app/apps/server/bridges/listeners.js
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • packages/apps-engine/src/server/managers/AppListenerManager.ts
  • apps/meteor/app/lib/server/functions/deleteMessage.ts
  • apps/meteor/server/lib/eraseRoom.ts
  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/app/livechat/server/lib/departmentsLib.ts
  • apps/meteor/app/livechat/server/lib/closeRoom.ts
  • apps/meteor/app/lib/server/functions/updateMessage.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
  • apps/meteor/server/services/messages/service.ts
  • apps/meteor/app/livechat/server/lib/RoutingManager.ts
  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
  • apps/meteor/ee/server/apps/communication/uikit.ts
  • apps/meteor/app/lib/server/functions/sendMessage.ts
**/*.spec.ts

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.spec.ts: Use descriptive test names that clearly communicate expected behavior in Playwright tests
Use .spec.ts extension for test files (e.g., login.spec.ts)

Files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
🧠 Learnings (21)
📚 Learning: 2025-11-04T16:49:19.107Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37377
File: apps/meteor/ee/server/hooks/federation/index.ts:86-88
Timestamp: 2025-11-04T16:49:19.107Z
Learning: In Rocket.Chat's federation system (apps/meteor/ee/server/hooks/federation/), permission checks follow two distinct patterns: (1) User-initiated federation actions (creating rooms, adding users to federated rooms, joining from invites) should throw MeteorError to inform users they lack 'access-federation' permission. (2) Remote server-initiated federation events should silently skip/ignore when users lack permission. The beforeAddUserToRoom hook only executes for local user-initiated actions, so throwing an error there is correct. Remote federation events are handled separately by the federation Matrix package with silent skipping logic.

Applied to files:

  • apps/meteor/app/lib/server/functions/deleteMessage.ts
  • apps/meteor/server/lib/eraseRoom.ts
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: AppUserBridge.getUserRoomIds in apps/meteor/app/apps/server/bridges/users.ts always returns an array of strings (mapping subscription documents to room IDs), never undefined, even when user has no room subscriptions.

Applied to files:

  • apps/meteor/server/lib/eraseRoom.ts
  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/app/livechat/server/lib/closeRoom.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
  • apps/meteor/app/livechat/server/lib/RoutingManager.ts
  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: AppUserBridge.getUserRoomIds in apps/meteor/app/apps/server/bridges/users.ts always returns an array of strings by mapping subscription documents to room IDs, never undefined, even when user has no room subscriptions.

Applied to files:

  • apps/meteor/server/lib/eraseRoom.ts
  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/app/livechat/server/lib/closeRoom.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
  • apps/meteor/app/livechat/server/lib/RoutingManager.ts
  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: UserBridge.doGetUserRoomIds in packages/apps-engine/src/server/bridges/UserBridge.ts has a bug where it implicitly returns undefined when the app lacks read permission (missing return statement in the else case of the permission check).

Applied to files:

  • apps/meteor/server/lib/eraseRoom.ts
  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/app/livechat/server/lib/closeRoom.ts
  • apps/meteor/app/livechat/server/lib/RoutingManager.ts
📚 Learning: 2025-10-28T16:53:42.761Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37205
File: ee/packages/federation-matrix/src/FederationMatrix.ts:296-301
Timestamp: 2025-10-28T16:53:42.761Z
Learning: In the Rocket.Chat federation-matrix integration (ee/packages/federation-matrix/), the createRoom method from rocket.chat/federation-sdk will support a 4-argument signature (userId, roomName, visibility, displayName) in newer versions. Code using this 4-argument call is forward-compatible with planned library updates and should not be flagged as an error.

Applied to files:

  • apps/meteor/server/lib/eraseRoom.ts
  • apps/meteor/app/livechat/server/lib/closeRoom.ts
  • apps/meteor/app/lib/server/functions/sendMessage.ts
📚 Learning: 2025-11-27T17:56:26.050Z
Learnt from: MartinSchoeler
Repo: RocketChat/Rocket.Chat PR: 37557
File: apps/meteor/client/views/admin/ABAC/AdminABACRooms.tsx:115-116
Timestamp: 2025-11-27T17:56:26.050Z
Learning: In Rocket.Chat, the GET /v1/abac/rooms endpoint (implemented in ee/packages/abac/src/index.ts) only returns rooms where abacAttributes exists and is not an empty array (query: { abacAttributes: { $exists: true, $ne: [] } }). Therefore, in components consuming this endpoint (like AdminABACRooms.tsx), room.abacAttributes is guaranteed to be defined for all returned rooms, and optional chaining before calling array methods like .join() is sufficient without additional null coalescing.

Applied to files:

  • apps/meteor/server/lib/eraseRoom.ts
📚 Learning: 2025-09-30T13:00:05.465Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 36990
File: apps/meteor/ee/server/apps/storage/AppRealStorage.ts:55-58
Timestamp: 2025-09-30T13:00:05.465Z
Learning: In AppRealStorage (apps/meteor/ee/server/apps/storage/AppRealStorage.ts), the `remove` method is designed to be idempotent and returns `{ success: true }` unconditionally because the goal is to ensure the app is removed, not to distinguish whether this specific call performed the deletion. Database errors will throw exceptions.

Applied to files:

  • apps/meteor/server/lib/eraseRoom.ts
📚 Learning: 2025-12-09T20:01:07.355Z
Learnt from: sampaiodiego
Repo: RocketChat/Rocket.Chat PR: 37532
File: ee/packages/federation-matrix/src/FederationMatrix.ts:920-927
Timestamp: 2025-12-09T20:01:07.355Z
Learning: In Rocket.Chat's federation invite handling (ee/packages/federation-matrix/src/FederationMatrix.ts), when a user rejects an invite via federationSDK.rejectInvite(), the subscription cleanup happens automatically through an event-driven flow: Matrix emits a leave event back, which is processed by handleLeave() in ee/packages/federation-matrix/src/events/member.ts, and that function calls Room.performUserRemoval() to clean up the subscription. No explicit cleanup is needed in the reject branch of handleInvite() because the leave event handler takes care of it.
<!-- </add_learning>

Applied to files:

  • apps/meteor/server/lib/eraseRoom.ts
📚 Learning: 2025-11-19T18:20:07.720Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37419
File: packages/i18n/src/locales/en.i18n.json:918-921
Timestamp: 2025-11-19T18:20:07.720Z
Learning: Repo: RocketChat/Rocket.Chat — i18n/formatting
Learning: This repository uses a custom message formatting parser in UI blocks/messages; do not assume standard Markdown rules. For keys like Call_ended_bold, Call_not_answered_bold, Call_failed_bold, and Call_transferred_bold in packages/i18n/src/locales/en.i18n.json, retain the existing single-asterisk emphasis unless maintainers request otherwise.

Applied to files:

  • apps/meteor/app/lib/server/functions/updateMessage.ts
📚 Learning: 2025-09-19T15:15:04.642Z
Learnt from: rodrigok
Repo: RocketChat/Rocket.Chat PR: 36991
File: apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts:219-221
Timestamp: 2025-09-19T15:15:04.642Z
Learning: The Federation_Matrix_homeserver_domain setting in apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts is part of the old federation system and is being deprecated/removed, so configuration issues with this setting should not be flagged for improvement.

Applied to files:

  • apps/meteor/app/lib/server/functions/updateMessage.ts
  • apps/meteor/server/services/messages/service.ts
📚 Learning: 2025-11-19T18:20:37.116Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37419
File: apps/meteor/server/services/media-call/service.ts:141-141
Timestamp: 2025-11-19T18:20:37.116Z
Learning: In apps/meteor/server/services/media-call/service.ts, the sendHistoryMessage method should use call.caller.id or call.createdBy?.id as the message author, not call.transferredBy?.id. Even for transferred calls, the message should appear in the DM between the two users who are calling each other, not sent by the person who transferred the call.

Applied to files:

  • apps/meteor/app/lib/server/functions/updateMessage.ts
  • apps/meteor/server/services/messages/service.ts
  • apps/meteor/app/livechat/server/lib/RoutingManager.ts
  • apps/meteor/app/lib/server/functions/sendMessage.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Ensure tests run reliably in parallel without shared state conflicts

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Ensure clean state for each test execution in Playwright tests

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Utilize Playwright fixtures (`test`, `page`, `expect`) for consistency in test files

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/page-objects/**/*.ts : Utilize existing page objects pattern from `apps/meteor/tests/e2e/page-objects/`

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Use `expect` matchers for assertions (`toEqual`, `toContain`, `toBeTruthy`, `toHaveLength`, etc.) instead of `assert` statements in Playwright tests

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : All test files must be created in `apps/meteor/tests/e2e/` directory

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Group related tests in the same file

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-10-06T20:30:45.540Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 37152
File: packages/apps-engine/tests/test-data/storage/storage.ts:101-122
Timestamp: 2025-10-06T20:30:45.540Z
Learning: In `packages/apps-engine/tests/test-data/storage/storage.ts`, the stub methods (updatePartialAndReturnDocument, updateStatus, updateSetting, updateAppInfo, updateMarketplaceInfo) intentionally throw "Method not implemented." Tests using these methods must stub them using `SpyOn` from the test library rather than relying on actual implementations.

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-12-10T21:00:54.909Z
Learnt from: KevLehman
Repo: RocketChat/Rocket.Chat PR: 37091
File: ee/packages/abac/jest.config.ts:4-7
Timestamp: 2025-12-10T21:00:54.909Z
Learning: Rocket.Chat monorepo: Jest testMatch pattern '<rootDir>/src/**/*.spec.(ts|js|mjs)' is valid in this repo and used across multiple packages (e.g., packages/tools, ee/packages/omnichannel-services). Do not flag it as invalid in future reviews.

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Maintain test isolation between test cases in Playwright tests

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
🧬 Code graph analysis (2)
apps/meteor/app/lib/server/functions/updateMessage.ts (1)
packages/core-services/src/index.ts (1)
  • Apps (152-152)
apps/meteor/app/lib/server/functions/sendMessage.ts (2)
packages/core-services/src/index.ts (1)
  • Apps (152-152)
apps/meteor/app/apps/server/bridges/listeners.ts (1)
  • messageEvent (107-171)
🪛 Biome (2.1.2)
apps/meteor/app/apps/server/bridges/listeners.ts

[error] 115-115: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 121-121: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 129-129: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 136-136: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 143-143: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 150-150: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 182-182: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 190-190: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 222-222: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 314-317: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

⏰ 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: cubic · AI code reviewer
🔇 Additional comments (21)
apps/meteor/app/livechat/server/lib/RoutingManager.ts (1)

289-289: LGTM! Event dispatch migration follows the new pattern.

The migration from bridge-based event invocation to Apps.self?.triggerEvent is correct, maintaining the fire-and-forget behavior with proper optional chaining.

apps/meteor/app/api/server/lib/eraseTeam.ts (2)

78-82: LGTM! Prevention event dispatch correctly migrated.

The prevention pattern is properly implemented with optional chaining and correct control flow.


91-93: LGTM! Post-deletion event dispatch migrated correctly.

Fire-and-forget pattern with optional chaining is appropriate for post-action events.

apps/meteor/app/lib/server/functions/deleteMessage.ts (2)

40-43: LGTM! Prevention event dispatch migrated correctly.

The prevention pattern with error throwing on prevent is correctly implemented.


103-105: The IPostMessageDeleted event is correctly invoked with (deletedMsg, user). The triggerEvent API accepts variadic arguments (...payload: any[]), and the listener bridge adapter in apps/meteor/app/apps/server/bridges/listeners.ts properly transforms these two arguments into the expected IMessageDeleteContext structure. No changes needed.

apps/meteor/ee/server/apps/communication/uikit.ts (5)

1-1: LGTM! Type abstraction improvement.

Using IAppServerOrchestrator interface instead of concrete class improves type safety and testability.

Also applies to: 196-199


216-217: LGTM! Conditional converter invocation is correct.

The ternary operators correctly prevent converter calls when IDs are absent, avoiding unnecessary operations.


233-236: LGTM! Event interface selection logic is correct.

The conditional selection between IUIKitInteractionHandler and IUIKitLivechatInteractionHandler based on visitor presence is appropriate.


311-312: LGTM! Consistent conditional converter pattern.

The conditional converter invocation matches the pattern used elsewhere in the file.


330-330: LGTM! Event dispatch uses AppEvents enum correctly.

Consistent with the pattern used throughout the file.

packages/apps-engine/src/server/managers/AppListenerManager.ts (1)

35-35: LGTM! Interface export enables bridge refactoring.

Making IListenerExecutor public allows the new TypeScript bridge implementation to properly type event handlers.

apps/meteor/app/livechat/server/lib/closeRoom.ts (1)

98-99: LGTM! Livechat room closed events migrated correctly.

Both the deprecated and current event dispatches use the new triggerEvent pattern correctly, with the deprecation notice properly preserved.

apps/meteor/app/lib/server/functions/updateMessage.ts (3)

28-31: LGTM! Prevention event dispatch migrated correctly.

The prevention pattern with error throwing is properly implemented.


33-38: LGTM! Sequential event processing is correct.

The extend-then-modify pattern correctly chains the events, with proper type checking before assignment.


88-92: LGTM! Post-update event dispatch migrated correctly.

Fire-and-forget pattern with optional chaining is appropriate.

apps/meteor/server/services/messages/service.ts (1)

1-1: LGTM! System message event dispatch migrated correctly.

The import addition and triggerEvent usage follow the established pattern throughout this refactor.

Also applies to: 198-200

apps/meteor/app/api/server/lib/eraseTeam.spec.ts (1)

43-46: LGTM!

The test stubs are correctly updated to mock the new Apps.self.triggerEvent API. The pattern is consistent across all stub configurations:

  • isLoaded returns a boolean via function call
  • triggerEvent is a sinon stub that returns appropriate values for each test scenario

Also applies to: 195-198, 247-250

apps/meteor/server/lib/eraseRoom.ts (1)

43-48: LGTM!

The event dispatch migration is correctly implemented:

  • Pre-delete prevention properly awaits the result and checks for truthy value to throw
  • Post-delete notification uses void for fire-and-forget semantics
  • Both calls are guarded by Apps.self?.isLoaded() check

Also applies to: 59-61

apps/meteor/app/lib/server/functions/sendMessage.ts (2)

233-251: LGTM!

The pre-send event flow is correctly implemented:

  • Prevent check properly short-circuits on truthy result
  • Extend/Modify chain correctly nests the calls so extend runs first, then modify receives the extended message
  • Result validation with validateMessage after app modifications prevents malicious changes

282-286: LGTM!

Post-send event dispatch correctly distinguishes between system messages (IPostSystemMessageSent) and regular messages (IPostMessageSent) using the message type field t. Fire-and-forget with void is appropriate here.

apps/meteor/app/apps/server/bridges/listeners.ts (1)

32-33: LGTM on class structure and type definitions.

The AppListenerBridge class is well-structured with clear separation of concerns. The type definitions for LivechatTransferData, LivechatAgentData, UserStatusChangedData, and UserCrudData are appropriate for the internal payload shapes.

@d-gubert d-gubert force-pushed the chore/convert-app-listeners-ts branch from b7a9aca to 6a04195 Compare January 7, 2026 14:03
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: 5

🤖 Fix all issues with AI agents
In @apps/meteor/app/apps/server/bridges/listeners.ts:
- Around line 313-320: The default case in the switch needs a block to avoid TDZ
for the const declaration; wrap the default branch body in braces and move the
const room declaration and subsequent await/return inside that block so the code
uses block scoping (i.e., change the default: handling around
this.orch.getConverters().get('rooms').convertRoom(...) and the return
this.orch.getManager().getListenerManager().executeListener(inte, room) to be
enclosed in { ... }).
- Around line 323-365: The userEvent method lacks an explicit return for
unexpected interface values; update the switch in userEvent to include a
default/fallback that explicitly returns (or throws) a value so the
Promise<unknown> is deterministic—e.g., add a default case after the existing
AppInterface cases that either returns Promise.resolve(undefined) or throws an
Error including the unexpected inte value; reference the userEvent function and
AppInterface enum to locate and modify the switch and ensure the method always
returns.
- Around line 219-230: The switch case for
AppInterface.IPostLivechatAgentAssigned / IPostLivechatAgentUnassigned declares
const agentData inside the case which is not block-scoped; wrap the case body in
braces to create a new block so const agentData is properly scoped. Locate the
switch handling these cases (the case labels
AppInterface.IPostLivechatAgentAssigned and
AppInterface.IPostLivechatAgentUnassigned) and enclose the logic that computes
agentData and calls
this.orch.getManager().getListenerManager().executeListener(...) in { ... } to
prevent TDZ/hoisting issues and ensure converters
(this.orch.getConverters().get('rooms') / get('users')) are used inside the new
block.
- Around line 178-199: The switch inside the params IIFE declares consts in
multiple case clauses which aren’t block-scoped; wrap the bodies of the cases
for AppInterface.IPreRoomUserJoined/AppInterface.IPostRoomUserJoined (the
joiningUser and invitingUser declarations) and
AppInterface.IPreRoomUserLeave/AppInterface.IPostRoomUserLeave (the leavingUser
and removedBy declarations) in braces so each case has its own block scope and
avoids TDZ errors; keep the returned objects the same and only add { ... }
around those case bodies.
- Around line 112-159: The switch inside the params IIFE creates block-scoped
consts in each case which can leak/trigger TDZ; to fix, wrap each case body in
its own brace block (e.g., case AppInterface.IPostMessageDeleted: { const
[userDeleted] = ...; return {...}; } ), doing the same for IPostMessageReacted,
IPostMessageFollowed, IPostMessagePinned, IPostMessageStarred and
IPostMessageReported so each const is confined to its case block and the default
return stays unchanged.
🧹 Nitpick comments (1)
apps/meteor/app/lib/server/functions/sendMessage.ts (1)

234-243: The nested triggerEvent pattern is already safe due to the guard clause.

The code at lines 240-243 is wrapped by the if (Apps.self?.isLoaded()) check at line 233, which ensures Apps.self is available throughout the entire block. Additionally, Apps.self is never reassigned in the codebase, making the optional chaining pattern consistent and safe. No race conditions exist between the inner and outer calls.

If readability is a concern, the nested pattern could optionally be refactored to extract the extended message first, but this is not necessary for correctness.

📜 Review details

Configuration used: Organization 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 b7a9aca and 6a04195.

📒 Files selected for processing (14)
  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/app/apps/server/bridges/listeners.js
  • apps/meteor/app/apps/server/bridges/listeners.ts
  • apps/meteor/app/lib/server/functions/deleteMessage.ts
  • apps/meteor/app/lib/server/functions/sendMessage.ts
  • apps/meteor/app/lib/server/functions/updateMessage.ts
  • apps/meteor/app/livechat/server/lib/RoutingManager.ts
  • apps/meteor/app/livechat/server/lib/closeRoom.ts
  • apps/meteor/app/livechat/server/lib/departmentsLib.ts
  • apps/meteor/ee/server/apps/communication/uikit.ts
  • apps/meteor/server/lib/eraseRoom.ts
  • apps/meteor/server/services/messages/service.ts
  • packages/apps-engine/src/server/managers/AppListenerManager.ts
💤 Files with no reviewable changes (1)
  • apps/meteor/app/apps/server/bridges/listeners.js
🚧 Files skipped from review as they are similar to previous changes (7)
  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
  • apps/meteor/app/livechat/server/lib/RoutingManager.ts
  • apps/meteor/server/services/messages/service.ts
  • packages/apps-engine/src/server/managers/AppListenerManager.ts
  • apps/meteor/app/livechat/server/lib/departmentsLib.ts
  • apps/meteor/app/lib/server/functions/updateMessage.ts
  • apps/meteor/server/lib/eraseRoom.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/app/livechat/server/lib/closeRoom.ts
  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/app/lib/server/functions/deleteMessage.ts
  • apps/meteor/ee/server/apps/communication/uikit.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
  • apps/meteor/app/lib/server/functions/sendMessage.ts
🧠 Learnings (8)
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: AppUserBridge.getUserRoomIds in apps/meteor/app/apps/server/bridges/users.ts always returns an array of strings (mapping subscription documents to room IDs), never undefined, even when user has no room subscriptions.

Applied to files:

  • apps/meteor/app/livechat/server/lib/closeRoom.ts
  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: UserBridge.doGetUserRoomIds in packages/apps-engine/src/server/bridges/UserBridge.ts has a bug where it implicitly returns undefined when the app lacks read permission (missing return statement in the else case of the permission check).

Applied to files:

  • apps/meteor/app/livechat/server/lib/closeRoom.ts
  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: AppUserBridge.getUserRoomIds in apps/meteor/app/apps/server/bridges/users.ts always returns an array of strings by mapping subscription documents to room IDs, never undefined, even when user has no room subscriptions.

Applied to files:

  • apps/meteor/app/livechat/server/lib/closeRoom.ts
  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
📚 Learning: 2025-10-28T16:53:42.761Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37205
File: ee/packages/federation-matrix/src/FederationMatrix.ts:296-301
Timestamp: 2025-10-28T16:53:42.761Z
Learning: In the Rocket.Chat federation-matrix integration (ee/packages/federation-matrix/), the createRoom method from rocket.chat/federation-sdk will support a 4-argument signature (userId, roomName, visibility, displayName) in newer versions. Code using this 4-argument call is forward-compatible with planned library updates and should not be flagged as an error.

Applied to files:

  • apps/meteor/app/livechat/server/lib/closeRoom.ts
  • apps/meteor/app/lib/server/functions/sendMessage.ts
📚 Learning: 2025-09-30T13:00:05.465Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 36990
File: apps/meteor/ee/server/apps/storage/AppRealStorage.ts:55-58
Timestamp: 2025-09-30T13:00:05.465Z
Learning: In AppRealStorage (apps/meteor/ee/server/apps/storage/AppRealStorage.ts), the `remove` method is designed to be idempotent and returns `{ success: true }` unconditionally because the goal is to ensure the app is removed, not to distinguish whether this specific call performed the deletion. Database errors will throw exceptions.

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.ts
📚 Learning: 2025-11-27T17:56:26.050Z
Learnt from: MartinSchoeler
Repo: RocketChat/Rocket.Chat PR: 37557
File: apps/meteor/client/views/admin/ABAC/AdminABACRooms.tsx:115-116
Timestamp: 2025-11-27T17:56:26.050Z
Learning: In Rocket.Chat, the GET /v1/abac/rooms endpoint (implemented in ee/packages/abac/src/index.ts) only returns rooms where abacAttributes exists and is not an empty array (query: { abacAttributes: { $exists: true, $ne: [] } }). Therefore, in components consuming this endpoint (like AdminABACRooms.tsx), room.abacAttributes is guaranteed to be defined for all returned rooms, and optional chaining before calling array methods like .join() is sufficient without additional null coalescing.

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.ts
📚 Learning: 2025-11-04T16:49:19.107Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37377
File: apps/meteor/ee/server/hooks/federation/index.ts:86-88
Timestamp: 2025-11-04T16:49:19.107Z
Learning: In Rocket.Chat's federation system (apps/meteor/ee/server/hooks/federation/), permission checks follow two distinct patterns: (1) User-initiated federation actions (creating rooms, adding users to federated rooms, joining from invites) should throw MeteorError to inform users they lack 'access-federation' permission. (2) Remote server-initiated federation events should silently skip/ignore when users lack permission. The beforeAddUserToRoom hook only executes for local user-initiated actions, so throwing an error there is correct. Remote federation events are handled separately by the federation Matrix package with silent skipping logic.

Applied to files:

  • apps/meteor/app/lib/server/functions/deleteMessage.ts
📚 Learning: 2025-11-19T18:20:37.116Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37419
File: apps/meteor/server/services/media-call/service.ts:141-141
Timestamp: 2025-11-19T18:20:37.116Z
Learning: In apps/meteor/server/services/media-call/service.ts, the sendHistoryMessage method should use call.caller.id or call.createdBy?.id as the message author, not call.transferredBy?.id. Even for transferred calls, the message should appear in the DM between the two users who are calling each other, not sent by the person who transferred the call.

Applied to files:

  • apps/meteor/app/lib/server/functions/sendMessage.ts
🧬 Code graph analysis (2)
apps/meteor/app/apps/server/bridges/listeners.ts (2)
packages/apps/src/AppsEngine.ts (1)
  • AppInterface (13-13)
packages/apps-engine/src/server/managers/AppListenerManager.ts (1)
  • IListenerExecutor (35-241)
apps/meteor/app/lib/server/functions/sendMessage.ts (2)
packages/core-services/src/index.ts (1)
  • Apps (152-152)
apps/meteor/app/apps/server/bridges/listeners.ts (1)
  • messageEvent (107-171)
🪛 Biome (2.1.2)
apps/meteor/app/apps/server/bridges/listeners.ts

[error] 115-115: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 121-121: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 129-129: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 136-136: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 143-143: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 150-150: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 182-182: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 190-190: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 222-222: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 314-317: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

⏰ 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). (3)
  • GitHub Check: 📦 Build Packages
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build
🔇 Additional comments (17)
apps/meteor/app/api/server/lib/eraseTeam.ts (1)

77-82: LGTM! Clean migration to direct event triggering.

The refactor from bridge-based dispatch to Apps.self?.triggerEvent() maintains the existing control flow correctly:

  • Line 78: Prevention event properly awaited with early return on prevent
  • Line 92: Post-deletion event appropriately fire-and-forget with void
  • Both guarded by isLoaded() checks

Also applies to: 91-93

apps/meteor/app/livechat/server/lib/closeRoom.ts (1)

93-100: LGTM! Event migration preserves deferred execution pattern.

The refactor correctly:

  • Maintains process.nextTick deferral to avoid blocking the transaction
  • Uses void for fire-and-forget event dispatch
  • Keeps the deprecated event for backwards compatibility as noted in the comment
  • Aligns with the broader Apps framework migration
apps/meteor/ee/server/apps/communication/uikit.ts (3)

196-198: LGTM! Interface-based dependency improves testability.

Changing from AppServerOrchestrator concrete class to IAppServerOrchestrator interface follows dependency inversion principle and makes the class easier to test with mocks.


216-217: LGTM! Conditional conversions prevent unnecessary lookups.

Making message and room conversions conditional on ID presence is a solid improvement:

  • Avoids unnecessary database/converter calls when IDs are absent
  • Returns explicit undefined for cleaner downstream handling
  • Pattern is consistent across all interaction types

Also applies to: 311-312


233-233: LGTM! AppEvents enum constants improve type safety.

Replacing string literals with AppEvents enum values provides:

  • Compile-time validation of event names
  • Better IDE support with autocomplete
  • Prevents typos and invalid event references
  • Line 233 correctly selects handler based on visitor presence

Also applies to: 264-264, 289-289, 330-330

apps/meteor/app/lib/server/functions/deleteMessage.ts (2)

40-43: LGTM! Pre-delete prevention correctly migrated to Apps.self?.triggerEvent.

The migration from bridge-based event to Apps.self?.triggerEvent(AppEvents.IPreMessageDeletePrevent, deletedMsg) is correct. The optional chaining ensures graceful handling when Apps is not loaded, and the prevention logic properly throws an error when an app blocks the deletion.


103-105: LGTM! Post-delete event properly dispatched.

The post-delete notification correctly uses Apps.self?.triggerEvent(AppEvents.IPostMessageDeleted, deletedMsg, user) and is properly guarded by the deletedMsg existence check.

apps/meteor/app/lib/server/functions/sendMessage.ts (2)

1-1: LGTM! Import updated correctly.

The addition of AppEvents and Apps imports from @rocket.chat/apps aligns with the TypeScript refactoring objective.


284-285: LGTM! Post-message event dispatch correctly implemented.

The conditional selection between AppEvents.IPostSystemMessageSent and AppEvents.IPostMessageSent based on message.t correctly distinguishes system messages from regular messages, and the void operator appropriately indicates fire-and-forget semantics.

apps/meteor/app/apps/server/bridges/listeners.ts (8)

1-6: LGTM!

The imports are properly structured with type-only imports where appropriate, following TypeScript best practices.


8-30: LGTM!

The internal type definitions are clear, properly typed, and appropriately scoped for this module.


32-33: LGTM!

Constructor properly initializes the orchestrator as a private readonly field.


35-96: LGTM!

The event routing logic comprehensively maps all AppInterface events to their respective handlers. The type assertion on line 95 is necessary and well-documented.


98-105: LGTM!

The default event handler appropriately delegates payload validation to the listener manager.


231-256: LGTM!

The room transfer event handling properly validates required entities and throws descriptive errors. The type assertion on line 252 is justified by the comment about incorrect type definitions in apps-engine.


258-285: LGTM!

The guest and room saved event handlers properly validate entities and throw descriptive errors when not found.


287-311: LGTM!

The department event handlers properly validate entities and provide appropriate error handling.

sampaiodiego
sampaiodiego previously approved these changes Jan 12, 2026
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: 4

🤖 Fix all issues with AI agents
In @apps/meteor/app/apps/server/bridges/listeners.ts:
- Around line 401-421: The error strings interpolate the departmentData object
directly (in the IPostLivechatDepartmentDisabled and
IPostLivechatDepartmentRemoved cases), causing “[object Object]” output; update
the thrown errors in these branches (the lines that throw new Error(`Department
${departmentData} not found`)) to include a concrete identifier such as
departmentData._id or departmentData.id and/or departmentData.name (e.g.,
`Department ${departmentData._id} not found`) or use
JSON.stringify(departmentData) if you need full context, so the message is
human-readable and debuggable.
- Around line 427-433: The error messages use roomData (an IRoom object) which
stringifies to [object Object]; update the throw statements in this block so
they reference roomData._id (or another identifying field like roomData.rid)
instead of roomData, e.g., use `Room ${roomData._id} not found` and `Room
${roomData._id} is not a livechat room` while keeping the existing checks around
isLivechatRoom(room).
- Around line 300-327: The switch in the anonymous async block improperly
declares consts directly in case clauses (Biome lint error) and the default
branch bypasses executeListener so room create/delete events never run; fix by
wrapping each case body that declares locals in braces (e.g., case
AppInterface.IPreRoomUserJoined: { const [, joiningUser, invitingUser] =
args.payload; ... } ) and replace the default return rm with a call to
this.orch.getManager().getListenerManager().executeListener(args.event, { room:
rm, ...(payload-dependent fields) }) or explicitly add cases for room
create/delete that call executeListener so all events (including
IPreRoomCreatePrevent, IPostRoomCreate, etc.) invoke the listener.
- Around line 220-293: The switch cases use unscoped const bindings and the
default branch bypasses listeners; wrap each case body in braces to give the
const declarations block scope (e.g., the
AppInterface.IPostMessageDeleted/IPostMessageReacted/... cases where you declare
userDeleted/userReacted/etc.), and change the default branch to invoke
this.orch.getManager().getListenerManager().executeListener(args.event, {
message: msg }) (or pass converted payload as needed) instead of returning msg
so events not explicitly listed still run their listeners; keep the post-execute
handling (checking for boolean/undefined and converting via
this.orch.getConverters().get('messages').convertAppMessage) unchanged.
🧹 Nitpick comments (3)
apps/meteor/app/apps/server/bridges/listeners.ts (3)

72-94: Type overlap in HandleRoomEvent union may cause confusion.

The events IPreRoomUserJoined, IPostRoomUserJoined, IPreRoomUserLeave, and IPostRoomUserLeave appear in both union branches (lines 74, 78 with [IRoom, IUser, IUser] payload, and lines 89-92 with [IRoom] payload). This overlap means the discriminated union won't narrow properly for these events—TypeScript cannot infer which payload shape applies.

If the intent is to support both payload shapes for these events, consider making this explicit with separate type guards. Otherwise, remove the duplicates from lines 89-92.

♻️ Suggested fix: remove duplicates from the second branch
 type HandleRoomEvent =
 	| {
 			event: AppInterface.IPreRoomUserJoined | AppInterface.IPostRoomUserJoined;
 			payload: [IRoom, IUser, IUser];
 	  }
 	| {
 			event: AppInterface.IPreRoomUserLeave | AppInterface.IPostRoomUserLeave;
 			payload: [IRoom, IUser, IUser];
 	  }
 	| {
 			event:
 				| AppInterface.IPreRoomCreatePrevent
 				| AppInterface.IPreRoomCreateExtend
 				| AppInterface.IPreRoomCreateModify
 				| AppInterface.IPostRoomCreate
 				| AppInterface.IPreRoomDeletePrevent
-				| AppInterface.IPostRoomDeleted
-				| AppInterface.IPreRoomUserJoined
-				| AppInterface.IPostRoomUserJoined
-				| AppInterface.IPreRoomUserLeave
-				| AppInterface.IPostRoomUserLeave;
+				| AppInterface.IPostRoomDeleted;
 			payload: [IRoom];
 	  };

336-348: Add block scope around the switch case declaration.

Line 340 declares const [agentData] without block scope (Biome error). Wrap in braces for proper scoping.

♻️ Suggested fix
 		switch (args.event) {
 			case AppInterface.IPostLivechatAgentAssigned:
-			case AppInterface.IPostLivechatAgentUnassigned:
-				const [agentData] = args.payload;
+			case AppInterface.IPostLivechatAgentUnassigned: {
+				const [agentData] = args.payload;
 				return this.orch
 					.getManager()
 					.getListenerManager()
 					.executeListener(args.event, {
 						room: (await this.orch.getConverters().get('rooms').convertRoom(agentData.room)) as IAppsLivechatRoom,
 						agent: this.orch.getConverters().get('users').convertToApp(agentData.user),
 					});
+			}

423-436: Add block scope around the default case declarations.

Lines 424-425 declare const variables without block scope (Biome error).

♻️ Suggested fix
-			default:
-				const [roomData] = args.payload;
-				const room = await this.orch.getConverters().get('rooms').convertRoom(roomData);
+			default: {
+				const [roomData] = args.payload;
+				const room = await this.orch.getConverters().get('rooms').convertRoom(roomData);
 
 				if (!room) {
 					throw new Error(`Room ${roomData} is not a livechat room`);
 				}
 
 				if (!isLivechatRoom(room)) {
 					throw new Error(`Room ${roomData} is not a livechat room`);
 				}
 
 				return this.orch.getManager().getListenerManager().executeListener(args.event, room);
+			}
📜 Review details

Configuration used: Organization 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 6a04195 and 716e687.

📒 Files selected for processing (2)
  • apps/meteor/app/apps/server/bridges/listeners.ts
  • apps/meteor/ee/server/apps/orchestrator.js
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/ee/server/apps/orchestrator.js
  • apps/meteor/app/apps/server/bridges/listeners.ts
🧠 Learnings (4)
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: AppUserBridge.getUserRoomIds in apps/meteor/app/apps/server/bridges/users.ts always returns an array of strings (mapping subscription documents to room IDs), never undefined, even when user has no room subscriptions.

Applied to files:

  • apps/meteor/app/apps/server/bridges/listeners.ts
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: AppUserBridge.getUserRoomIds in apps/meteor/app/apps/server/bridges/users.ts always returns an array of strings by mapping subscription documents to room IDs, never undefined, even when user has no room subscriptions.

Applied to files:

  • apps/meteor/app/apps/server/bridges/listeners.ts
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: UserBridge.doGetUserRoomIds in packages/apps-engine/src/server/bridges/UserBridge.ts has a bug where it implicitly returns undefined when the app lacks read permission (missing return statement in the else case of the permission check).

Applied to files:

  • apps/meteor/app/apps/server/bridges/listeners.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Ensure tests run reliably in parallel without shared state conflicts

Applied to files:

  • apps/meteor/app/apps/server/bridges/listeners.ts
🧬 Code graph analysis (1)
apps/meteor/app/apps/server/bridges/listeners.ts (2)
packages/apps/src/AppsEngine.ts (1)
  • AppInterface (13-13)
packages/apps-engine/src/definition/rooms/index.ts (6)
  • IPreRoomCreatePrevent (19-19)
  • IPreRoomCreateExtend (17-17)
  • IPreRoomCreateModify (18-18)
  • IPostRoomCreate (15-15)
  • IPreRoomDeletePrevent (20-20)
  • IPostRoomDeleted (16-16)
🪛 Biome (2.1.2)
apps/meteor/app/apps/server/bridges/listeners.ts

[error] 223-223: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 232-232: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 243-243: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 253-253: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 263-263: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 273-273: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 304-304: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 315-315: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 340-340: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 424-424: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 425-425: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

⏰ 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). (2)
  • GitHub Check: cubic · AI code reviewer
  • GitHub Check: CodeQL-Build
🔇 Additional comments (4)
apps/meteor/ee/server/apps/orchestrator.js (1)

340-355: Interface change from spread arguments to structured object is correctly implemented.

The call to handleEvent now passes a single object { event, payload } instead of spreading event, ...payload as separate arguments. This matches the new AppListenerBridge.handleEvent signature which expects a HandleEvent parameter—a union type where each variant has both event and payload properties. The change aligns with the PR's goal of introducing structured payloads and is properly typed.

apps/meteor/app/apps/server/bridges/listeners.ts (3)

1-31: LGTM!

Type definitions are well-structured with clear field types. The internal type aliases appropriately capture the event data structures needed for the bridge.


146-206: LGTM!

The event routing in handleEvent is well-organized with clear categorization of events to their respective handlers. The deprecation comment for ILivechatRoomClosedHandler is a good practice.


439-479: LGTM!

The userEvent method correctly uses block scoping for all cases and properly constructs the context objects. The handling of optional performedBy and previousUser fields aligns with the type definitions.

@d-gubert d-gubert force-pushed the chore/convert-app-listeners-ts branch from 716e687 to 3b4a84c Compare January 13, 2026 17:47
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: 5

🤖 Fix all issues with AI agents
In @apps/meteor/app/apps/server/bridges/listeners.ts:
- Around line 401-421: The error messages in the IPostLivechatDepartmentDisabled
and IPostLivechatDepartmentRemoved cases interpolate the departmentData object
directly, producing "[object Object]"; change the throw to include a meaningful
identifier (e.g., departmentData._id or departmentData.id or
departmentData.name) or fallback to JSON.stringify(departmentData) so the
message is informative; update the throw lines in the two cases (within
listeners.ts where convertDepartment is called) to something like: throw new
Error(`Department ${departmentData._id || departmentData.id ||
departmentData.name || JSON.stringify(departmentData)} not found`).
- Around line 220-285: The switch on args.event (cases like
AppInterface.IPostMessageDeleted, IPostMessageReacted, IPostMessageFollowed,
IPostMessagePinned, IPostMessageStarred, IPostMessageReported) declares consts
(userDeleted, userReacted, userFollowed, userPinned, userStarred, userReported,
etc.) that can leak across cases; wrap each case body in its own block ({ ... })
so those const declarations are block-scoped and cannot be accessed by other
cases, keeping the existing return/executeListener logic unchanged.
- Around line 300-327: The switch cases in the anonymous async IIFE that assigns
result declare consts (e.g., "const [, joiningUser, invitingUser]" and "const [,
leavingUser, removedBy]") directly in case clauses which creates scope issues;
fix by wrapping each case body in braces so those const declarations are
block-scoped (add `{ ... }` around the code for the
IPreRoomUserJoined/IPostRoomUserJoined block and likewise for the
IPreRoomUserLeave/IPostRoomUserLeave block), keeping the existing calls to
this.orch.getManager().getListenerManager().executeListener(...) unchanged.
- Around line 336-348: The switch case for
AppInterface.IPostLivechatAgentAssigned /
AppInterface.IPostLivechatAgentUnassigned in the livechatEvent method declares a
const (const [agentData]) without a block scope; wrap the case body in braces to
create a block scope around that declaration and the subsequent return so the
const is properly scoped (i.e., add { ... } around the code that currently
follows those two case labels inside livechatEvent).
- Around line 423-436: In the switch default case in listeners.ts, wrap the
const declarations for roomData and room in a block (e.g., { ... }) to provide
proper block scoping; then change the thrown Error messages to include a unique
identifier from roomData (such as roomData._id or roomData.id) instead of
interpolating the whole object, and update the checks around convertRoom
(getConverters().get('rooms').convertRoom) and isLivechatRoom to use those
block-scoped variables before calling
getListenerManager().executeListener(args.event, room).
📜 Review details

Configuration used: Organization 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 716e687 and 3b4a84c.

📒 Files selected for processing (14)
  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/app/apps/server/bridges/listeners.js
  • apps/meteor/app/apps/server/bridges/listeners.ts
  • apps/meteor/app/lib/server/functions/deleteMessage.ts
  • apps/meteor/app/lib/server/functions/sendMessage.ts
  • apps/meteor/app/lib/server/functions/updateMessage.ts
  • apps/meteor/app/livechat/server/lib/RoutingManager.ts
  • apps/meteor/app/livechat/server/lib/closeRoom.ts
  • apps/meteor/app/livechat/server/lib/departmentsLib.ts
  • apps/meteor/ee/server/apps/communication/uikit.ts
  • apps/meteor/ee/server/apps/orchestrator.js
  • apps/meteor/server/lib/eraseRoom.ts
  • apps/meteor/server/services/messages/service.ts
💤 Files with no reviewable changes (1)
  • apps/meteor/app/apps/server/bridges/listeners.js
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/meteor/app/livechat/server/lib/departmentsLib.ts
  • apps/meteor/ee/server/apps/orchestrator.js
  • apps/meteor/app/livechat/server/lib/RoutingManager.ts
  • apps/meteor/app/lib/server/functions/updateMessage.ts
  • apps/meteor/app/lib/server/functions/deleteMessage.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/server/services/messages/service.ts
  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/server/lib/eraseRoom.ts
  • apps/meteor/app/lib/server/functions/sendMessage.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
  • apps/meteor/ee/server/apps/communication/uikit.ts
  • apps/meteor/app/livechat/server/lib/closeRoom.ts
**/*.spec.ts

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.spec.ts: Use descriptive test names that clearly communicate expected behavior in Playwright tests
Use .spec.ts extension for test files (e.g., login.spec.ts)

Files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
🧠 Learnings (21)
📚 Learning: 2025-11-19T18:20:37.116Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37419
File: apps/meteor/server/services/media-call/service.ts:141-141
Timestamp: 2025-11-19T18:20:37.116Z
Learning: In apps/meteor/server/services/media-call/service.ts, the sendHistoryMessage method should use call.caller.id or call.createdBy?.id as the message author, not call.transferredBy?.id. Even for transferred calls, the message should appear in the DM between the two users who are calling each other, not sent by the person who transferred the call.

Applied to files:

  • apps/meteor/server/services/messages/service.ts
  • apps/meteor/app/lib/server/functions/sendMessage.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
📚 Learning: 2025-09-19T15:15:04.642Z
Learnt from: rodrigok
Repo: RocketChat/Rocket.Chat PR: 36991
File: apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts:219-221
Timestamp: 2025-09-19T15:15:04.642Z
Learning: The Federation_Matrix_homeserver_domain setting in apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts is part of the old federation system and is being deprecated/removed, so configuration issues with this setting should not be flagged for improvement.

Applied to files:

  • apps/meteor/server/services/messages/service.ts
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: AppUserBridge.getUserRoomIds in apps/meteor/app/apps/server/bridges/users.ts always returns an array of strings (mapping subscription documents to room IDs), never undefined, even when user has no room subscriptions.

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/server/lib/eraseRoom.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
  • apps/meteor/app/livechat/server/lib/closeRoom.ts
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: AppUserBridge.getUserRoomIds in apps/meteor/app/apps/server/bridges/users.ts always returns an array of strings by mapping subscription documents to room IDs, never undefined, even when user has no room subscriptions.

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/server/lib/eraseRoom.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
  • apps/meteor/app/livechat/server/lib/closeRoom.ts
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: UserBridge.doGetUserRoomIds in packages/apps-engine/src/server/bridges/UserBridge.ts has a bug where it implicitly returns undefined when the app lacks read permission (missing return statement in the else case of the permission check).

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/server/lib/eraseRoom.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
  • apps/meteor/app/livechat/server/lib/closeRoom.ts
📚 Learning: 2025-09-30T13:00:05.465Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 36990
File: apps/meteor/ee/server/apps/storage/AppRealStorage.ts:55-58
Timestamp: 2025-09-30T13:00:05.465Z
Learning: In AppRealStorage (apps/meteor/ee/server/apps/storage/AppRealStorage.ts), the `remove` method is designed to be idempotent and returns `{ success: true }` unconditionally because the goal is to ensure the app is removed, not to distinguish whether this specific call performed the deletion. Database errors will throw exceptions.

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/server/lib/eraseRoom.ts
📚 Learning: 2025-11-27T17:56:26.050Z
Learnt from: MartinSchoeler
Repo: RocketChat/Rocket.Chat PR: 37557
File: apps/meteor/client/views/admin/ABAC/AdminABACRooms.tsx:115-116
Timestamp: 2025-11-27T17:56:26.050Z
Learning: In Rocket.Chat, the GET /v1/abac/rooms endpoint (implemented in ee/packages/abac/src/index.ts) only returns rooms where abacAttributes exists and is not an empty array (query: { abacAttributes: { $exists: true, $ne: [] } }). Therefore, in components consuming this endpoint (like AdminABACRooms.tsx), room.abacAttributes is guaranteed to be defined for all returned rooms, and optional chaining before calling array methods like .join() is sufficient without additional null coalescing.

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
📚 Learning: 2025-11-04T16:49:19.107Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37377
File: apps/meteor/ee/server/hooks/federation/index.ts:86-88
Timestamp: 2025-11-04T16:49:19.107Z
Learning: In Rocket.Chat's federation system (apps/meteor/ee/server/hooks/federation/), permission checks follow two distinct patterns: (1) User-initiated federation actions (creating rooms, adding users to federated rooms, joining from invites) should throw MeteorError to inform users they lack 'access-federation' permission. (2) Remote server-initiated federation events should silently skip/ignore when users lack permission. The beforeAddUserToRoom hook only executes for local user-initiated actions, so throwing an error there is correct. Remote federation events are handled separately by the federation Matrix package with silent skipping logic.

Applied to files:

  • apps/meteor/server/lib/eraseRoom.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
📚 Learning: 2025-12-09T20:01:07.355Z
Learnt from: sampaiodiego
Repo: RocketChat/Rocket.Chat PR: 37532
File: ee/packages/federation-matrix/src/FederationMatrix.ts:920-927
Timestamp: 2025-12-09T20:01:07.355Z
Learning: In Rocket.Chat's federation invite handling (ee/packages/federation-matrix/src/FederationMatrix.ts), when a user rejects an invite via federationSDK.rejectInvite(), the subscription cleanup happens automatically through an event-driven flow: Matrix emits a leave event back, which is processed by handleLeave() in ee/packages/federation-matrix/src/events/member.ts, and that function calls Room.performUserRemoval() to clean up the subscription. No explicit cleanup is needed in the reject branch of handleInvite() because the leave event handler takes care of it.
<!-- </add_learning>

Applied to files:

  • apps/meteor/server/lib/eraseRoom.ts
📚 Learning: 2025-10-28T16:53:42.761Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37205
File: ee/packages/federation-matrix/src/FederationMatrix.ts:296-301
Timestamp: 2025-10-28T16:53:42.761Z
Learning: In the Rocket.Chat federation-matrix integration (ee/packages/federation-matrix/), the createRoom method from rocket.chat/federation-sdk will support a 4-argument signature (userId, roomName, visibility, displayName) in newer versions. Code using this 4-argument call is forward-compatible with planned library updates and should not be flagged as an error.

Applied to files:

  • apps/meteor/app/lib/server/functions/sendMessage.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
  • apps/meteor/app/livechat/server/lib/closeRoom.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Ensure tests run reliably in parallel without shared state conflicts

Applied to files:

  • apps/meteor/app/apps/server/bridges/listeners.ts
  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-09-16T13:33:49.237Z
Learnt from: cardoso
Repo: RocketChat/Rocket.Chat PR: 36890
File: apps/meteor/tests/e2e/e2e-encryption/e2ee-otr.spec.ts:21-26
Timestamp: 2025-09-16T13:33:49.237Z
Learning: The im.delete API endpoint accepts either a `roomId` parameter (requiring the actual DM room _id) or a `username` parameter (for the DM partner's username). Constructing slug-like identifiers like `user2${Users.userE2EE.data.username}` doesn't work for this endpoint.

Applied to files:

  • apps/meteor/app/apps/server/bridges/listeners.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Utilize Playwright fixtures (`test`, `page`, `expect`) for consistency in test files

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Use `expect` matchers for assertions (`toEqual`, `toContain`, `toBeTruthy`, `toHaveLength`, etc.) instead of `assert` statements in Playwright tests

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Ensure clean state for each test execution in Playwright tests

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : All test files must be created in `apps/meteor/tests/e2e/` directory

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Group related tests in the same file

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-10-06T20:30:45.540Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 37152
File: packages/apps-engine/tests/test-data/storage/storage.ts:101-122
Timestamp: 2025-10-06T20:30:45.540Z
Learning: In `packages/apps-engine/tests/test-data/storage/storage.ts`, the stub methods (updatePartialAndReturnDocument, updateStatus, updateSetting, updateAppInfo, updateMarketplaceInfo) intentionally throw "Method not implemented." Tests using these methods must stub them using `SpyOn` from the test library rather than relying on actual implementations.

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/page-objects/**/*.ts : Utilize existing page objects pattern from `apps/meteor/tests/e2e/page-objects/`

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Use `test.step()` for complex test scenarios to improve organization in Playwright tests

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Use `test.beforeAll()` and `test.afterAll()` for setup/teardown in Playwright tests

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
🧬 Code graph analysis (1)
apps/meteor/app/lib/server/functions/sendMessage.ts (2)
packages/core-services/src/index.ts (1)
  • Apps (152-152)
apps/meteor/app/apps/server/bridges/listeners.ts (1)
  • messageEvent (215-293)
🪛 Biome (2.1.2)
apps/meteor/app/apps/server/bridges/listeners.ts

[error] 223-223: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 232-232: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 243-243: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 253-253: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 263-263: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 273-273: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 304-304: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 315-315: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 340-340: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 424-424: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 425-425: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

⏰ 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). (3)
  • GitHub Check: 📦 Build Packages
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build
🔇 Additional comments (21)
apps/meteor/server/services/messages/service.ts (2)

1-1: LGTM!

Import correctly brings in AppEvents enum for type-safe event dispatch.


204-206: LGTM!

Clean migration from bridge-based listener call to the unified Apps.self?.triggerEvent pattern with typed AppEvents enum. The guard check and fire-and-forget pattern are appropriate here.

apps/meteor/server/lib/eraseRoom.ts (2)

43-48: LGTM!

The migration from Apps.getBridges()?.getListenerBridge().roomEvent() to Apps.self?.triggerEvent() is correctly implemented. The isLoaded() guard ensures the Apps engine is ready before triggering events.


59-61: LGTM!

The post-delete event is correctly fired as fire-and-forget using void. This maintains the original behavior where the deletion completes regardless of whether apps process the event.

apps/meteor/ee/server/apps/communication/uikit.ts (5)

1-1: LGTM!

The import correctly brings in AppEvents for enum-based event references and IAppServerOrchestrator for interface-based typing.


196-199: LGTM!

Using the IAppServerOrchestrator interface type instead of the concrete class improves flexibility and follows dependency inversion principles.


216-217: LGTM!

The conditional conversion pattern (mid ? await ... : undefined) is cleaner and explicitly handles the case when IDs are not present, avoiding potential undefined access issues.


233-235: LGTM!

Using AppEvents.IUIKitInteractionHandler and AppEvents.IUIKitLivechatInteractionHandler enum values instead of string literals improves type safety and enables compile-time checking.


311-312: LGTM!

Consistent application of the conditional conversion pattern for room and message in the actionButton case.

apps/meteor/app/lib/server/functions/sendMessage.ts (2)

233-251: LGTM!

The migration to Apps.self?.triggerEvent for pre-message events is well implemented:

  • Prevention check correctly returns early when apps prevent sending
  • The extend → modify chain preserves the original event flow
  • Re-validation after app modifications is a good defensive practice

282-286: LGTM!

The post-send event dispatch correctly differentiates between system messages (IPostSystemMessageSent) and regular messages (IPostMessageSent) using the message.t property. The fire-and-forget pattern using void is appropriate here.

apps/meteor/app/api/server/lib/eraseTeam.ts (2)

77-82: LGTM!

The pre-delete prevention check is correctly implemented using Apps.self?.triggerEvent. The return of false (rather than throwing) aligns with the "loose validation" semantics of this function.


91-93: LGTM!

Post-delete event dispatch is consistent with the pattern in eraseRoom.ts, using fire-and-forget semantics.

apps/meteor/app/api/server/lib/eraseTeam.spec.ts (3)

43-46: LGTM!

The default Apps stub correctly models the new interface with Apps.self.isLoaded() returning false and a stubbed triggerEvent. This ensures tests run without app interference by default.


190-219: LGTM!

The test correctly verifies that when triggerEvent returns true for IPreRoomDeletePrevent, the deletion is prevented and false is returned. The stub structure properly reflects the new Apps.self.triggerEvent API.


242-272: LGTM!

The test properly verifies the success path:

  • First triggerEvent call (pre-delete) returns false (no prevention)
  • Second triggerEvent call (post-delete) fires
  • roomEventStub.calledTwice assertion confirms both events were triggered
apps/meteor/app/livechat/server/lib/closeRoom.ts (1)

93-100: LGTM!

The migration from the bridge-based livechatEvent dispatch to the unified Apps.self?.triggerEvent API is clean and consistent with the broader refactoring pattern across the codebase. The deprecation comment is properly retained, and the void keyword appropriately handles the floating promises.

apps/meteor/app/apps/server/bridges/listeners.ts (4)

1-144: LGTM!

The type definitions are well-structured using discriminated union types that provide type safety for event handling. The HandleEvent union and its constituent types clearly define the expected payload shapes for each event category.


146-206: LGTM!

The handleEvent method correctly routes events to their specialized handlers. The complexity exemption is justified given the exhaustive switch statement for event routing.


208-213: LGTM!

The defaultEvent method serves as an appropriate fallback for events that don't require specialized handling.


439-479: LGTM!

The userEvent method properly uses block scoping for all switch cases. The event handling for login/logout, status changes, and CRUD operations is well-structured with appropriate context construction.

@ggazzo ggazzo force-pushed the chore/convert-app-listeners-ts branch from 3b4a84c to 4915155 Compare January 13, 2026 19:20
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: 2

🤖 Fix all issues with AI agents
In @apps/meteor/app/apps/server/bridges/listeners.ts:
- Around line 72-94: The discriminated union HandleRoomEvent currently maps the
same event discriminants (IPreRoomUserJoined, IPostRoomUserJoined,
IPreRoomUserLeave, IPostRoomUserLeave) to two different payload shapes which
breaks type narrowing; update the union so each event appears only once with its
correct payload by removing those four duplicate event types from the third
union member (the member with payload: [IRoom]) and keep them only in the
appropriate union member with payload: [IRoom, IUser, IUser], ensuring each
event name in HandleRoomEvent uniquely corresponds to one payload shape.
- Around line 149-151: Remove the debug console.log in the async
handleEvent(args: HandleEvent): Promise<any> method; delete the line
"console.log('args', args)" (or replace it with a proper server logger call if
intentional) so that production server logs are not polluted, ensuring any
required event details are logged via the existing logging mechanism instead of
console.log.
🧹 Nitpick comments (4)
apps/meteor/app/apps/server/bridges/listeners.ts (4)

221-286: Wrap switch case declarations in blocks to prevent scope leakage.

Variables declared with const inside switch cases without blocks can be accessed from other cases, leading to potential bugs. Biome flags this as an error.

♻️ Suggested fix - wrap each case in a block
 		const result = await (() => {
 			switch (args.event) {
-				case AppInterface.IPostMessageDeleted:
-					const [, userDeleted] = args.payload;
+				case AppInterface.IPostMessageDeleted: {
+					const [, userDeleted] = args.payload;
 					return this.orch
 						.getManager()
 						.getListenerManager()
 						.executeListener(args.event, {
 							message: msg,
 							user: this.orch.getConverters().get('users').convertToApp(userDeleted),
 						});
-				case AppInterface.IPostMessageReacted:
-					const [, userReacted, reaction, isReacted] = args.payload;
+				}
+				case AppInterface.IPostMessageReacted: {
+					const [, userReacted, reaction, isReacted] = args.payload;
 					return this.orch
 						.getManager()
 						.getListenerManager()
 						.executeListener(args.event, {
 							message: msg,
 							user: this.orch.getConverters().get('users').convertToApp(userReacted),
 							reaction,
 							isReacted,
 						});
+				}
 				// ... similar wrapping needed for remaining cases

340-351: Wrap switch case declarations in blocks for livechatEvent.

The const agentData declaration on line 344 and declarations in the default case (lines 428-429) need block scoping to prevent scope leakage issues flagged by static analysis.

♻️ Suggested fix for agent assigned/unassigned case
 		switch (args.event) {
-			case AppInterface.IPostLivechatAgentAssigned:
-			case AppInterface.IPostLivechatAgentUnassigned:
-				const [agentData] = args.payload;
+			case AppInterface.IPostLivechatAgentAssigned:
+			case AppInterface.IPostLivechatAgentUnassigned: {
+				const [agentData] = args.payload;
 				return this.orch
 					.getManager()
 					.getListenerManager()
 					.executeListener(args.event, {
 						room: (await this.orch.getConverters().get('rooms').convertRoom(agentData.room)) as IAppsLivechatRoom,
 						agent: this.orch.getConverters().get('users').convertToApp(agentData.user),
 					});
+			}

427-440: Default case needs block scoping for its declarations.

The roomData and room declarations in the default case need to be wrapped in a block.

♻️ Suggested fix
-			default:
-				const [roomData] = args.payload;
-				const room = await this.orch.getConverters().get('rooms').convertRoom(roomData);
+			default: {
+				const [roomData] = args.payload;
+				const room = await this.orch.getConverters().get('rooms').convertRoom(roomData);
 
 				if (!room) {
 					throw new Error(`Room ${roomData} not found`);
 				}
 
 				if (!isLivechatRoom(room)) {
 					throw new Error(`Room ${roomData} is not a livechat room`);
 				}
 
 				return this.orch.getManager().getListenerManager().executeListener(args.event, room);
+			}

209-214: Consider adding a TODO or improving type safety for defaultEvent.

The as any cast on line 213 bypasses type checking. While this may be necessary for the fallback handler, it could hide type mismatches for events that should have specific handling.

📜 Review details

Configuration used: Organization 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 3b4a84c and 4915155.

📒 Files selected for processing (15)
  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/app/apps/server/bridges/listeners.js
  • apps/meteor/app/apps/server/bridges/listeners.ts
  • apps/meteor/app/lib/server/functions/deleteMessage.ts
  • apps/meteor/app/lib/server/functions/sendMessage.ts
  • apps/meteor/app/lib/server/functions/updateMessage.ts
  • apps/meteor/app/livechat/server/lib/RoutingManager.ts
  • apps/meteor/app/livechat/server/lib/closeRoom.ts
  • apps/meteor/app/livechat/server/lib/departmentsLib.ts
  • apps/meteor/ee/server/apps/communication/uikit.ts
  • apps/meteor/ee/server/apps/orchestrator.js
  • apps/meteor/server/lib/eraseRoom.ts
  • apps/meteor/server/services/messages/service.ts
  • packages/apps-engine/src/server/managers/AppListenerManager.ts
💤 Files with no reviewable changes (1)
  • apps/meteor/app/apps/server/bridges/listeners.js
🚧 Files skipped from review as they are similar to previous changes (8)
  • apps/meteor/app/lib/server/functions/deleteMessage.ts
  • apps/meteor/ee/server/apps/orchestrator.js
  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
  • apps/meteor/app/livechat/server/lib/departmentsLib.ts
  • packages/apps-engine/src/server/managers/AppListenerManager.ts
  • apps/meteor/app/livechat/server/lib/RoutingManager.ts
  • apps/meteor/app/livechat/server/lib/closeRoom.ts
  • apps/meteor/app/lib/server/functions/sendMessage.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
  • apps/meteor/server/lib/eraseRoom.ts
  • apps/meteor/ee/server/apps/communication/uikit.ts
  • apps/meteor/server/services/messages/service.ts
  • apps/meteor/app/lib/server/functions/updateMessage.ts
🧠 Learnings (12)
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: UserBridge.doGetUserRoomIds in packages/apps-engine/src/server/bridges/UserBridge.ts has a bug where it implicitly returns undefined when the app lacks read permission (missing return statement in the else case of the permission check).

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
  • apps/meteor/server/lib/eraseRoom.ts
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: AppUserBridge.getUserRoomIds in apps/meteor/app/apps/server/bridges/users.ts always returns an array of strings (mapping subscription documents to room IDs), never undefined, even when user has no room subscriptions.

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
  • apps/meteor/server/lib/eraseRoom.ts
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: AppUserBridge.getUserRoomIds in apps/meteor/app/apps/server/bridges/users.ts always returns an array of strings by mapping subscription documents to room IDs, never undefined, even when user has no room subscriptions.

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
  • apps/meteor/server/lib/eraseRoom.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Ensure tests run reliably in parallel without shared state conflicts

Applied to files:

  • apps/meteor/app/apps/server/bridges/listeners.ts
📚 Learning: 2025-10-28T16:53:42.761Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37205
File: ee/packages/federation-matrix/src/FederationMatrix.ts:296-301
Timestamp: 2025-10-28T16:53:42.761Z
Learning: In the Rocket.Chat federation-matrix integration (ee/packages/federation-matrix/), the createRoom method from rocket.chat/federation-sdk will support a 4-argument signature (userId, roomName, visibility, displayName) in newer versions. Code using this 4-argument call is forward-compatible with planned library updates and should not be flagged as an error.

Applied to files:

  • apps/meteor/app/apps/server/bridges/listeners.ts
📚 Learning: 2025-11-27T17:56:26.050Z
Learnt from: MartinSchoeler
Repo: RocketChat/Rocket.Chat PR: 37557
File: apps/meteor/client/views/admin/ABAC/AdminABACRooms.tsx:115-116
Timestamp: 2025-11-27T17:56:26.050Z
Learning: In Rocket.Chat, the GET /v1/abac/rooms endpoint (implemented in ee/packages/abac/src/index.ts) only returns rooms where abacAttributes exists and is not an empty array (query: { abacAttributes: { $exists: true, $ne: [] } }). Therefore, in components consuming this endpoint (like AdminABACRooms.tsx), room.abacAttributes is guaranteed to be defined for all returned rooms, and optional chaining before calling array methods like .join() is sufficient without additional null coalescing.

Applied to files:

  • apps/meteor/app/apps/server/bridges/listeners.ts
📚 Learning: 2025-11-04T16:49:19.107Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37377
File: apps/meteor/ee/server/hooks/federation/index.ts:86-88
Timestamp: 2025-11-04T16:49:19.107Z
Learning: In Rocket.Chat's federation system (apps/meteor/ee/server/hooks/federation/), permission checks follow two distinct patterns: (1) User-initiated federation actions (creating rooms, adding users to federated rooms, joining from invites) should throw MeteorError to inform users they lack 'access-federation' permission. (2) Remote server-initiated federation events should silently skip/ignore when users lack permission. The beforeAddUserToRoom hook only executes for local user-initiated actions, so throwing an error there is correct. Remote federation events are handled separately by the federation Matrix package with silent skipping logic.

Applied to files:

  • apps/meteor/app/apps/server/bridges/listeners.ts
  • apps/meteor/server/lib/eraseRoom.ts
📚 Learning: 2025-09-16T13:33:49.237Z
Learnt from: cardoso
Repo: RocketChat/Rocket.Chat PR: 36890
File: apps/meteor/tests/e2e/e2e-encryption/e2ee-otr.spec.ts:21-26
Timestamp: 2025-09-16T13:33:49.237Z
Learning: The im.delete API endpoint accepts either a `roomId` parameter (requiring the actual DM room _id) or a `username` parameter (for the DM partner's username). Constructing slug-like identifiers like `user2${Users.userE2EE.data.username}` doesn't work for this endpoint.

Applied to files:

  • apps/meteor/app/apps/server/bridges/listeners.ts
📚 Learning: 2025-09-30T13:00:05.465Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 36990
File: apps/meteor/ee/server/apps/storage/AppRealStorage.ts:55-58
Timestamp: 2025-09-30T13:00:05.465Z
Learning: In AppRealStorage (apps/meteor/ee/server/apps/storage/AppRealStorage.ts), the `remove` method is designed to be idempotent and returns `{ success: true }` unconditionally because the goal is to ensure the app is removed, not to distinguish whether this specific call performed the deletion. Database errors will throw exceptions.

Applied to files:

  • apps/meteor/server/lib/eraseRoom.ts
📚 Learning: 2025-12-09T20:01:07.355Z
Learnt from: sampaiodiego
Repo: RocketChat/Rocket.Chat PR: 37532
File: ee/packages/federation-matrix/src/FederationMatrix.ts:920-927
Timestamp: 2025-12-09T20:01:07.355Z
Learning: In Rocket.Chat's federation invite handling (ee/packages/federation-matrix/src/FederationMatrix.ts), when a user rejects an invite via federationSDK.rejectInvite(), the subscription cleanup happens automatically through an event-driven flow: Matrix emits a leave event back, which is processed by handleLeave() in ee/packages/federation-matrix/src/events/member.ts, and that function calls Room.performUserRemoval() to clean up the subscription. No explicit cleanup is needed in the reject branch of handleInvite() because the leave event handler takes care of it.
<!-- </add_learning>

Applied to files:

  • apps/meteor/server/lib/eraseRoom.ts
📚 Learning: 2025-11-19T18:20:37.116Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37419
File: apps/meteor/server/services/media-call/service.ts:141-141
Timestamp: 2025-11-19T18:20:37.116Z
Learning: In apps/meteor/server/services/media-call/service.ts, the sendHistoryMessage method should use call.caller.id or call.createdBy?.id as the message author, not call.transferredBy?.id. Even for transferred calls, the message should appear in the DM between the two users who are calling each other, not sent by the person who transferred the call.

Applied to files:

  • apps/meteor/server/services/messages/service.ts
📚 Learning: 2025-09-19T15:15:04.642Z
Learnt from: rodrigok
Repo: RocketChat/Rocket.Chat PR: 36991
File: apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts:219-221
Timestamp: 2025-09-19T15:15:04.642Z
Learning: The Federation_Matrix_homeserver_domain setting in apps/meteor/server/services/federation/infrastructure/rocket-chat/adapters/Settings.ts is part of the old federation system and is being deprecated/removed, so configuration issues with this setting should not be flagged for improvement.

Applied to files:

  • apps/meteor/server/services/messages/service.ts
🧬 Code graph analysis (1)
apps/meteor/app/apps/server/bridges/listeners.ts (3)
packages/apps/src/AppsEngine.ts (1)
  • AppInterface (13-13)
packages/apps-engine/src/definition/rooms/index.ts (6)
  • IPreRoomCreatePrevent (19-19)
  • IPreRoomCreateExtend (17-17)
  • IPreRoomCreateModify (18-18)
  • IPostRoomCreate (15-15)
  • IPreRoomDeletePrevent (20-20)
  • IPostRoomDeleted (16-16)
apps/meteor/client/lib/rooms/roomCoordinator.tsx (1)
  • isLivechatRoom (98-100)
🪛 Biome (2.1.2)
apps/meteor/app/apps/server/bridges/listeners.ts

[error] 224-224: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 233-233: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 244-244: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 254-254: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 264-264: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 274-274: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 308-308: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 319-319: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 344-344: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 428-428: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 429-429: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

⏰ 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). (3)
  • GitHub Check: 📦 Build Packages
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build
🔇 Additional comments (13)
apps/meteor/app/api/server/lib/eraseTeam.ts (1)

77-93: LGTM! Event dispatch migration looks correct.

The migration from bridge-based calls to Apps.self?.triggerEvent preserves the existing behavior:

  • Pre-delete prevention returns early if an app blocks the deletion
  • Post-delete notification is correctly fire-and-forget with void

Minor observation: The optional chaining on Apps.self?.triggerEvent (lines 78, 92) is redundant after the Apps.self?.isLoaded() guard, but this is harmless and consistent with the pattern used across other files in this PR.

apps/meteor/app/lib/server/functions/updateMessage.ts (2)

27-39: LGTM! Event dispatch migration correctly preserves extend/modify chain.

The migration maintains the correct sequencing:

  1. IPreMessageUpdatedPrevent - blocks update if app prevents it
  2. IPreMessageUpdatedExtend - allows apps to extend the message
  3. IPreMessageUpdatedModify - allows apps to modify the extended result

The typeof result === 'object' check at line 36 safely handles potential undefined returns from the optional chaining.


88-92: LGTM! Post-update event correctly fires asynchronously.

The void keyword appropriately indicates fire-and-forget semantics for the post-update notification.

apps/meteor/server/lib/eraseRoom.ts (1)

43-61: LGTM! Event dispatch migration consistent with strict validation pattern.

The migration correctly preserves:

  • Pre-delete prevention that throws Meteor.Error when blocked (strict validation)
  • Post-delete fire-and-forget notification with void

This aligns with the sibling function eraseRoomLooseValidation in eraseTeam.ts, which uses the same pattern but returns false instead of throwing.

apps/meteor/server/services/messages/service.ts (2)

1-1: LGTM! Import updated for AppEvents enum.

The import correctly brings in AppEvents alongside Apps for type-safe event dispatch.


204-206: LGTM! System message event dispatch migrated correctly.

The fire-and-forget pattern with void is appropriate for post-system-message notifications.

apps/meteor/ee/server/apps/communication/uikit.ts (5)

1-1: LGTM! Imports updated for type-safe orchestrator interface and events.

The import correctly brings in AppEvents for type-safe event names and IAppServerOrchestrator interface for better decoupling.


196-202: LGTM! Interface type improves flexibility.

Changing from concrete AppServerOrchestrator to IAppServerOrchestrator interface improves testability and decoupling.


216-217: LGTM! Conditional conversion prevents unnecessary async operations.

The explicit conditional pattern mid ? await ... : undefined is cleaner and avoids unnecessary converter calls when IDs are absent.


233-235: LGTM! Event interface selection uses type-safe enum.

The ternary correctly routes to IUIKitLivechatInteractionHandler for visitor interactions and IUIKitInteractionHandler otherwise, using the AppEvents enum for type safety.


264-264: LGTM! All triggerEvent calls consistently use AppEvents enum.

The migration from string literals to AppEvents.IUIKitInteractionHandler provides compile-time safety and consistency across all interaction types (viewClosed, viewSubmit, actionButton).

Also applies to: 289-289, 311-312, 330-330

apps/meteor/app/apps/server/bridges/listeners.ts (2)

304-331: Non-null assertions may cause runtime errors if user conversion fails.

Lines 314 and 325 use ! assertions on convertToApp results for joiningUser and leavingUser. If the conversion returns null or undefined, this will propagate an invalid value to the listener.

Consider adding validation or using a more defensive approach.

Additionally, this switch also needs block scoping for the const declarations (same issue as messageEvent).


443-483: Good use of block scoping in switch cases.

This method correctly wraps all switch case declarations in blocks, which is the pattern that should be applied to the other event handler methods (messageEvent, roomEvent, livechatEvent).

Consider adding a default case that either throws an error or returns a sentinel value to ensure exhaustive handling if new user events are added in the future.

@d-gubert d-gubert force-pushed the chore/convert-app-listeners-ts branch from 4915155 to 0b28672 Compare January 13, 2026 22:04
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: 6

🤖 Fix all issues with AI agents
In @apps/meteor/app/apps/server/bridges/listeners.ts:
- Around line 427-440: In the switch default branch the const declarations
(const [roomData] and const room) leak into the switch scope; wrap the default
case body in a block (add { ... } after default:) so roomData and room are
block-scoped, i.e. enclose the await
this.orch.getConverters().get('rooms').convertRoom(...) call, the
isLivechatRoom(room) check, and the return of
this.orch.getManager().getListenerManager().executeListener(...) inside the new
braces to limit scope and avoid redeclaration conflicts.
- Around line 342-351: The const declaration in the switch cases for
AppInterface.IPostLivechatAgentAssigned and
AppInterface.IPostLivechatAgentUnassigned can leak into later cases; wrap those
two case labels in a block { ... } so that const [agentData] = args.payload and
the subsequent await this.orch.getConverters().get('rooms').convertRoom(...) and
this.orch.getConverters().get('users').convertToApp(agentData.user) are scoped
locally before calling
this.orch.getManager().getListenerManager().executeListener(...).
- Around line 72-94: The HandleRoomEvent union is ambiguous because
IPreRoomUserJoined, IPostRoomUserJoined, IPreRoomUserLeave, and
IPostRoomUserLeave are declared in both the 3-element payload variant and the
1-element payload variant; remove those four event names from the single-element
branch so that the 3-element variant (payload: [IRoom, IUser, IUser])
exclusively handles them and the remaining room lifecycle events
(IPreRoomCreatePrevent, IPreRoomCreateExtend, IPreRoomCreateModify,
IPostRoomCreate, IPreRoomDeletePrevent, IPostRoomDeleted) remain in the
single-element branch (payload: [IRoom]).
- Line 151: Remove the temporary debug statement console.log('args', args) from
the listener in listeners.ts; locate the occurrence inside the relevant event
handler function (around the listener where args is referenced) and delete it,
or replace it with an appropriate structured log via the existing logger (e.g.,
processLogger or debug utility) if persistent logging is required.
- Around line 221-286: The switch cases inside the IIFE assigning to result
declare const variables (e.g., userDeleted, userReacted, reaction, isReacted,
userFollowed, isFollowed, userPinned, isPinned, userStarred, isStarred,
userReported, reason) which can leak between cases; fix this by wrapping each
case body in its own block (add { } around the code after each case label) so
the const declarations are block-scoped; apply this to the cases for
AppInterface.IPostMessageDeleted, IPostMessageReacted, IPostMessageFollowed,
IPostMessagePinned, IPostMessageStarred and IPostMessageReported while keeping
the existing calls to
this.orch.getManager().getListenerManager().executeListener(...) intact.
- Around line 304-331: The switch cases handling
AppInterface.IPreRoomUserJoined/IPostRoomUserJoined and
AppInterface.IPreRoomUserLeave/IPostRoomUserLeave declare consts from
args.payload in the case bodies, so wrap each case body in braces ({ ... }) to
create a block scope around the const declarations; update the branches that
call this.orch.getManager().getListenerManager().executeListener(...) so the
const [, joiningUser, invitingUser] and const [, leavingUser, removedBy] are
scoped inside their respective block-wrapped cases and avoid duplicate-const
errors.
🧹 Nitpick comments (9)
apps/meteor/app/lib/server/functions/updateMessage.ts (2)

27-39: Redundant optional chaining inside guarded block.

The outer condition on line 27 already ensures Apps.self is truthy, so the optional chaining (?.) on lines 28, 33, and 34 is unnecessary. While harmless, removing it would improve clarity.

Suggested simplification
 	if (message && Apps.self && Apps.isLoaded()) {
-		const prevent = await Apps.self?.triggerEvent(AppEvents.IPreMessageUpdatedPrevent, messageData);
+		const prevent = await Apps.self.triggerEvent(AppEvents.IPreMessageUpdatedPrevent, messageData);
 		if (prevent) {
 			throw new Meteor.Error('error-app-prevented-updating', 'A Rocket.Chat App prevented the message updating.');
 		}

-		let result = await Apps.self?.triggerEvent(AppEvents.IPreMessageUpdatedExtend, messageData);
-		result = await Apps.self?.triggerEvent(AppEvents.IPreMessageUpdatedModify, result);
+		let result = await Apps.self.triggerEvent(AppEvents.IPreMessageUpdatedExtend, messageData);
+		result = await Apps.self.triggerEvent(AppEvents.IPreMessageUpdatedModify, result);

 		if (typeof result === 'object') {
 			Object.assign(messageData, result);
 		}
 	}

88-92: Same redundancy pattern for post-update event.

Similar to above, Apps.self?. on line 91 is redundant since line 88 already guards with Apps.self?.isLoaded().

Suggested simplification
 	if (Apps.self?.isLoaded()) {
 		// This returns a promise, but it won't mutate anything about the message
 		// so, we don't really care if it is successful or fails
-		void Apps.self?.triggerEvent(AppEvents.IPostMessageUpdated, messageData);
+		void Apps.self.triggerEvent(AppEvents.IPostMessageUpdated, messageData);
 	}
apps/meteor/app/lib/server/functions/sendMessage.ts (2)

233-251: Redundant optional chaining inside guarded block.

Lines 234 and 240-242 use Apps.self?. inside a block already guarded by Apps.self?.isLoaded(). Since the guard ensures Apps.self is truthy, the optional chaining is redundant.

Suggested simplification
 	if (Apps.self?.isLoaded()) {
-		const prevent = await Apps.self?.triggerEvent(AppEvents.IPreMessageSentPrevent, message);
+		const prevent = await Apps.self.triggerEvent(AppEvents.IPreMessageSentPrevent, message);

 		if (prevent) {
 			return;
 		}

-		const result = await Apps.self?.triggerEvent(
+		const result = await Apps.self.triggerEvent(
 			AppEvents.IPreMessageSentModify,
-			await Apps.self?.triggerEvent(AppEvents.IPreMessageSentExtend, message),
+			await Apps.self.triggerEvent(AppEvents.IPreMessageSentExtend, message),
 		);

 		if (typeof result === 'object') {
 			message = Object.assign(message, result);

 			// Some app may have inserted malicious/invalid values in the message, let's check it again
 			await validateMessage(message, room, user);
 		}
 	}

282-286: Same redundancy pattern for post-send events.

The optional chaining on line 285 is redundant since line 282 already ensures Apps.self is truthy.

Suggested simplification
 	if (Apps.self?.isLoaded()) {
 		// If the message has a type (system message), we should notify the listener about it
 		const messageEvent = message.t ? AppEvents.IPostSystemMessageSent : AppEvents.IPostMessageSent;
-		void Apps.self?.triggerEvent(messageEvent, message);
+		void Apps.self.triggerEvent(messageEvent, message);
 	}
apps/meteor/app/api/server/lib/eraseTeam.ts (2)

77-82: Redundant optional chaining after isLoaded() check.

Since Apps.self?.isLoaded() at line 77 is truthy, Apps.self is guaranteed to be defined inside the block. The ?. on line 78 is redundant.

Suggested fix
 	if (Apps.self?.isLoaded()) {
-		const prevent = await Apps.self?.triggerEvent(AppEvents.IPreRoomDeletePrevent, room);
+		const prevent = await Apps.self.triggerEvent(AppEvents.IPreRoomDeletePrevent, room);
 		if (prevent) {
 			return false;
 		}
 	}

91-93: Same redundant optional chaining pattern.

The ?. on line 92 is unnecessary since Apps.self is already confirmed to exist by the isLoaded() check.

Suggested fix
 	if (Apps.self?.isLoaded()) {
-		void Apps.self?.triggerEvent(AppEvents.IPostRoomDeleted, room);
+		void Apps.self.triggerEvent(AppEvents.IPostRoomDeleted, room);
 	}
apps/meteor/server/lib/eraseRoom.ts (2)

43-48: Redundant optional chaining inside guarded block.

Inside the if (Apps.self?.isLoaded()) block, Apps.self is guaranteed to be defined, so the ?. on line 44 is redundant. This is harmless but slightly inconsistent.

✨ Suggested simplification
 	if (Apps.self?.isLoaded()) {
-		const prevent = await Apps.self?.triggerEvent(AppEvents.IPreRoomDeletePrevent, room);
+		const prevent = await Apps.self.triggerEvent(AppEvents.IPreRoomDeletePrevent, room);
 		if (prevent) {
 			throw new Meteor.Error('error-app-prevented-deleting', 'A Rocket.Chat App prevented the room erasing.');
 		}
 	}

59-61: Same redundant optional chaining; fire-and-forget pattern is correct.

Same nitpick as above: Apps.self.triggerEvent can be used directly inside the guarded block. The void prefix correctly signals fire-and-forget semantics for the post-delete event.

✨ Suggested simplification
 	if (Apps.self?.isLoaded()) {
-		void Apps.self?.triggerEvent(AppEvents.IPostRoomDeleted, room);
+		void Apps.self.triggerEvent(AppEvents.IPostRoomDeleted, room);
 	}
apps/meteor/app/apps/server/bridges/listeners.ts (1)

443-483: Consider adding exhaustive check for future-proofing.

The switch handles all current HandleUserEvent variants, but adding an exhaustive check helps catch missed cases if new events are added later.

Suggested pattern
 			case AppInterface.IPostUserUpdated: {
 				const [crudData] = args.payload;
 				const context: IUserUpdateContext = {
 					user: this.orch.getConverters().get('users').convertToApp(crudData.user),
 					performedBy: this.orch.getConverters().get('users').convertToApp(crudData.performedBy),
 					previousData: this.orch.getConverters().get('users').convertToApp(crudData.previousUser),
 				};
 
 				return this.orch.getManager().getListenerManager().executeListener(args.event, context);
 			}
+			default: {
+				const _exhaustiveCheck: never = args;
+				throw new Error(`Unhandled user event: ${(_exhaustiveCheck as HandleUserEvent).event}`);
+			}
 		}
 	}
📜 Review details

Configuration used: Organization 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 4915155 and 0b28672.

📒 Files selected for processing (15)
  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/app/apps/server/bridges/listeners.js
  • apps/meteor/app/apps/server/bridges/listeners.ts
  • apps/meteor/app/lib/server/functions/deleteMessage.ts
  • apps/meteor/app/lib/server/functions/sendMessage.ts
  • apps/meteor/app/lib/server/functions/updateMessage.ts
  • apps/meteor/app/livechat/server/lib/RoutingManager.ts
  • apps/meteor/app/livechat/server/lib/closeRoom.ts
  • apps/meteor/app/livechat/server/lib/departmentsLib.ts
  • apps/meteor/ee/server/apps/communication/uikit.ts
  • apps/meteor/ee/server/apps/orchestrator.js
  • apps/meteor/server/lib/eraseRoom.ts
  • apps/meteor/server/services/messages/service.ts
  • packages/apps-engine/src/server/managers/AppListenerManager.ts
💤 Files with no reviewable changes (1)
  • apps/meteor/app/apps/server/bridges/listeners.js
🚧 Files skipped from review as they are similar to previous changes (4)
  • apps/meteor/ee/server/apps/orchestrator.js
  • apps/meteor/app/livechat/server/lib/closeRoom.ts
  • packages/apps-engine/src/server/managers/AppListenerManager.ts
  • apps/meteor/server/services/messages/service.ts
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.{ts,tsx,js}: Write concise, technical TypeScript/JavaScript with accurate typing in Playwright tests
Avoid code comments in the implementation

Files:

  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/server/lib/eraseRoom.ts
  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
  • apps/meteor/app/lib/server/functions/deleteMessage.ts
  • apps/meteor/app/lib/server/functions/updateMessage.ts
  • apps/meteor/ee/server/apps/communication/uikit.ts
  • apps/meteor/app/livechat/server/lib/RoutingManager.ts
  • apps/meteor/app/livechat/server/lib/departmentsLib.ts
  • apps/meteor/app/lib/server/functions/sendMessage.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
**/*.spec.ts

📄 CodeRabbit inference engine (.cursor/rules/playwright.mdc)

**/*.spec.ts: Use descriptive test names that clearly communicate expected behavior in Playwright tests
Use .spec.ts extension for test files (e.g., login.spec.ts)

Files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
🧠 Learnings (20)
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: AppUserBridge.getUserRoomIds in apps/meteor/app/apps/server/bridges/users.ts always returns an array of strings (mapping subscription documents to room IDs), never undefined, even when user has no room subscriptions.

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/server/lib/eraseRoom.ts
  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
  • apps/meteor/app/livechat/server/lib/RoutingManager.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: AppUserBridge.getUserRoomIds in apps/meteor/app/apps/server/bridges/users.ts always returns an array of strings by mapping subscription documents to room IDs, never undefined, even when user has no room subscriptions.

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/server/lib/eraseRoom.ts
  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
  • apps/meteor/app/livechat/server/lib/RoutingManager.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
📚 Learning: 2025-09-25T09:59:26.461Z
Learnt from: Dnouv
Repo: RocketChat/Rocket.Chat PR: 37057
File: packages/apps-engine/src/definition/accessors/IUserRead.ts:23-27
Timestamp: 2025-09-25T09:59:26.461Z
Learning: UserBridge.doGetUserRoomIds in packages/apps-engine/src/server/bridges/UserBridge.ts has a bug where it implicitly returns undefined when the app lacks read permission (missing return statement in the else case of the permission check).

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.ts
  • apps/meteor/server/lib/eraseRoom.ts
  • apps/meteor/app/livechat/server/lib/RoutingManager.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
📚 Learning: 2025-11-04T16:49:19.107Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37377
File: apps/meteor/ee/server/hooks/federation/index.ts:86-88
Timestamp: 2025-11-04T16:49:19.107Z
Learning: In Rocket.Chat's federation system (apps/meteor/ee/server/hooks/federation/), permission checks follow two distinct patterns: (1) User-initiated federation actions (creating rooms, adding users to federated rooms, joining from invites) should throw MeteorError to inform users they lack 'access-federation' permission. (2) Remote server-initiated federation events should silently skip/ignore when users lack permission. The beforeAddUserToRoom hook only executes for local user-initiated actions, so throwing an error there is correct. Remote federation events are handled separately by the federation Matrix package with silent skipping logic.

Applied to files:

  • apps/meteor/server/lib/eraseRoom.ts
  • apps/meteor/app/lib/server/functions/deleteMessage.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
📚 Learning: 2025-09-30T13:00:05.465Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 36990
File: apps/meteor/ee/server/apps/storage/AppRealStorage.ts:55-58
Timestamp: 2025-09-30T13:00:05.465Z
Learning: In AppRealStorage (apps/meteor/ee/server/apps/storage/AppRealStorage.ts), the `remove` method is designed to be idempotent and returns `{ success: true }` unconditionally because the goal is to ensure the app is removed, not to distinguish whether this specific call performed the deletion. Database errors will throw exceptions.

Applied to files:

  • apps/meteor/server/lib/eraseRoom.ts
📚 Learning: 2025-12-09T20:01:07.355Z
Learnt from: sampaiodiego
Repo: RocketChat/Rocket.Chat PR: 37532
File: ee/packages/federation-matrix/src/FederationMatrix.ts:920-927
Timestamp: 2025-12-09T20:01:07.355Z
Learning: In Rocket.Chat's federation invite handling (ee/packages/federation-matrix/src/FederationMatrix.ts), when a user rejects an invite via federationSDK.rejectInvite(), the subscription cleanup happens automatically through an event-driven flow: Matrix emits a leave event back, which is processed by handleLeave() in ee/packages/federation-matrix/src/events/member.ts, and that function calls Room.performUserRemoval() to clean up the subscription. No explicit cleanup is needed in the reject branch of handleInvite() because the leave event handler takes care of it.
<!-- </add_learning>

Applied to files:

  • apps/meteor/server/lib/eraseRoom.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Ensure tests run reliably in parallel without shared state conflicts

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : All test files must be created in `apps/meteor/tests/e2e/` directory

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Utilize Playwright fixtures (`test`, `page`, `expect`) for consistency in test files

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-12-10T21:00:54.909Z
Learnt from: KevLehman
Repo: RocketChat/Rocket.Chat PR: 37091
File: ee/packages/abac/jest.config.ts:4-7
Timestamp: 2025-12-10T21:00:54.909Z
Learning: Rocket.Chat monorepo: Jest testMatch pattern '<rootDir>/src/**/*.spec.(ts|js|mjs)' is valid in this repo and used across multiple packages (e.g., packages/tools, ee/packages/omnichannel-services). Do not flag it as invalid in future reviews.

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Ensure clean state for each test execution in Playwright tests

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Use `expect` matchers for assertions (`toEqual`, `toContain`, `toBeTruthy`, `toHaveLength`, etc.) instead of `assert` statements in Playwright tests

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/page-objects/**/*.ts : Utilize existing page objects pattern from `apps/meteor/tests/e2e/page-objects/`

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Group related tests in the same file

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-10-06T20:30:45.540Z
Learnt from: d-gubert
Repo: RocketChat/Rocket.Chat PR: 37152
File: packages/apps-engine/tests/test-data/storage/storage.ts:101-122
Timestamp: 2025-10-06T20:30:45.540Z
Learning: In `packages/apps-engine/tests/test-data/storage/storage.ts`, the stub methods (updatePartialAndReturnDocument, updateStatus, updateSetting, updateAppInfo, updateMarketplaceInfo) intentionally throw "Method not implemented." Tests using these methods must stub them using `SpyOn` from the test library rather than relying on actual implementations.

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-11-24T17:08:17.065Z
Learnt from: CR
Repo: RocketChat/Rocket.Chat PR: 0
File: .cursor/rules/playwright.mdc:0-0
Timestamp: 2025-11-24T17:08:17.065Z
Learning: Applies to apps/meteor/tests/e2e/**/*.spec.ts : Prefer web-first assertions (`toBeVisible`, `toHaveText`, etc.) in Playwright tests

Applied to files:

  • apps/meteor/app/api/server/lib/eraseTeam.spec.ts
📚 Learning: 2025-11-19T18:20:37.116Z
Learnt from: gabriellsh
Repo: RocketChat/Rocket.Chat PR: 37419
File: apps/meteor/server/services/media-call/service.ts:141-141
Timestamp: 2025-11-19T18:20:37.116Z
Learning: In apps/meteor/server/services/media-call/service.ts, the sendHistoryMessage method should use call.caller.id or call.createdBy?.id as the message author, not call.transferredBy?.id. Even for transferred calls, the message should appear in the DM between the two users who are calling each other, not sent by the person who transferred the call.

Applied to files:

  • apps/meteor/app/livechat/server/lib/RoutingManager.ts
  • apps/meteor/app/lib/server/functions/sendMessage.ts
📚 Learning: 2025-10-28T16:53:42.761Z
Learnt from: ricardogarim
Repo: RocketChat/Rocket.Chat PR: 37205
File: ee/packages/federation-matrix/src/FederationMatrix.ts:296-301
Timestamp: 2025-10-28T16:53:42.761Z
Learning: In the Rocket.Chat federation-matrix integration (ee/packages/federation-matrix/), the createRoom method from rocket.chat/federation-sdk will support a 4-argument signature (userId, roomName, visibility, displayName) in newer versions. Code using this 4-argument call is forward-compatible with planned library updates and should not be flagged as an error.

Applied to files:

  • apps/meteor/app/lib/server/functions/sendMessage.ts
  • apps/meteor/app/apps/server/bridges/listeners.ts
📚 Learning: 2025-11-27T17:56:26.050Z
Learnt from: MartinSchoeler
Repo: RocketChat/Rocket.Chat PR: 37557
File: apps/meteor/client/views/admin/ABAC/AdminABACRooms.tsx:115-116
Timestamp: 2025-11-27T17:56:26.050Z
Learning: In Rocket.Chat, the GET /v1/abac/rooms endpoint (implemented in ee/packages/abac/src/index.ts) only returns rooms where abacAttributes exists and is not an empty array (query: { abacAttributes: { $exists: true, $ne: [] } }). Therefore, in components consuming this endpoint (like AdminABACRooms.tsx), room.abacAttributes is guaranteed to be defined for all returned rooms, and optional chaining before calling array methods like .join() is sufficient without additional null coalescing.

Applied to files:

  • apps/meteor/app/apps/server/bridges/listeners.ts
📚 Learning: 2025-09-16T13:33:49.237Z
Learnt from: cardoso
Repo: RocketChat/Rocket.Chat PR: 36890
File: apps/meteor/tests/e2e/e2e-encryption/e2ee-otr.spec.ts:21-26
Timestamp: 2025-09-16T13:33:49.237Z
Learning: The im.delete API endpoint accepts either a `roomId` parameter (requiring the actual DM room _id) or a `username` parameter (for the DM partner's username). Constructing slug-like identifiers like `user2${Users.userE2EE.data.username}` doesn't work for this endpoint.

Applied to files:

  • apps/meteor/app/apps/server/bridges/listeners.ts
🧬 Code graph analysis (3)
apps/meteor/app/lib/server/functions/updateMessage.ts (1)
packages/core-services/src/index.ts (1)
  • Apps (152-152)
apps/meteor/app/lib/server/functions/sendMessage.ts (2)
packages/core-services/src/index.ts (1)
  • Apps (152-152)
apps/meteor/app/apps/server/bridges/listeners.ts (1)
  • messageEvent (216-297)
apps/meteor/app/apps/server/bridges/listeners.ts (2)
packages/apps/src/AppsEngine.ts (1)
  • AppInterface (13-13)
packages/apps-engine/src/definition/rooms/index.ts (6)
  • IPreRoomCreatePrevent (19-19)
  • IPreRoomCreateExtend (17-17)
  • IPreRoomCreateModify (18-18)
  • IPostRoomCreate (15-15)
  • IPreRoomDeletePrevent (20-20)
  • IPostRoomDeleted (16-16)
🪛 Biome (2.1.2)
apps/meteor/app/apps/server/bridges/listeners.ts

[error] 224-224: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 233-233: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 244-244: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 254-254: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 264-264: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 274-274: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 308-308: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 319-319: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 344-344: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 428-428: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)


[error] 429-429: Other switch clauses can erroneously access this declaration.
Wrap the declaration in a block to restrict its access to the switch clause.

The declaration is defined in this switch clause:

Safe fix: Wrap the declaration in a block.

(lint/correctness/noSwitchDeclarations)

⏰ 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). (3)
  • GitHub Check: 📦 Build Packages
  • GitHub Check: CodeQL-Build
  • GitHub Check: CodeQL-Build
🔇 Additional comments (18)
apps/meteor/app/livechat/server/lib/RoutingManager.ts (1)

294-294: LGTM!

The migration from the Bridges-based getListenerBridge().livechatEvent() to the unified Apps.self?.triggerEvent() API is correctly implemented. The fire-and-forget semantics are preserved with void, and proper null safety is maintained via optional chaining.

apps/meteor/app/livechat/server/lib/departmentsLib.ts (2)

139-143: LGTM - Clean refactor to unified event API.

The change from getBridges()?.getListenerBridge().livechatEvent() to Apps.self?.triggerEvent() simplifies the call chain while preserving the fire-and-forget semantics with void and null safety via optional chaining.


271-272: LGTM - Consistent with the new event triggering pattern.

Same refactor pattern applied here for the department removal event, maintaining behavioral parity with the previous implementation.

apps/meteor/app/lib/server/functions/deleteMessage.ts (2)

39-43: LGTM - Safe handling of optional Apps.

The optional chaining correctly handles the case where Apps.self might be undefined. If Apps aren't loaded, prevent will be undefined (falsy), and execution continues normally without throwing.


103-105: LGTM - Post-delete event properly guarded.

The post-delete event is correctly wrapped with the deletedMsg check and uses appropriate optional chaining for Apps.self. The void keyword correctly indicates the fire-and-forget pattern.

apps/meteor/app/lib/server/functions/sendMessage.ts (1)

1-1: LGTM - Import updated correctly.

The import now includes AppEvents alongside Apps, which provides type-safe event names for the triggerEvent calls throughout the file.

apps/meteor/app/api/server/lib/eraseTeam.spec.ts (3)

43-48: LGTM!

The stub structure correctly mirrors the new Apps.self.triggerEvent API surface, replacing the previous bridge-based chain. Default isLoaded: () => false ensures Apps events are skipped in tests that don't need them.


190-219: LGTM!

The test correctly verifies that when triggerEvent returns true for IPreRoomDeletePrevent, the deletion is blocked and the function returns false. The stub setup properly reflects the new API.


242-272: LGTM!

The test properly verifies the success path: first triggerEvent call (pre-delete) returns false to allow deletion, then second call (post-delete) is invoked. The assertion sinon.assert.calledTwice(roomEventStub) confirms both events are triggered.

apps/meteor/ee/server/apps/communication/uikit.ts (6)

1-1: LGTM!

Good use of the interface type IAppServerOrchestrator and AppEvents constant imports, aligning with the dependency inversion principle and improving type safety.


196-199: LGTM!

Using the interface IAppServerOrchestrator instead of the concrete class improves testability and follows the dependency inversion principle.


216-217: LGTM!

The conditional conversion (mid ? ... : undefined) correctly handles cases where mid/rid are not present in the request body, avoiding unnecessary conversion calls.


233-235: LGTM!

Using AppEvents.IUIKitInteractionHandler and AppEvents.IUIKitLivechatInteractionHandler constants instead of string literals improves type safety and makes the code more resilient to refactoring.


264-264: LGTM!

Consistent use of AppEvents.IUIKitInteractionHandler constant across all handler cases (viewClosed, viewSubmit, actionButton) aligns with the broader refactor pattern.

Also applies to: 289-289, 330-330


311-312: LGTM!

Same safe conditional conversion pattern as in blockAction case, correctly handling optional rid/mid fields.

apps/meteor/app/apps/server/bridges/listeners.ts (3)

209-214: LGTM with note on as any cast.

The as any cast is pragmatic here since defaultEvent is a fallback handler for events not explicitly handled. The type variability makes strict typing impractical in this context.


1-31: Type definitions are well-structured.

The internal type aliases (LivechatTransferData, LivechatAgentData, UserStatusChangedData, UserCrudData) provide clear contracts for the event payloads. Good use of discriminated unions for type safety.


146-147: LGTM!

Clean constructor with proper dependency injection pattern.

@ggazzo ggazzo force-pushed the chore/convert-app-listeners-ts branch from 0b28672 to 69b79aa Compare January 14, 2026 01:56
@ggazzo ggazzo added this to the 8.1.0 milestone Jan 14, 2026
@ggazzo ggazzo added the stat: QA assured Means it has been tested and approved by a company insider label Jan 14, 2026
@dionisio-bot dionisio-bot bot added the stat: ready to merge PR tested and approved waiting for merge label Jan 14, 2026
@ggazzo ggazzo merged commit 3de2642 into develop Jan 14, 2026
48 checks passed
@ggazzo ggazzo deleted the chore/convert-app-listeners-ts branch January 14, 2026 19:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stat: QA assured Means it has been tested and approved by a company insider stat: ready to merge PR tested and approved waiting for merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants