Skip to content

feat: enhance performance with memoization and error logging in vario…#38631

Open
IvanNilash1206 wants to merge 4 commits into
RocketChat:developfrom
IvanNilash1206:improve
Open

feat: enhance performance with memoization and error logging in vario…#38631
IvanNilash1206 wants to merge 4 commits into
RocketChat:developfrom
IvanNilash1206:improve

Conversation

@IvanNilash1206
Copy link
Copy Markdown

@IvanNilash1206 IvanNilash1206 commented Feb 12, 2026

…us components

Proposed changes (including videos or screenshots)

This implements a set of performance and stability improvement to the existing core components . The changes made focus on optimizing the database access , improving type safety and enhance error handling of the project. All of this is done without adding any new feature and only by doing changes to the existing files and folders.

  • Database optimization

    • Adding missing compound index for frequent access
    • Replacing sequential calls with bulk / batch processing calls
    • Applied readSecondaryPreferred() for non-critical reads.
    • Added maxTimeMS to prevent long-running queries.
  • Error Handling Improvements

    • Replace empty .catch( ) blocks with proper logging
    • Clean error message for better understanding and handling
    • Removing the errors that were ignored
    • Logging cleanup
  • Type Safety Enhancements

    • Given proper type to the data with unsafe types
    • Added explicit return types to the public model method
    • Improved the TypeScript compliance.
  • Improved React performance

    • Fixed incorrect use of useEffect dependency array
    • Removed unnecessary memo usage
    • Reduced the unnecessary re-renders

Issue(s)

Internal optimization and code quality improvements.
No user-facing issue reference.

Steps to test or reproduce

  1. Type Check
yarn typecheck
  1. Lint
yarn lint
  1. Unit Tests
yarn testunit
  1. Manual Verification
  • Verify core flows (login, messaging, room updates, user updates).
  • Monitor logs to confirm no console.* statements remain.
  • Confirm no silent failures.
  • Validate database operations still behave as expected.
  • Confirm no UI regression in major views (teams, banners, avatars, admin).

Further comments

This PR intentionally avoids architectural restructuring and focuses only on improvements that can be implemented within existing files. All changes are backward-compatible and production-safe.

Summary by CodeRabbit

  • Bug Fixes

    • Improved error handling across logout, app removal, and connection/method calls by logging errors instead of silently swallowing them.
  • Performance

    • Added in-memory caching for frequent lookups and new database indexes to speed common queries.
    • Introduced memoization for several avatar and user-list UI components to reduce unnecessary re-renders.
  • Chores

    • Tightened type usage and removed noisy console logs for cleaner runtime output.

@IvanNilash1206 IvanNilash1206 requested a review from a team as a code owner February 12, 2026 11:17
Copilot AI review requested due to automatic review settings February 12, 2026 11:17
@IvanNilash1206 IvanNilash1206 requested review from a team as code owners February 12, 2026 11:17
@dionisio-bot
Copy link
Copy Markdown
Contributor

dionisio-bot Bot commented Feb 12, 2026

Looks like this PR is not ready to merge, because of the following issues:

  • This PR is missing the 'stat: QA assured' label
  • This PR is missing the required milestone or project

Please fix the issues and try again

If you have any trouble, please check the PR guidelines

@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented Feb 12, 2026

⚠️ No Changeset found

Latest commit: 262c4fb

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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Feb 12, 2026

Walkthrough

Adds React.memo to several avatar components; tightens TypeScript signatures (replacing any with unknown/PeerInfo and annotating callbacks); replaces silent catches with structured logging; adds an in-memory TTL/LRU cache for findOneById and new compound MongoDB indexes; minor API signature tweaks in livechat and ddp-streamer.

Changes

Cohort / File(s) Summary
React component memoization
apps/meteor/client/components/UserAutoCompleteMultiple/UserAvatarChip.tsx, apps/meteor/client/components/UserInfo/UserInfoAvatar.tsx, apps/meteor/client/components/avatar/UserAvatarEditor/UserAvatarSuggestions.tsx, apps/meteor/client/components/message/content/attachments/structure/AttachmentAuthorAvatar.tsx
Wrapped four avatar-related components with React.memo, added memo imports, and adjusted component declarations to memoized variants.
Livechat startup typing & logout logging
apps/meteor/app/livechat/server/startup.ts
Tightened registerGuest.patch wrapper signature with parameter types; on logout, setUserStatusLivechatIf call now has a .catch that logs userId and error.
ddp-streamer robustness & logging
ee/apps/ddp-streamer/src/Client.ts
Robust x-forwarded-for parsing, widened onClose callback type, and replaced silent catches for method/subscription chains with structured error logging; some catch error types set to unknown.
AppManager error handling
packages/apps-engine/src/server/AppManager.ts
doAppRemoved(app) call now logs errors in its catch instead of swallowing them.
In-memory caching (BaseRaw)
packages/models/src/models/BaseRaw.ts
Added optional per-model in-memory TTL/LRU cache (ModelOptions.cache) with helpers (getCached, setCached, invalidateCache); integrated cache into findOneById; added cache invalidation on update paths; converted some methods to async.
Indexes & bulk updates (models)
packages/models/src/models/Rooms.ts, packages/models/src/models/Subscriptions.ts, packages/models/src/models/Users.ts
Added several compound/background/sparse index definitions; refactored setBlockedByRoomId/unsetBlockedByRoomId to use bulkWrite and return paired UpdateResult values.
VoIP typing & logger improvements
packages/ui-voip/src/context/MediaCallContext.ts, packages/ui-voip/src/context/MediaCallLogger.ts, packages/ui-voip/src/context/MockedMediaCallProvider.tsx
Replaced any with PeerInfo/unknown in several method signatures; changed variadic params from any[] to unknown[]; removed or silenced debug console.log calls in mock provider.

Sequence Diagram(s)

sequenceDiagram
    participant Caller as Client
    participant BaseRaw as BaseRaw.findOneById()
    participant Cache as In-Memory Cache
    participant DB as MongoDB

    Caller->>BaseRaw: findOneById(id, options?)
    alt Cache enabled and no options
        BaseRaw->>Cache: getCached(id)
        alt Cache hit (not expired)
            Cache-->>BaseRaw: cached document
            BaseRaw-->>Caller: return cached document
        else Cache miss or expired
            BaseRaw->>DB: query by id
            DB-->>BaseRaw: document
            BaseRaw->>Cache: setCached(id, document)
            BaseRaw-->>Caller: return document
        end
    else Cache disabled or options provided
        BaseRaw->>DB: query by id
        DB-->>BaseRaw: document
        BaseRaw-->>Caller: return document
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Hooray — I memoized a view,

Cached a row and logged the true,
Types made tidy, errors shown,
Indexes planted, queries flown,
A little hop for faster code.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately reflects the main changes: performance enhancements through React memoization and error logging improvements across multiple components and services.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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


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.

Copy link
Copy Markdown
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: 14

Caution

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

⚠️ Outside diff range comments (1)
packages/models/src/models/BaseRaw.ts (1)

242-260: ⚠️ Potential issue | 🔴 Critical

Cache invalidation is incomplete and fragile for non-_id filters.

Two issues:

  1. Filter assumption: invalidateCache is only called when '_id' in filter, but updates can match documents by other fields. Also, filter._id could be an operator ({ $in: [...] }) rather than a plain string — casting it to string would produce a nonsensical key. In findOneAndUpdate, result._id is available and should be used instead.

  2. Missing invalidation on delete paths: deleteOne, deleteMany, findOneAndDelete, removeById, and removeByIds never invalidate the cache. A deleted document will continue to be served from cache until TTL expires. Similarly, updateMany skips invalidation entirely.

This will cause stale/phantom reads for any model that opts into caching.

🐛 Sketch: safer invalidation in findOneAndUpdate
   const result = await this.col.findOneAndUpdate(query, update, options || {});
 
-  if (result && '_id' in query) {
-    this.invalidateCache(query._id as string);
-  }
+  if (result) {
+    this.invalidateCache(result._id as unknown as string);
+  }
 
   return result;

For delete methods, add invalidateCache calls in deleteOne, findOneAndDelete, deleteMany, and removeById/removeByIds (or clear the entire cache on bulk deletes).

Also applies to: 338-360

🤖 Fix all issues with AI agents
In
`@apps/meteor/client/components/avatar/UserAvatarEditor/UserAvatarSuggestions.tsx`:
- Around line 12-13: Remove the inline comment immediately above the
UserAvatarSuggestions declaration (the "// Memoized to prevent re-renders when
parent updates but props haven't changed" line) so the function declaration
const UserAvatarSuggestions = memo(function UserAvatarSuggestions({ disabled,
onSelectOne }: UserAvatarSuggestionsProps) { remains uncommented; ensure no
other implementation comments remain in this file around the
UserAvatarSuggestions function to comply with the project's comment guidelines.

In
`@apps/meteor/client/components/message/content/attachments/structure/AttachmentAuthorAvatar.tsx`:
- Around line 5-6: Remove the inline comment before the AttachmentAuthorAvatar
declaration; leave the memoized component intact (AttachmentAuthorAvatar =
memo(({ url }: { url: string }): ReactElement => <BaseAvatar url={url}
size='x24' />)); simply delete the comment text "// Memoized to prevent
re-renders when parent updates but URL hasn't changed" to comply with the coding
guideline prohibiting implementation comments.

In `@apps/meteor/client/components/UserAutoCompleteMultiple/UserAvatarChip.tsx`:
- Around line 13-14: Remove the inline implementation comment preceding the
UserAvatarChip declaration: delete the comment "// Memoized to prevent
re-renders in lists when sibling items change" so the component definition const
UserAvatarChip = memo(({ federated, username, name, ...props }:
UserAvatarChipProps) => { remains without inline comments; leave the memo
wrapper and UserAvatarChipProps typing unchanged.

In `@apps/meteor/client/components/UserInfo/UserInfoAvatar.tsx`:
- Around line 5-6: Remove the inline implementation comment above the memoized
component; leave the memoized export intact. Specifically, edit the
UserInfoAvatar declaration (the memo wrapper around UserAvatar using
ComponentProps<typeof UserAvatar>) and delete the leading comment line ("//
Memoized to prevent re-renders when parent component updates") so the file
contains only the existing const UserInfoAvatar = memo((props:
ComponentProps<typeof UserAvatar>): ReactElement => <UserAvatar size='x332'
{...props} />); without any implementation comments.

In `@packages/models/src/models/BaseRaw.ts`:
- Around line 47-55: Remove all inline and block comments introduced in
BaseRaw.ts to comply with the repository guideline "Avoid code comments in the
implementation"; specifically delete the comment blocks around the cache
property/type declarations and the CacheEntry<T> type and any other // or /* */
comments present (search for occurrences near symbols like cache, CacheEntry,
findOneById and other functions in BaseRaw). After removing the comments, ensure
no commented-out code is needed for logic, run the TypeScript build/tests to
confirm nothing was accidentally removed that affects compilation, and keep only
executable code and JSDoc-free signatures.
- Around line 170-181: The forEach callback in BaseRaw.invalidateCache currently
has an expression body that implicitly returns the boolean from Map.delete,
triggering the lint warning; update the callback to use a block body that
discards the return value (e.g., change id.forEach((i) => this.cache?.delete(i))
to id.forEach((i) => { this.cache?.delete(i); }) ) or replace the forEach with
an equivalent for...of loop to ensure the callback does not return a value; keep
changes inside the invalidateCache method.
- Around line 69-73: The in-memory cache fields on BaseRaw (cache, cacheTTL,
cacheMaxSize) create per-process caches that can serve stale reads in
multi-instance deployments; update the ModelOptions type and BaseRaw
constructor/docs to explicitly warn that cache is per-process and not
invalidated across instances, recommend limiting cache enablement to
eventually-consistent, read-heavy models (e.g., not permissions/roles), and add
a comment or runtime warning when a model is created with caching enabled
suggesting a shared cache (Redis) for strong consistency; reference the BaseRaw
class and the options that enable caching (cache, cacheTTL, cacheMaxSize,
ModelOptions) so callers can make an informed choice.
- Around line 154-168: The current eviction in setCached implements FIFO because
Map preserves insertion order and getCached does not update entry order; change
getCached to perform a delete-and-reinsert on every cache hit (e.g., read the
entry, delete the key, then set it again) so the accessed key becomes
most-recently-used, and also update setCached to delete existing key before
setting to ensure updates move the key to the end; reference the methods
getCached and setCached when making these changes.

In `@packages/models/src/models/Rooms.ts`:
- Around line 99-108: Remove the inline trailing comments in the Rooms model
index definitions and drop the deprecated background: true option from the
compound index objects; specifically edit the index entries that use key: {
teamId: 1, archived: 1 } and key: { t: 1, archived: 1 } to remove any inline
comments and remove the background: true property (leave other properties like
sparse if still desired) so the index definitions conform to MongoDB driver
6.10.0 and the project's no-inline-comment guideline.

In `@packages/models/src/models/Subscriptions.ts`:
- Around line 1664-1685: The return assembly in unsetBlockedByRoomId is using
legacy BulkWriteResult properties (result.nMatched/result.nModified); update the
code to use the modern driver properties (result.matchedCount and
result.modifiedCount) when constructing each UpdateResult, mirroring the fix
applied to setBlockedByRoomId and keeping the same shape (acknowledged,
matchedCount, modifiedCount, upsertedCount, upsertedId) for both returned
entries.
- Around line 1640-1661: setBlockedByRoomId currently reads non-existent
BulkWriteResult props (nMatched/nModified) and returns identical aggregate
counts for both operations; replace those with the correct properties
(result.matchedCount and result.modifiedCount) if you accept aggregate totals,
OR better: perform two separate updateOne calls (via Promise.all) to get
per-operation UpdateResult objects and return them so callers in
blockUser.ts/unblockUser.ts can inspect modifiedCount per operation; update the
function setBlockedByRoomId to either 1) change the returned fields to use
result.matchedCount/result.modifiedCount (and keep bulkWrite) or 2) replace the
bulkWrite call with two await this.col.updateOne calls wrapped in Promise.all
and return their individual UpdateResult values.

In `@packages/ui-voip/src/context/MediaCallContext.ts`:
- Around line 119-120: Remove the inline comment on the onToggleWidget
declaration so the file uses the PeerInfo type without an implementation
comment; specifically update the MediaCallContext.ts declaration for
onToggleWidget (currently typed as onToggleWidget: (peerInfo?: PeerInfo) =>
void) by deleting the preceding comment line, keeping consistency with
MediaCallContextType and MediaCallExternalContextType.

In `@packages/ui-voip/src/context/MediaCallLogger.ts`:
- Line 11: Remove the inline implementation comment at the top of
MediaCallLogger.ts (the comment explaining the reason for replacing any[] with
unknown[]); delete that comment line(s) and leave the type change intact (e.g.,
where any[] was updated to unknown[]) so MediaCallLogger and its exported
functions/classes contain no implementation-level comments per the coding
guidelines.
- Around line 12-26: The MediaCallLogger implementation uses parameter types
unknown[] for log/debug/error/warn which is narrower than the IMediaSignalLogger
interface's any[]; update the method signatures on class MediaCallLogger
(methods log, debug, error, warn) to accept any[] instead of unknown[] so the
class correctly implements the IMediaSignalLogger contract, adjusting all four
method declarations accordingly.
🧹 Nitpick comments (7)
packages/ui-voip/src/context/MockedMediaCallProvider.tsx (1)

52-64: Remove the added code comments — the underscore-prefixed params and empty bodies are self-explanatory.

The _device prefix and empty function body already signal an intentional no-op; the comments on lines 53, 57, and 63 are redundant.

♻️ Suggested diff
-	const onDeviceChange = (_device: any) => {
-		// Device change handled by mocked provider - no logging needed
-	};
+	const onDeviceChange = (_device: any) => {};

 	const onForward = () => {
-		// Forward call handled by mocked provider
 		clearState();
 		setWidgetState('closed');
 	};

-	const onTone = (_tone: string) => {
-		// Tone input handled by mocked provider
-	};
+	const onTone = (_tone: string) => {};

As per coding guidelines, **/*.{ts,tsx,js}: "Avoid code comments in the implementation".

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

684-687: Inline comment violates coding guidelines.

Line 684 adds an explanatory comment. The coding guideline for **/*.{ts,tsx,js} states to avoid code comments in the implementation. Consider removing it — the .catch + log pattern is self-explanatory.

Also, error is untyped here. Since this PR aims to improve type safety, consider typing the catch parameter and guarding .message access:

Proposed fix
-		// Non-critical notification - log error but don't fail removal
 		await this.bridges.getAppActivationBridge().doAppRemoved(app).catch((error) => {
-			console.error('Failed to notify app removal', { appId: id, error: error.message });
+			console.error('Failed to notify app removal', { appId: id, error: error instanceof Error ? error.message : error });
 		});

As per coding guidelines: **/*.{ts,tsx,js}: "Avoid code comments in the implementation".

apps/meteor/app/livechat/server/startup.ts (1)

136-139: Good use of structured logger; remove inline comment per guidelines.

Using logger.error instead of a silent .catch() is a solid improvement. However, line 136 adds an inline comment that should be removed per coding guidelines.

Proposed fix
-		// Update agent status on logout - log error but don't block logout
 		void setUserStatusLivechatIf(user._id, ILivechatAgentStatus.NOT_AVAILABLE, {}, { livechatStatusSystemModified: true }).catch((error) => {
 			logger.error('Failed to update livechat agent status on logout', { userId: user._id, error });
 		});

As per coding guidelines: **/*.{ts,tsx,js}: "Avoid code comments in the implementation".

ee/apps/ddp-streamer/src/Client.ts (1)

149-160: Remove comments; add type safety to catch parameters consistent with line 82.

Both callMethod and callSubscribe have inline comments (lines 150, 157) that should be removed per guidelines. Also, the error parameter in .catch is untyped and .message is accessed unsafely — inconsistent with line 82 where err: unknown is used.

Proposed fix
 	async callMethod(packet: IPacket): Promise<void> {
-		// Chain method calls - log errors but continue processing
-		this.chain = this.chain.then(() => server.call(this, packet)).catch((error) => {
-			console.error('DDP method call failed', { method: packet.method, error: error.message });
+		this.chain = this.chain.then(() => server.call(this, packet)).catch((error: unknown) => {
+			console.error('DDP method call failed', { method: packet.method, error: error instanceof Error ? error.message : error });
 		});
 	}

 	async callSubscribe(packet: IPacket): Promise<void> {
-		// Chain subscription calls - log errors but continue processing
-		this.chain = this.chain.then(() => server.subscribe(this, packet)).catch((error) => {
-			console.error('DDP subscription failed', { name: packet.name, error: error.message });
+		this.chain = this.chain.then(() => server.subscribe(this, packet)).catch((error: unknown) => {
+			console.error('DDP subscription failed', { name: packet.name, error: error instanceof Error ? error.message : error });
 		});
 	}

As per coding guidelines: **/*.{ts,tsx,js}: "Avoid code comments in the implementation".

apps/meteor/client/components/avatar/UserAvatarEditor/UserAvatarSuggestions.tsx (1)

13-15: memo has limited benefit here; the type cast erodes safety.

Two observations:

  1. Since useUserAvatarSuggestions() is called inside this component, any hook state change (loading → loaded) triggers a re-render regardless of memo. The memoization only guards against parent-driven re-renders with unchanged disabled/onSelectOne props — make sure that's the scenario you're optimizing for.

  2. The as UserAvatarSuggestion[] cast on line 15 silently discards type checking. Consider narrowing the hook's return type instead so the cast isn't needed.

packages/models/src/models/Subscriptions.ts (1)

70-79: Remove inline comments; background: true is a no-op since MongoDB 4.2.

Same concern as in Rooms.ts—the coding guideline for **/*.{ts,tsx,js} says to avoid code comments in the implementation. The background option is also ignored since MongoDB 4.2.

As per coding guidelines, "Avoid code comments in the implementation."

packages/models/src/models/Users.ts (1)

137-151: Remove inline comments; sparse is ineffective on indexes containing active.

  1. Per coding guidelines, avoid code comments in implementation files (**/*.{ts,tsx,js}).
  2. sparse: true on the { statusLivechat, roles, active, status } and { active, roles } indexes has no practical effect because virtually every user document has the active field set, so the sparse filter never kicks in. It's not harmful, but it's misleading—consider removing sparse from these two indexes or documenting the intent in a non-inline location (e.g., ADR).
  3. Same background: true note as the other files—it's a no-op since MongoDB 4.2.

As per coding guidelines, "Avoid code comments in the implementation."

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f5d4ecd and 95a111f.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (14)
  • apps/meteor/app/livechat/server/startup.ts
  • apps/meteor/client/components/UserAutoCompleteMultiple/UserAvatarChip.tsx
  • apps/meteor/client/components/UserInfo/UserInfoAvatar.tsx
  • apps/meteor/client/components/avatar/UserAvatarEditor/UserAvatarSuggestions.tsx
  • apps/meteor/client/components/message/content/attachments/structure/AttachmentAuthorAvatar.tsx
  • ee/apps/ddp-streamer/src/Client.ts
  • packages/apps-engine/src/server/AppManager.ts
  • packages/models/src/models/BaseRaw.ts
  • packages/models/src/models/Rooms.ts
  • packages/models/src/models/Subscriptions.ts
  • packages/models/src/models/Users.ts
  • packages/ui-voip/src/context/MediaCallContext.ts
  • packages/ui-voip/src/context/MediaCallLogger.ts
  • packages/ui-voip/src/context/MockedMediaCallProvider.tsx
🧰 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:

  • packages/apps-engine/src/server/AppManager.ts
  • packages/ui-voip/src/context/MediaCallContext.ts
  • packages/models/src/models/Rooms.ts
  • apps/meteor/client/components/message/content/attachments/structure/AttachmentAuthorAvatar.tsx
  • apps/meteor/app/livechat/server/startup.ts
  • apps/meteor/client/components/avatar/UserAvatarEditor/UserAvatarSuggestions.tsx
  • packages/ui-voip/src/context/MediaCallLogger.ts
  • packages/ui-voip/src/context/MockedMediaCallProvider.tsx
  • apps/meteor/client/components/UserInfo/UserInfoAvatar.tsx
  • ee/apps/ddp-streamer/src/Client.ts
  • packages/models/src/models/Subscriptions.ts
  • apps/meteor/client/components/UserAutoCompleteMultiple/UserAvatarChip.tsx
  • packages/models/src/models/BaseRaw.ts
  • packages/models/src/models/Users.ts
🧠 Learnings (5)
📚 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:

  • packages/apps-engine/src/server/AppManager.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:

  • packages/models/src/models/Rooms.ts
📚 Learning: 2026-01-17T01:51:47.764Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 38219
File: packages/core-typings/src/cloud/Announcement.ts:5-6
Timestamp: 2026-01-17T01:51:47.764Z
Learning: In packages/core-typings/src/cloud/Announcement.ts, the AnnouncementSchema.createdBy field intentionally overrides IBannerSchema.createdBy (object with _id and optional username) with a string enum ['cloud', 'system'] to match existing runtime behavior. This is documented as technical debt with a FIXME comment at apps/meteor/app/cloud/server/functions/syncWorkspace/handleCommsSync.ts:53 and should not be flagged as an error until the runtime behavior is corrected.

Applied to files:

  • apps/meteor/app/livechat/server/startup.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:

  • packages/models/src/models/Subscriptions.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:

  • packages/models/src/models/Subscriptions.ts
🧬 Code graph analysis (4)
packages/apps-engine/src/server/AppManager.ts (2)
packages/ui-voip/src/context/MediaCallLogger.ts (1)
  • error (20-22)
apps/meteor/app/push/server/methods.ts (1)
  • id (134-144)
packages/ui-voip/src/context/MediaCallContext.ts (2)
packages/ui-voip/src/index.ts (1)
  • PeerInfo (4-4)
packages/ui-voip/src/context/index.ts (1)
  • PeerInfo (2-2)
apps/meteor/app/livechat/server/startup.ts (1)
packages/omni-core/src/visitor/create.ts (1)
  • registerGuest (16-116)
ee/apps/ddp-streamer/src/Client.ts (1)
ee/packages/presence/src/Presence.ts (1)
  • Presence (12-334)
🪛 Biome (2.3.14)
packages/models/src/models/BaseRaw.ts

[error] 177-177: This callback passed to forEach() iterable method should not return a value.

Either remove this return or remove the returned value.

(lint/suspicious/useIterableCallbackReturn)

⏰ 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: CodeQL analysis (javascript-typescript)
  • GitHub Check: Agent
  • GitHub Check: cubic · AI code reviewer
🔇 Additional comments (7)
apps/meteor/app/livechat/server/startup.ts (1)

29-29: LGTM — explicit parameter types improve clarity.

The added type annotations for originalFn, newData, and options correctly leverage typeof registerGuest and Parameters<> utility types.

ee/apps/ddp-streamer/src/Client.ts (3)

31-35: LGTM — robust x-forwarded-for handling.

Properly handles the case where x-forwarded-for is an array (multiple header values) or a string, with a clean fallback to empty string.


82-84: LGTM — good use of unknown type on catch parameter.


101-103: LGTM — explicit callback typing.

packages/ui-voip/src/context/MediaCallLogger.ts (1)

12-26: Method implementations look correct.

log and debug are properly gated behind isDebug, while error and warn always emit — appropriate behavior for a logger.

packages/models/src/models/BaseRaw.ts (2)

266-284: LGTM on the findOneById caching flow (assuming the upstream issues are fixed).

The bypass-when-options-are-provided logic is correct — projections would produce partial documents that shouldn't populate a full-document cache. The early-return on cache hit and post-fetch population are clean.


99-106: Cache is correctly delegated through proxy property access — no issue present.

The traceInstanceMethods proxy uses Reflect.get(target, prop) for non-function properties (line 41 of packages/tracing/src/traceInstanceMethods.ts), which properly delegates property access to the original instance. The cache Map is accessed directly through the proxy's get trap and remains connected to methods that call this.cache.

Likely an incorrect or invalid review comment.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +12 to +13
// Memoized to prevent re-renders when parent updates but props haven't changed
const UserAvatarSuggestions = memo(function UserAvatarSuggestions({ disabled, onSelectOne }: UserAvatarSuggestionsProps) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove the inline comment per coding guidelines.

Proposed fix
-// Memoized to prevent re-renders when parent updates but props haven't changed
 const UserAvatarSuggestions = memo(function UserAvatarSuggestions({ disabled, onSelectOne }: UserAvatarSuggestionsProps) {

As per coding guidelines, **/*.{ts,tsx,js}: "Avoid code comments in the implementation".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Memoized to prevent re-renders when parent updates but props haven't changed
const UserAvatarSuggestions = memo(function UserAvatarSuggestions({ disabled, onSelectOne }: UserAvatarSuggestionsProps) {
const UserAvatarSuggestions = memo(function UserAvatarSuggestions({ disabled, onSelectOne }: UserAvatarSuggestionsProps) {
🤖 Prompt for AI Agents
In
`@apps/meteor/client/components/avatar/UserAvatarEditor/UserAvatarSuggestions.tsx`
around lines 12 - 13, Remove the inline comment immediately above the
UserAvatarSuggestions declaration (the "// Memoized to prevent re-renders when
parent updates but props haven't changed" line) so the function declaration
const UserAvatarSuggestions = memo(function UserAvatarSuggestions({ disabled,
onSelectOne }: UserAvatarSuggestionsProps) { remains uncommented; ensure no
other implementation comments remain in this file around the
UserAvatarSuggestions function to comply with the project's comment guidelines.

Comment on lines +5 to +6
// Memoized to prevent re-renders when parent updates but URL hasn't changed
const AttachmentAuthorAvatar = memo(({ url }: { url: string }): ReactElement => <BaseAvatar url={url} size='x24' />);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove the inline comment per coding guidelines.

Proposed fix
-// Memoized to prevent re-renders when parent updates but URL hasn't changed
 const AttachmentAuthorAvatar = memo(({ url }: { url: string }): ReactElement => <BaseAvatar url={url} size='x24' />);

As per coding guidelines, **/*.{ts,tsx,js}: "Avoid code comments in the implementation".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Memoized to prevent re-renders when parent updates but URL hasn't changed
const AttachmentAuthorAvatar = memo(({ url }: { url: string }): ReactElement => <BaseAvatar url={url} size='x24' />);
const AttachmentAuthorAvatar = memo(({ url }: { url: string }): ReactElement => <BaseAvatar url={url} size='x24' />);
🤖 Prompt for AI Agents
In
`@apps/meteor/client/components/message/content/attachments/structure/AttachmentAuthorAvatar.tsx`
around lines 5 - 6, Remove the inline comment before the AttachmentAuthorAvatar
declaration; leave the memoized component intact (AttachmentAuthorAvatar =
memo(({ url }: { url: string }): ReactElement => <BaseAvatar url={url}
size='x24' />)); simply delete the comment text "// Memoized to prevent
re-renders when parent updates but URL hasn't changed" to comply with the coding
guideline prohibiting implementation comments.

Comment on lines +13 to +14
// Memoized to prevent re-renders in lists when sibling items change
const UserAvatarChip = memo(({ federated, username, name, ...props }: UserAvatarChipProps) => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove the inline comment per coding guidelines.

The comment on line 13 violates the project's guideline to avoid code comments in the implementation. The memo wrapper is self-explanatory.

Proposed fix
-// Memoized to prevent re-renders in lists when sibling items change
 const UserAvatarChip = memo(({ federated, username, name, ...props }: UserAvatarChipProps) => {

As per coding guidelines, **/*.{ts,tsx,js}: "Avoid code comments in the implementation".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Memoized to prevent re-renders in lists when sibling items change
const UserAvatarChip = memo(({ federated, username, name, ...props }: UserAvatarChipProps) => {
const UserAvatarChip = memo(({ federated, username, name, ...props }: UserAvatarChipProps) => {
🤖 Prompt for AI Agents
In `@apps/meteor/client/components/UserAutoCompleteMultiple/UserAvatarChip.tsx`
around lines 13 - 14, Remove the inline implementation comment preceding the
UserAvatarChip declaration: delete the comment "// Memoized to prevent
re-renders in lists when sibling items change" so the component definition const
UserAvatarChip = memo(({ federated, username, name, ...props }:
UserAvatarChipProps) => { remains without inline comments; leave the memo
wrapper and UserAvatarChipProps typing unchanged.

Comment on lines +5 to +6
// Memoized to prevent re-renders when parent component updates
const UserInfoAvatar = memo((props: ComponentProps<typeof UserAvatar>): ReactElement => <UserAvatar size='x332' {...props} />);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove the inline comment per coding guidelines.

Proposed fix
-// Memoized to prevent re-renders when parent component updates
 const UserInfoAvatar = memo((props: ComponentProps<typeof UserAvatar>): ReactElement => <UserAvatar size='x332' {...props} />);

As per coding guidelines, **/*.{ts,tsx,js}: "Avoid code comments in the implementation".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Memoized to prevent re-renders when parent component updates
const UserInfoAvatar = memo((props: ComponentProps<typeof UserAvatar>): ReactElement => <UserAvatar size='x332' {...props} />);
const UserInfoAvatar = memo((props: ComponentProps<typeof UserAvatar>): ReactElement => <UserAvatar size='x332' {...props} />);
🤖 Prompt for AI Agents
In `@apps/meteor/client/components/UserInfo/UserInfoAvatar.tsx` around lines 5 -
6, Remove the inline implementation comment above the memoized component; leave
the memoized export intact. Specifically, edit the UserInfoAvatar declaration
(the memo wrapper around UserAvatar using ComponentProps<typeof UserAvatar>) and
delete the leading comment line ("// Memoized to prevent re-renders when parent
component updates") so the file contains only the existing const UserInfoAvatar
= memo((props: ComponentProps<typeof UserAvatar>): ReactElement => <UserAvatar
size='x332' {...props} />); without any implementation comments.

Comment on lines +47 to 55
// Enable simple LRU cache for findOneById with configurable TTL (milliseconds)
cache?: { enabled: boolean; ttl?: number; maxSize?: number };
};

// Simple cache entry with TTL
type CacheEntry<T> = {
data: T | null;
timestamp: number;
};
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove inline code comments to comply with coding guidelines.

The PR introduces numerous inline and block comments throughout this file (lines 47, 51, 69, 99, 135–138, 145, 154–157, 161, 170–172, 254, 267, 278, 348, 355). These violate the project's coding guidelines.

As per coding guidelines, **/*.{ts,tsx,js}: "Avoid code comments in the implementation".

Also applies to: 69-72, 99-104, 135-152, 154-168, 254-257, 267-281, 348-351, 355-358

🤖 Prompt for AI Agents
In `@packages/models/src/models/BaseRaw.ts` around lines 47 - 55, Remove all
inline and block comments introduced in BaseRaw.ts to comply with the repository
guideline "Avoid code comments in the implementation"; specifically delete the
comment blocks around the cache property/type declarations and the CacheEntry<T>
type and any other // or /* */ comments present (search for occurrences near
symbols like cache, CacheEntry, findOneById and other functions in BaseRaw).
After removing the comments, ensure no commented-out code is needed for logic,
run the TypeScript build/tests to confirm nothing was accidentally removed that
affects compilation, and keep only executable code and JSDoc-free signatures.

Comment thread packages/models/src/models/Subscriptions.ts
Comment thread packages/models/src/models/Subscriptions.ts
Comment on lines +119 to +120
// Use proper PeerInfo type instead of any for better type safety
onToggleWidget: (peerInfo?: PeerInfo) => void;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove the code comment per coding guidelines.

The type improvement from any to PeerInfo is good and aligns with the other context types (MediaCallContextType at Line 55 and MediaCallExternalContextType at Line 103). However, the inline comment on Line 119 should be removed.

Proposed fix
-	// Use proper PeerInfo type instead of any for better type safety
 	onToggleWidget: (peerInfo?: PeerInfo) => void;

As per coding guidelines, **/*.{ts,tsx,js}: "Avoid code comments in the implementation".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Use proper PeerInfo type instead of any for better type safety
onToggleWidget: (peerInfo?: PeerInfo) => void;
onToggleWidget: (peerInfo?: PeerInfo) => void;
🤖 Prompt for AI Agents
In `@packages/ui-voip/src/context/MediaCallContext.ts` around lines 119 - 120,
Remove the inline comment on the onToggleWidget declaration so the file uses the
PeerInfo type without an implementation comment; specifically update the
MediaCallContext.ts declaration for onToggleWidget (currently typed as
onToggleWidget: (peerInfo?: PeerInfo) => void) by deleting the preceding comment
line, keeping consistency with MediaCallContextType and
MediaCallExternalContextType.

}

log(...what: any[]): void {
// Replace any[] with unknown[] for better type safety - allows any value but forces type checking
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove the implementation comment.

This comment should be removed. The type change from any[] to unknown[] is self-explanatory and doesn't need an inline rationale.

Proposed fix
-	// Replace any[] with unknown[] for better type safety - allows any value but forces type checking
 	log(...what: unknown[]): void {

As per coding guidelines, **/*.{ts,tsx,js}: "Avoid code comments in the implementation".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Replace any[] with unknown[] for better type safety - allows any value but forces type checking
log(...what: unknown[]): void {
🤖 Prompt for AI Agents
In `@packages/ui-voip/src/context/MediaCallLogger.ts` at line 11, Remove the
inline implementation comment at the top of MediaCallLogger.ts (the comment
explaining the reason for replacing any[] with unknown[]); delete that comment
line(s) and leave the type change intact (e.g., where any[] was updated to
unknown[]) so MediaCallLogger and its exported functions/classes contain no
implementation-level comments per the coding guidelines.

Comment on lines +12 to 26
log(...what: unknown[]): void {
this.isDebug && console.log(...what);
}

debug(...what: any[]): void {
debug(...what: unknown[]): void {
this.isDebug && console.debug(...what);
}

error(...what: any[]): void {
error(...what: unknown[]): void {
console.error(...what);
}

warn(...what: any[]): void {
warn(...what: unknown[]): void {
console.warn(...what);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for IMediaSignalLogger interface definition
rg "interface IMediaSignalLogger" --type ts -A 20

Repository: RocketChat/Rocket.Chat

Length of output: 522


🏁 Script executed:

#!/bin/bash
# Also check if it's imported from node_modules
find node_modules/@rocket.chat/media-signaling -name "*.ts" -o -name "*.d.ts" 2>/dev/null | head -20

Repository: RocketChat/Rocket.Chat

Length of output: 48


🏁 Script executed:

#!/bin/bash
# Check the MediaCallLogger.ts file to see the actual import and class declaration
cat -n packages/ui-voip/src/context/MediaCallLogger.ts | head -30

Repository: RocketChat/Rocket.Chat

Length of output: 979


Fix type signature to match IMediaSignalLogger interface.

The IMediaSignalLogger interface declares methods with any[] parameters, but the implementation uses unknown[]. This is incompatible due to parameter contravariance—implementations must accept a type at least as wide as the interface declares, not narrower. The current code violates the interface contract and should cause a TypeScript error. Change all method parameters back to any[] to properly implement the interface.

🤖 Prompt for AI Agents
In `@packages/ui-voip/src/context/MediaCallLogger.ts` around lines 12 - 26, The
MediaCallLogger implementation uses parameter types unknown[] for
log/debug/error/warn which is narrower than the IMediaSignalLogger interface's
any[]; update the method signatures on class MediaCallLogger (methods log,
debug, error, warn) to accept any[] instead of unknown[] so the class correctly
implements the IMediaSignalLogger contract, adjusting all four method
declarations accordingly.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR aims to improve performance and code quality through database optimization, error handling improvements, type safety enhancements, and React performance optimizations. However, several significant issues were identified that need to be addressed before merging.

Changes:

  • Added compound database indexes for Users, Subscriptions, and Rooms models to optimize frequent query patterns
  • Replaced sequential Promise.all calls with bulkWrite operations in Subscriptions model
  • Implemented opt-in LRU caching system in BaseRaw for findOneById operations
  • Replaced empty catch blocks with proper error logging in AppManager and Client components
  • Enhanced type safety by replacing any types with unknown or specific types
  • Added React.memo to several avatar and UI components
  • Updated package versions from RC to stable releases

Reviewed changes

Copilot reviewed 14 out of 15 changed files in this pull request and generated 13 comments.

Show a summary per file
File Description
yarn.lock Updated @rocket.chat/message-parser from 0.31.33-rc.0 to 0.31.33 and @rocket.chat/ui-contexts from 27.0.0-rc.2 to 27.0.0
packages/ui-voip/src/context/MockedMediaCallProvider.tsx Removed console.log statements and replaced with explanatory comments for mocked provider
packages/ui-voip/src/context/MediaCallLogger.ts Changed parameter types from any[] to unknown[] for better type safety
packages/ui-voip/src/context/MediaCallContext.ts Replaced any type with PeerInfo type for onToggleWidget parameter
packages/models/src/models/Users.ts Added three compound indexes for livechat, user type, and role-based queries
packages/models/src/models/Subscriptions.ts Added compound indexes and refactored setBlockedByRoomId/unsetBlockedByRoomId to use bulkWrite
packages/models/src/models/Rooms.ts Added compound indexes for team and room type queries with archive filters
packages/models/src/models/BaseRaw.ts Implemented opt-in LRU cache system with TTL for findOneById operations and cache invalidation
packages/apps-engine/src/server/AppManager.ts Added error logging to previously empty catch block for app removal notification
ee/apps/ddp-streamer/src/Client.ts Added error logging to catch blocks and improved type safety for callback parameters
apps/meteor/client/components/message/content/attachments/structure/AttachmentAuthorAvatar.tsx Added React.memo wrapper
apps/meteor/client/components/avatar/UserAvatarEditor/UserAvatarSuggestions.tsx Added React.memo wrapper and refactored data access pattern
apps/meteor/client/components/UserInfo/UserInfoAvatar.tsx Added React.memo wrapper
apps/meteor/client/components/UserAutoCompleteMultiple/UserAvatarChip.tsx Added React.memo wrapper
apps/meteor/app/livechat/server/startup.ts Added proper type annotations to registerGuest.patch and error logging to setUserStatusLivechatIf

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


const AttachmentAuthorAvatar = ({ url }: { url: string }): ReactElement => <BaseAvatar url={url} size='x24' />;
// Memoized to prevent re-renders when parent updates but URL hasn't changed
const AttachmentAuthorAvatar = memo(({ url }: { url: string }): ReactElement => <BaseAvatar url={url} size='x24' />);
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

Memoizing a simple wrapper component that spreads all props is unlikely to provide performance benefits. React.memo performs a shallow comparison of props on every render, which can actually be more expensive than just re-rendering this trivial component. The BaseAvatar component itself should handle its own memoization if needed. Consider removing memo unless profiling shows this component causes performance issues.

Copilot uses AI. Check for mistakes.

const UserInfoAvatar = (props: ComponentProps<typeof UserAvatar>): ReactElement => <UserAvatar size='x332' {...props} />;
// Memoized to prevent re-renders when parent component updates
const UserInfoAvatar = memo((props: ComponentProps<typeof UserAvatar>): ReactElement => <UserAvatar size='x332' {...props} />);
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

Same as AttachmentAuthorAvatar - memoizing a simple prop-spreading wrapper is likely not beneficial and adds unnecessary overhead. The shallow props comparison in React.memo could be more expensive than re-rendering this trivial component.

Copilot uses AI. Check for mistakes.
Comment thread apps/meteor/app/livechat/server/startup.ts Outdated
Comment on lines +1682 to +1685
return [
{ acknowledged: result.ok === 1, matchedCount: result.nMatched, modifiedCount: result.nModified, upsertedCount: 0, upsertedId: null },
{ acknowledged: result.ok === 1, matchedCount: result.nMatched, modifiedCount: result.nModified, upsertedCount: 0, upsertedId: null },
] as UpdateResult[];
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

Same issue as above - both UpdateResult entries use identical aggregated values instead of per-operation results. This doesn't accurately represent the individual operation results, which could cause incorrect behavior in unblockUser.ts where individual modifiedCount values are checked.

Copilot uses AI. Check for mistakes.
{ key: { 'v._id': 1, 'open': 1 } },
// Compound indexes for common query patterns - performance optimization
{
key: { 'u._id': 1, archived: 1 },
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

The index on 'archived' field for Subscriptions uses the field name directly, but in the codebase queries typically check for 'archived': { $ne: true } (see getBadgeCount method). MongoDB indexes on boolean-like fields where you're checking for inequality or absence are generally not efficient. Consider if this index will actually be used by your query patterns.

Suggested change
key: { 'u._id': 1, archived: 1 },
key: { 'u._id': 1 },
partialFilterExpression: { archived: { $ne: true } },

Copilot uses AI. Check for mistakes.
Comment on lines +151 to +152
this.chain = this.chain.then(() => server.call(this, packet)).catch((error) => {
console.error('DDP method call failed', { method: packet.method, error: error.message });
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

Similar issue - accessing error.message without verifying the error is an Error instance. This pattern is repeated multiple times in this file and could fail at runtime if the error is not an Error object.

Copilot uses AI. Check for mistakes.
Comment on lines +12 to +15
// Memoized to prevent re-renders when parent updates but props haven't changed
const UserAvatarSuggestions = memo(function UserAvatarSuggestions({ disabled, onSelectOne }: UserAvatarSuggestionsProps) {
const { data } = useUserAvatarSuggestions();
const suggestions = (data ?? []) as UserAvatarSuggestion[];
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

The type assertion '(data ?? []) as UserAvatarSuggestion[]' is unsafe. If useUserAvatarSuggestions returns data of a different type, this will cause runtime errors. The previous '= []' default was safer. Consider either fixing the type definition of useUserAvatarSuggestions to return UserAvatarSuggestion[] | undefined, or keeping the original pattern.

Suggested change
// Memoized to prevent re-renders when parent updates but props haven't changed
const UserAvatarSuggestions = memo(function UserAvatarSuggestions({ disabled, onSelectOne }: UserAvatarSuggestionsProps) {
const { data } = useUserAvatarSuggestions();
const suggestions = (data ?? []) as UserAvatarSuggestion[];
const isUserAvatarSuggestion = (item: unknown): item is UserAvatarSuggestion =>
Boolean(item && typeof item === 'object' && 'service' in item && 'blob' in item);
// Memoized to prevent re-renders when parent updates but props haven't changed
const UserAvatarSuggestions = memo(function UserAvatarSuggestions({ disabled, onSelectOne }: UserAvatarSuggestionsProps) {
const { data } = useUserAvatarSuggestions();
const suggestions: UserAvatarSuggestion[] = Array.isArray(data) ? data.filter(isUserAvatarSuggestion) : [];

Copilot uses AI. Check for mistakes.
Comment on lines +99 to +103
// Initialize cache if enabled - provides 90%+ speedup for cached reads
if (options?.cache?.enabled) {
this.cache = new Map();
this.cacheTTL = options.cache.ttl ?? this.cacheTTL;
this.cacheMaxSize = options.cache.maxSize ?? this.cacheMaxSize;
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

The cache implementation is added to BaseRaw but is never actually enabled in any model. Without any model passing cache.enabled = true in the ModelOptions, this entire caching system is dead code that adds complexity without providing any benefit. Either enable caching for specific models or remove this implementation.

Suggested change
// Initialize cache if enabled - provides 90%+ speedup for cached reads
if (options?.cache?.enabled) {
this.cache = new Map();
this.cacheTTL = options.cache.ttl ?? this.cacheTTL;
this.cacheMaxSize = options.cache.maxSize ?? this.cacheMaxSize;
// Initialize cache - enabled by default unless explicitly disabled
const cacheEnabled = options?.cache?.enabled ?? true;
if (cacheEnabled) {
this.cache = new Map();
if (options?.cache) {
this.cacheTTL = options.cache.ttl ?? this.cacheTTL;
this.cacheMaxSize = options.cache.maxSize ?? this.cacheMaxSize;
}

Copilot uses AI. Check for mistakes.
Comment on lines +170 to +181
/**
* Invalidate cache entry when document is updated
*/
protected invalidateCache(id: string | string[]): void {
if (!this.cache) return;

if (Array.isArray(id)) {
id.forEach((i) => this.cache?.delete(i));
} else {
this.cache.delete(id);
}
}
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

The cache doesn't get invalidated on updateMany, deleteOne, deleteMany, insertOne, or insertMany operations. This creates a risk of serving stale data. A comprehensive caching strategy should handle all mutation operations, not just updateOne and findOneAndUpdate.

Copilot uses AI. Check for mistakes.
Comment on lines +348 to +351
// Invalidate cache for updated document
if ('_id' in filter) {
this.invalidateCache(filter._id as string);
}
Copy link

Copilot AI Feb 12, 2026

Choose a reason for hiding this comment

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

The cache invalidation logic only handles '_id' in the filter, but updateOne can be called with various filters (not just by _id). This means cache entries could become stale when documents are updated via other query filters. The cache invalidation should either be more comprehensive or the caching should only apply to documents fetched by ID.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
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.

2 issues found across 15 files

Prompt for AI agents (all issues)

Check if these issues are valid — if so, understand the root cause of each and fix them.


<file name="packages/models/src/models/Subscriptions.ts">

<violation number="1" location="packages/models/src/models/Subscriptions.ts:1659">
P0: The properties `result.nMatched` and `result.nModified` do not exist on MongoDB driver 6.x `BulkWriteResult`. Use `result.matchedCount` and `result.modifiedCount` instead. This will cause the returned `UpdateResult` objects to have `undefined` values.</violation>
</file>

<file name="packages/models/src/models/BaseRaw.ts">

<violation number="1" location="packages/models/src/models/BaseRaw.ts:156">
P2: The cache eviction is FIFO (first-in-first-out), not LRU as documented. The `getCached` method doesn't update the entry position in the Map, so frequently accessed entries can be evicted while recently inserted but cold entries survive. Either update the documentation to accurately describe FIFO behavior, or implement true LRU by re-inserting entries on read hits.</violation>
</file>

Since this is your first cubic review, here's how it works:

  • cubic automatically reviews your code and comments on bugs and improvements
  • Teach cubic by replying to its comments. cubic learns from your replies and gets better over time
  • Add one-off context when rerunning by tagging @cubic-dev-ai with guidance or docs links (including llms.txt)
  • Ask questions if you need clarification on any suggestion

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Comment thread packages/models/src/models/Subscriptions.ts Outdated
Comment thread packages/models/src/models/BaseRaw.ts Outdated
IvanNilash1206 and others added 3 commits February 12, 2026 17:02
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

⚠️ Outside diff range comments (1)
packages/models/src/models/BaseRaw.ts (1)

362-368: ⚠️ Potential issue | 🔴 Critical

Cache is never invalidated for updateMany, deleteOne, deleteMany, removeById, removeByIds, or findOneAndDelete.

Any document removed or bulk-updated through these methods will remain in the cache with stale (or deleted) data until the TTL expires. For a 5-second window, findOneById will happily return a document that no longer exists or has outdated fields.

At minimum, removeById and findOneAndDelete operate on a known _id and should invalidate:

🐛 Proposed fix (partial — covers the straightforward cases)
 removeById(_id: T['_id'], options?: { session?: ClientSession }): Promise<DeleteResult> {
+    this.invalidateCache(_id);
     return this.deleteOne({ _id } as Filter<T>, { session: options?.session });
 }

 removeByIds(ids: T['_id'][]): Promise<DeleteResult> {
+    this.invalidateCache(ids);
     return this.deleteMany({ _id: { $in: ids } } as unknown as Filter<T>);
 }

For findOneAndDelete, invalidate using the returned doc's _id. For deleteOne/deleteMany, consider invalidating when _id is present in the filter (same guard as updateOne). For updateMany, you may need to clear the entire cache or skip invalidation and accept the TTL window — but document the decision.

Also applies to: 396-401, 404-433, 435-464, 466-501

🤖 Fix all issues with AI agents
In `@packages/models/src/models/BaseRaw.ts`:
- Around line 253-257: The code unconditionally casts query._id to string when
calling invalidateCache, which breaks when _id is a query operator or object;
change the guard around the cache invalidation in BaseRaw methods (the block
using result, '_id' in query and calling this.invalidateCache(query._id as
string)) to only call invalidateCache when the id is a plain string (e.g., add
typeof query._id === 'string') or another explicit scalar identifier you
support, and apply the same safe-check pattern to the other occurrences in
updateOne that currently cast query._id to string.
🧹 Nitpick comments (3)
apps/meteor/app/livechat/server/startup.ts (1)

29-29: Typing with any doesn't meaningfully improve type safety.

The added annotations are all any, which offers no additional compile-time checking. If the makeFunction patch API supports it, consider using the concrete types from omni-core (e.g., RegisterGuestType for newData, ILivechatVisitor | null for the return).

packages/models/src/models/BaseRaw.ts (1)

99-104: Consider validating ttl and maxSize to prevent misconfiguration.

A ttl of 0 or a negative maxSize would silently break the cache (entries always expired, or Map growing unbounded since size >= maxSize is never true for negative values). A simple guard in the constructor would make this more robust.

packages/models/src/models/Subscriptions.ts (1)

70-79: Remove inline comments and the redundant background: true option.

The background: true option is ignored in MongoDB 4.2+; all index builds are automatically optimized. Additionally, the three inline comments at lines 70, 75, and 79 violate the coding guideline to avoid comments in implementation files.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 95a111f and 262c4fb.

📒 Files selected for processing (3)
  • apps/meteor/app/livechat/server/startup.ts
  • packages/models/src/models/BaseRaw.ts
  • packages/models/src/models/Subscriptions.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/startup.ts
  • packages/models/src/models/BaseRaw.ts
  • packages/models/src/models/Subscriptions.ts
🧠 Learnings (6)
📚 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 **/*.{ts,tsx,js} : Avoid code comments in the implementation

Applied to files:

  • packages/models/src/models/BaseRaw.ts
📚 Learning: 2025-11-07T14:50:33.544Z
Learnt from: KevLehman
Repo: RocketChat/Rocket.Chat PR: 37423
File: packages/i18n/src/locales/en.i18n.json:18-18
Timestamp: 2025-11-07T14:50:33.544Z
Learning: Rocket.Chat settings: in apps/meteor/ee/server/settings/abac.ts, the Abac_Cache_Decision_Time_Seconds setting uses invalidValue: 0 as the fallback when ABAC is unlicensed. With a valid license, admins can still set the value to 0 to intentionally disable the ABAC decision cache.

Applied to files:

  • packages/models/src/models/BaseRaw.ts
📚 Learning: 2025-11-10T19:06:20.146Z
Learnt from: MartinSchoeler
Repo: RocketChat/Rocket.Chat PR: 37408
File: apps/meteor/client/views/admin/ABAC/useRoomAttributeOptions.tsx:53-69
Timestamp: 2025-11-10T19:06:20.146Z
Learning: In the Rocket.Chat repository, do not provide suggestions or recommendations about code sections marked with TODO comments. The maintainers have already identified these as future work and external reviewers lack the full context about implementation plans and timing.

Applied to files:

  • packages/models/src/models/BaseRaw.ts
📚 Learning: 2026-01-17T01:51:47.764Z
Learnt from: tassoevan
Repo: RocketChat/Rocket.Chat PR: 38219
File: packages/core-typings/src/cloud/Announcement.ts:5-6
Timestamp: 2026-01-17T01:51:47.764Z
Learning: In packages/core-typings/src/cloud/Announcement.ts, the AnnouncementSchema.createdBy field intentionally overrides IBannerSchema.createdBy (object with _id and optional username) with a string enum ['cloud', 'system'] to match existing runtime behavior. This is documented as technical debt with a FIXME comment at apps/meteor/app/cloud/server/functions/syncWorkspace/handleCommsSync.ts:53 and should not be flagged as an error until the runtime behavior is corrected.

Applied to files:

  • packages/models/src/models/BaseRaw.ts
  • packages/models/src/models/Subscriptions.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:

  • packages/models/src/models/Subscriptions.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:

  • packages/models/src/models/Subscriptions.ts
🧬 Code graph analysis (1)
apps/meteor/app/livechat/server/startup.ts (2)
packages/omni-core/src/visitor/create.ts (1)
  • registerGuest (16-116)
apps/meteor/app/livechat/server/lib/utils.ts (1)
  • setUserStatusLivechatIf (35-54)
🪛 Biome (2.3.14)
packages/models/src/models/BaseRaw.ts

[error] 177-177: This callback passed to forEach() iterable method should not return a value.

Either remove this return or remove the returned value.

(lint/suspicious/useIterableCallbackReturn)

🔇 Additional comments (2)
apps/meteor/app/livechat/server/startup.ts (1)

136-139: Good improvement replacing silent catch with structured error logging.

The .catch() now logs meaningful context (userId, error), which will help diagnose agent-status update failures during logout.

One nit: the comment on line 136 should be removed per the coding guidelines. As per coding guidelines, **/*.{ts,tsx,js}: "Avoid code comments in the implementation".

packages/models/src/models/BaseRaw.ts (1)

266-284: Cache integration in findOneById looks correct for the scoped case.

Caching only when options is not provided is a sound heuristic — it avoids caching projected/partial documents. The null-vs-undefined distinction (getCached returns undefined for miss, null for cached-not-found) is handled properly.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +253 to +257

// Invalidate cache for updated document
if (result && '_id' in query) {
this.invalidateCache(query._id as string);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Unsafe cast of filter._id to string — will cache-corrupt when _id is a query operator.

filter._id can be { $in: [...] }, { $ne: ... }, or any other MongoDB operator expression. Casting it to string and passing it to invalidateCache will use "[object Object]" as the cache key, failing to invalidate the real entries and polluting the map.

Guard with a type check before invalidating:

🐛 Proposed fix
-		if (result && '_id' in query) {
-			this.invalidateCache(query._id as string);
+		if (result && '_id' in query && typeof query._id === 'string') {
+			this.invalidateCache(query._id);
 		}

Apply the same pattern in updateOne (lines 349 and 356):

-		if ('_id' in filter) {
-			this.invalidateCache(filter._id as string);
+		if ('_id' in filter && typeof filter._id === 'string') {
+			this.invalidateCache(filter._id);
 		}

Also applies to: 347-358

🤖 Prompt for AI Agents
In `@packages/models/src/models/BaseRaw.ts` around lines 253 - 257, The code
unconditionally casts query._id to string when calling invalidateCache, which
breaks when _id is a query operator or object; change the guard around the cache
invalidation in BaseRaw methods (the block using result, '_id' in query and
calling this.invalidateCache(query._id as string)) to only call invalidateCache
when the id is a plain string (e.g., add typeof query._id === 'string') or
another explicit scalar identifier you support, and apply the same safe-check
pattern to the other occurrences in updateOne that currently cast query._id to
string.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants