Skip to content

fix(common-settings-bridge): clear lint and harden message validation#368

Open
rezk2ll wants to merge 3 commits into
devfrom
fix-lint-issues
Open

fix(common-settings-bridge): clear lint and harden message validation#368
rezk2ll wants to merge 3 commits into
devfrom
fix-lint-issues

Conversation

@rezk2ll
Copy link
Copy Markdown
Member

@rezk2ll rezk2ll commented May 26, 2026

Summary

The whole packages/common-settings-bridge package now passes biome ci cleanly. #367 merged with a "we'll clean it up later" compromise; this is the cleanup. Along the way, replacing the as any casts in the message handler with real runtime narrowing uncovered two latent bugs that the type-erasure had been hiding.

The handler typed timestamp and version as numbers but relied on a falsy/null check, so any JSON producer could send 1e1000 (parses to Infinity) and slip past validation. An Infinity timestamp then crashed new Date(...).toISOString() inside the info log, escaped the validation try/catch, and turned the intended ack-and-drop into a retry-then-DLQ storm. A non-finite version made every comparison in shouldApplyUpdate return false and silently discarded legitimate updates as "stale". Both guards now use Number.isFinite.

Also splits the "payload is not an object" case out of UserIdNotProvidedError so the two failure modes show up differently in logs, and refactors saveSettings to take a meta object since six positional parameters tripped useMaxParams.

The lint CI is still red overall because the rest of the monorepo (apps, other packages) has its own pre-existing errors, but the package this PR touches is clean.

rezk2ll added 2 commits May 26, 2026 18:24
Removes `any` casts from the AMQP bridge tests and the `as unknown as
CommonSettingsMessage` cast in the message handler. `validateMessage`
now accepts `Record<string, unknown>` and narrows fields at runtime,
matching the actual shape the lib hands us.
`typeof x === "number"` lets NaN and Infinity through, both of which JSON
producers can reach via `1e1000` / `-1e1000`. An Infinity timestamp crashed
`new Date(...).toISOString()` inside the info log, escaping the validation
try/catch and turning the intended ack-and-drop into a retry-then-DLQ storm.
A NaN/-Infinity version silently lost legitimate updates because every
comparison in shouldApplyUpdate returns false. Switch both guards to
`Number.isFinite` and split the payload-not-object error from
UserIdNotProvidedError so the two failure modes are distinguishable in logs.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

Review Change Stack

📝 Walkthrough

Fundamental flaw
Loose runtime validation allowed non-finite numeric values (NaN, Infinity, -Infinity) and unstructured payloads to slip through. That could crash date formatting (escaping validation handling and causing retry/DLQ storms) or silently make shouldApplyUpdate comparisons fail and drop legitimate updates.

Systemic data-flow and core logic changes

  • All AMQP message parsing now happens at the boundary via validateMessage(message: Record<string, unknown>) which performs strict runtime narrowing and returns a well-shaped ParsedMessage.
    • request_id: required non-empty string.
    • timestamp/version: validated with Number.isFinite (rejects NaN/Infinity/-Infinity); version defaults to 1 if non-finite.
    • payload: must be a plain JSON object (not null, not an array); matrix_id validated as non-empty string.
    • source defaults defensively.
  • The message handler uses validateMessage instead of unsafe casts and explicitly catches MessageParseError and UserIdNotProvidedError to ack-and-drop without downstream side effects.
  • saveSettings call site changed: bridge now calls settingsRepository.saveSettings(userId, payload, { version, timestamp, requestId, isNewUser }) (single meta object).
  • SettingsRepository API changed to saveSettings(userId, payload, meta: SaveSettingsMeta) and internally destructures meta; duplicate-key handling now checks for a code property more defensively.
  • Logging formatting adjusted during subscription rollback to be a multi-line template string.

Deprecated APIs / removed legacy code

  • Removed unsafe casts: all occurrences of as any and as unknown as CommonSettingsMessage were eliminated in favor of runtime guards.
  • Replaced loose typeof x === "number" checks with Number.isFinite guards.
  • Introduced MessageParseError to separate payload-not-object failures from UserIdNotProvidedError.
  • Exported new SaveSettingsMeta interface and changed SettingsRepository.saveSettings signature to accept a meta object.

Test and typing hardening

  • Jest mocks upgraded from loose any to jest.Mocked types.
  • Handler tests now accept unknown and exercise explicit runtime-guard cases: non-object payload roots (null, 42, []), non-finite timestamps/versions, and that non-finite version defaults to 1 and is persisted.
  • Tests for repository updated to assert the new saveSettings meta-object shape.

Explicitly ignored technical debt

  • Validation remains handcrafted runtime narrowing on Record<string, unknown> rather than adopting a compile-time/JSON-schema schema parser. This is an explicit trade-off for runtime robustness at the message boundary instead of introducing a schema library and its ergonomics.

Walkthrough

This PR adds runtime validation for AMQP messages in CommonSettingsBridge, replaces positional persistence args with a single exported SaveSettingsMeta, and tightens Jest mock typings and test fixtures across bridge, repository, index, and profile-updater tests.

Changes

Message Validation Type Safety

Layer / File(s) Summary
Runtime message validation implementation
packages/common-settings-bridge/src/bridge.ts
Remove compile-time message cast and implement validateMessage(message: Record<string, unknown>) with runtime checks for request_id (non-empty string), finite timestamp, and object payload with non-empty matrix_id. Handler calls validator directly and saves settings using a meta object; subscription rollback log reformatted.
SettingsRepository API and implementation
packages/common-settings-bridge/src/settings-repository.ts
Add exported SaveSettingsMeta and change saveSettings to accept meta: SaveSettingsMeta; destructure meta internally and guard duplicate-key detection by checking error.code at runtime.
SettingsRepository tests (meta-object calls)
packages/common-settings-bridge/src/settings-repository.test.ts
Refactor tests to call and assert saveSettings(..., meta) object shape; inline construction of duplicate-key error via Object.assign; instantiate repository with typed mock casts.
Bridge test infrastructure and typed mocks
packages/common-settings-bridge/src/bridge.test.ts
Introduce local jest.Mocked helper types, rebuild typed mocks in beforeEach using jest.MockedClass casts, derive admin APIs mock from mocked Intent, and fix BridgeConfig typing in start tests.
Message validation edge-case testing
packages/common-settings-bridge/src/bridge.test.ts
Retype handler to (message: unknown) => Promise<void> and add tests asserting ack-and-drop for non-object payloads, no repo/updater calls on non-finite timestamps, and defaulting non-finite version to 1 which is persisted.
Index test suite & entrypoint
packages/common-settings-bridge/src/index.test.ts, packages/common-settings-bridge/src/index.ts
Type subscription handler as RabbitMQMessageHandler, create a logger mock with configure, add TestMessageOverrides and typed createTestMessage using Partial<ISettingsPayload>, replace casts with new CommonSettingsBridge(config), and re-export ./types from package entrypoint.
Matrix profile-updater error shaping & tests
packages/common-settings-bridge/src/matrix-profile-updater.ts, packages/common-settings-bridge/src/matrix-profile-updater.test.ts
Add asMatrixError(err: unknown) to normalize thrown values; update catch blocks to use mxErr for errcode/message and retry logic. Tighten mocked Matrix client, Intent, and Logger typings in tests.

Possibly related PRs

  • linagora/ToM-server#367: Both PRs update RabbitMQ message handling and validation in CommonSettingsBridge for the new @linagora/rabbitmq-client integration.

Suggested labels

bug, javascript, priority::normal, severity::major

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 100.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: linting fixes and hardened message validation with proper numeric guards.
Description check ✅ Passed The description provides clear rationale for changes, technical details about bugs fixed, and implementation decisions, but lacks explicit checklist completion status.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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.

@nx-cloud
Copy link
Copy Markdown

nx-cloud Bot commented May 26, 2026

View your CI Pipeline Execution ↗ for commit 3bed56c

Command Status Duration Result
nx affected -t test ✅ Succeeded 11s View ↗
nx affected -t build ✅ Succeeded 4s View ↗

☁️ Nx Cloud last updated this comment at 2026-05-26 17:38:42 UTC

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


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 02895298-0dff-4074-b94c-e411ccd9287f

📥 Commits

Reviewing files that changed from the base of the PR and between 93e11b7 and 3bed56c.

📒 Files selected for processing (3)
  • packages/common-settings-bridge/src/bridge.test.ts
  • packages/common-settings-bridge/src/bridge.ts
  • packages/common-settings-bridge/src/index.test.ts
📜 Review details
⏰ 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: Test / Test Affected Packages
  • GitHub Check: Docs / Update Documentation
🧰 Additional context used
📓 Path-based instructions (5)
packages/common-settings-bridge/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (packages/common-settings-bridge/AGENTS.md)

Use express for the HTTP server implementation of the Application Service endpoint

Files:

  • packages/common-settings-bridge/src/bridge.ts
  • packages/common-settings-bridge/src/bridge.test.ts
  • packages/common-settings-bridge/src/index.test.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.{js,ts,jsx,tsx}: Code must follow the philosophy of simplicity over cleverness - junior developers should understand code in 30 seconds, avoid metaprogramming, deep generics, decorator magic, and prefer readable for loops over complex chains like .reduce().flatMap().filter()
Code must follow the philosophy of explicit over implicit - dependencies must be injected not imported globally, errors must be typed not caught-and-rethrown, data flows must be traceable through function signatures
Code comments must explain why, never what - the code itself explains what
Do not use // comments to disable code - delete dead code instead, as Git has history

Files:

  • packages/common-settings-bridge/src/bridge.ts
  • packages/common-settings-bridge/src/bridge.test.ts
  • packages/common-settings-bridge/src/index.test.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.{ts,tsx}: Code must follow the philosophy of boundaries over conventions - use module facades enforced by lint rules instead of comments, prefer #private fields over naming conventions, prefer TypeScript types over JSDoc comments
Do not introduce new any types in TypeScript - warnings are existing tech debt, new ones are blockers

Enforce TypeScript strict mode across all packages in ToM-Server

**/*.{ts,tsx}: Use PascalCase for types, interfaces, classes, and enums
Return types must be explicit on all non-trivial functions
Every function must return a meaningful value (void is forbidden)
Use ActionResult type for functions that perform actions with no natural data return
Use Result<T, E> type for functions that produce data, making failure first-class
any type is forbidden without exception
as unknown as T double casting is forbidden without exception
Use unknown over any for data from external sources (HTTP, JSON.parse, databases)
Prefer type for unions and intersections, interface for object shapes
Avoid TypeScript enum; use string union types for internal values
Provide type guards and validation helpers for string unions that cross system boundaries
Use Result or ActionResult for expected, domain-meaningful failures (not exceptions)
Use Error.cause when wrapping or rethrowing errors to preserve the original error chain
Type caught errors correctly using instanceof checks (not casting unknown to Error)
Prefer functions and plain objects over classes; use classes only for genuine encapsulation with private mutable state and lifecycle
Do not use static-only classes to group related functions; use named exports instead
Classes must have a single responsibility
@ts-ignore and @ts-expect-error must have a written explanation

Files:

  • packages/common-settings-bridge/src/bridge.ts
  • packages/common-settings-bridge/src/bridge.test.ts
  • packages/common-settings-bridge/src/index.test.ts
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use ES modules (type: module) throughout the ToM-Server project

Files:

  • packages/common-settings-bridge/src/bridge.ts
  • packages/common-settings-bridge/src/bridge.test.ts
  • packages/common-settings-bridge/src/index.test.ts
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit inference engine (CODING_STYLE.md)

**/*.{js,ts,tsx,jsx}: Use 2 spaces for indentation (not 4, not tabs)
Opening braces must go on the same line (never on a new line)
Use trailing commas in multi-line structures
Semicolons are required on all statements
Maximum line length is 120 characters (hard limit)
Use camelCase for variables and functions
Use SCREAMING_SNAKE_CASE only for module-level primitive constants that never change
Boolean variables must use is/has/can prefix (e.g., isLoading, hasPermission, canRetry)
Do not abbreviate variable names beyond accepted list (i, j, e, err, ctx, req, res)
Functions must have a single responsibility (no 'and' in function names)
Use function declarations for named, standalone, exported units; use arrow functions for callbacks and inline helpers
Maximum 5 function arguments (use options object for more parameters)
Keep functions short (25-40 lines maximum, fit on one screen without scrolling)
Recursion must be tail-call only, or use an iterative loop instead
Maximum 2 levels of nesting (no level 3)
Use early returns to reduce nesting and establish preconditions
Avoid else after a return
Throw exceptions only for invariant violations and programming errors
Catch errors at system boundaries (HTTP handlers, job runners, event listeners), not deep in business logic
Never swallow errors silently (no empty catch blocks)
finally is for cleanup only, not for conditional logic
Import from specific files, not barrel exports
Organize imports in order: Node built-ins, external packages, internal absolute paths, internal relative paths (with blank lines between groups)
Comments must explain why, not what
Document function contracts in JSDoc, not implementation mechanics
TODO comments must have an owner name and ticket reference
Prefer async/await over .then() chains
Run independent async operations in parallel using Promise.all
Never fire-and-forget async operations without a .catch() handler
Use === instead of == (never use loose equality)
Do not use mutabl...

Files:

  • packages/common-settings-bridge/src/bridge.ts
  • packages/common-settings-bridge/src/bridge.test.ts
  • packages/common-settings-bridge/src/index.test.ts
🧠 Learnings (1)
📚 Learning: 2026-03-31T07:26:27.898Z
Learnt from: pm-McFly
Repo: linagora/ToM-server PR: 355
File: packages/matrix-identity-server/src/db/index.ts:413-413
Timestamp: 2026-03-31T07:26:27.898Z
Learning: When reviewing TypeScript code in this repo, follow Biome’s `noDoubleEquals` rule: do not use loose equality (`== null` / `!= null`) as a shorthand. For nullish checks, use explicit strict comparisons instead (e.g., `value === null || value === undefined` or `value !== null && value !== undefined`).

Applied to files:

  • packages/common-settings-bridge/src/bridge.ts
  • packages/common-settings-bridge/src/bridge.test.ts
  • packages/common-settings-bridge/src/index.test.ts
🔇 Additional comments (1)
packages/common-settings-bridge/src/index.test.ts (1)

2-4: LGTM!

Also applies to: 6-6, 16-19, 94-94, 96-101, 143-148, 175-203, 209-209, 226-226, 245-245, 265-265, 289-289, 308-308, 337-337, 353-353, 366-366, 389-389, 417-417, 453-453, 466-466, 478-478

Comment thread packages/common-settings-bridge/src/bridge.test.ts
Comment on lines +54 to 55
if (!Number.isFinite(timestamp)) {
throw new MessageParseError("Message missing required timestamp field");
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 | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

In JavaScript/TypeScript, what exact millisecond range is valid for new Date(ms).toISOString()before it throwsRangeError?

💡 Result:

In JavaScript/TypeScript, new Date(ms).toISOString() throws a RangeError when the Date’s internal time value is not a finite millisecond time (e.g., NaN/Infinity) or when it is outside the supported time-value range. Exact millisecond bounds for the supported Date time value are: - Valid range: -8,640,000,000,000,000 ms through +8,640,000,000,000,000 ms (inclusive) - RangeError occurs for any ms strictly less than -8,640,000,000,000,000 or strictly greater than +8,640,000,000,000,000 (and for non-finite timestamps). [1][2] So, the “valid ms range” before toISOString() throws is: - -8640000000000000 <= ms <= 8640000000000000 [2][1] Notes: - toISOString() throws when the Date object is invalid (MDN describes this as “Thrown if the date is invalid”). [3] - The ECMAScript spec states the time value range supported by a Date is -8,640,000,000,000,000 to +8,640,000,000,000,000 milliseconds. [1]

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo_root="$(pwd)"
file="packages/common-settings-bridge/src/bridge.ts"

# Show relevant sections without dumping the whole file
echo "=== validateMessage area (lines ~1-120) ==="
nl -ba "$file" | sed -n '1,140p'

echo "=== other relevant areas (~230-320) ==="
nl -ba "$file" | sed -n '220,330p'

echo "=== formatTimestamp / usage area (~450-560) ==="
nl -ba "$file" | sed -n '440,560p'

Repository: linagora/ToM-server

Length of output: 168


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="packages/common-settings-bridge/src/bridge.ts"

echo "=== validateMessage area (lines ~1-180) ==="
cat -n "$file" | sed -n '1,180p'

echo "=== other relevant areas (~180-360) ==="
cat -n "$file" | sed -n '180,360p'

echo "=== formatTimestamp / usage area (~360-620) ==="
cat -n "$file" | sed -n '360,620p'

Repository: linagora/ToM-server

Length of output: 26029


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "saveSettings\\(" packages/common-settings-bridge/src
rg -n "timestamp" packages/common-settings-bridge/src/types.ts packages/common-settings-bridge/src/settings-repository.ts packages/common-settings-bridge/src -S

Repository: linagora/ToM-server

Length of output: 11976


Guard timestamp against JS Date out-of-range, not just finiteness.

Number.isFinite still lets finite millisecond values outside [-8_640_000_000_000_000, +8_640_000_000_000_000] through; new Date(ms).toISOString() will throw RangeError, and your formatTimestamp(timestamp) calls happen after validation (outside the validateMessage try/catch), so this can still cascade into retries/DLQ.

Suggested hardening
+const MAX_JS_DATE_MS = 8_640_000_000_000_000;
+
+function isValidTimestamp(value: unknown): value is number {
+  return (
+    typeof value === "number" &&
+    Number.isFinite(value) &&
+    Number.isInteger(value) &&
+    Math.abs(value) <= MAX_JS_DATE_MS
+  );
+}
+
 function validateMessage(message: Record<string, unknown>): ParsedMessage {
   const requestId = message.request_id;
   if (typeof requestId !== "string" || requestId.length === 0) {
     throw new MessageParseError("Message missing required request_id field");
   }
   const timestamp = message.timestamp;
-  if (!Number.isFinite(timestamp)) {
+  if (!isValidTimestamp(timestamp)) {
     throw new MessageParseError("Message missing required timestamp field");
   }
   const payload = message.payload;
@@
   return {
@@
-    timestamp: timestamp as number,
+    timestamp,
@@
   };
 }

mockUploadContent.mockResolvedValue("mxc://example.com/uploaded123");
// Mock global fetch for avatar downloads
global.fetch = jest.fn(() => Promise.resolve(createMockFetchResponse())) as any;
global.fetch = jest.fn(() => Promise.resolve(createMockFetchResponse())) as unknown as typeof fetch;
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 | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify no forbidden double-casts remain in this test file (and optionally repo-wide).
rg -n --type=ts '\bas\s+unknown\s+as\b' packages/common-settings-bridge/src/index.test.ts
rg -n --type=ts '\bas\s+unknown\s+as\b' packages/common-settings-bridge/src

Repository: linagora/ToM-server

Length of output: 1135


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="packages/common-settings-bridge/src/index.test.ts"
# Print around the flagged line
sed -n '120,170p' "$FILE" | cat -n

# Also locate and print createMockFetchResponse definition and its return type
rg -n "createMockFetchResponse" "$FILE"
python3 - <<'PY'
import re,sys,subprocess,os,shlex, textwrap, json
file="packages/common-settings-bridge/src/index.test.ts"
# Find line numbers for the function/const definition
import pathlib
p=pathlib.Path(file)
lines=p.read_text().splitlines()
for i,l in enumerate(lines,1):
    if "createMockFetchResponse" in l and ("function" in l or "const" in l or "let" in l or "async" in l):
        print("DEF_CANDIDATE", i, l.strip())
PY

Repository: linagora/ToM-server

Length of output: 2471


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE="packages/common-settings-bridge/src/index.test.ts"
# show top of file to see environment and imports
sed -n '1,80p' "$FILE" | cat -n
# show around flagged line
sed -n '100,160p' "$FILE" | cat -n

Repository: linagora/ToM-server

Length of output: 5435


🏁 Script executed:

#!/bin/bash
set -euo pipefail
FILE="packages/common-settings-bridge/src/index.test.ts"
# show top of file to see environment and imports
sed -n '1,80p' "$FILE" | cat -n
# show around flagged line
sed -n '100,160p' "$FILE" | cat -n

Repository: linagora/ToM-server

Length of output: 5435


Kill the forbidden as unknown as typeof fetch in packages/common-settings-bridge/src/index.test.ts

The global.fetch mock still uses a forbidden as unknown as T double-cast; replace it with a single-type assertion on the mock function.

Suggested fix
-  global.fetch = jest.fn(() => Promise.resolve(createMockFetchResponse())) as unknown as typeof fetch;
+  global.fetch = jest.fn(() => Promise.resolve(createMockFetchResponse())) as jest.MockedFunction<typeof fetch>;

Also, packages/common-settings-bridge/src/index.ts and packages/common-settings-bridge/src/bridge.test.ts still contain as unknown as casts and need the same treatment.

📝 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
global.fetch = jest.fn(() => Promise.resolve(createMockFetchResponse())) as unknown as typeof fetch;
global.fetch = jest.fn(() => Promise.resolve(createMockFetchResponse())) as jest.MockedFunction<typeof fetch>;

Replaces `any` and `(err as any)?.code` with proper `unknown` narrowing in
the profile updater and settings repository, bundles `saveSettings` extra
parameters into a `SaveSettingsMeta` object to satisfy `useMaxParams`, and
swaps `(MockLogger as any).configure` for `Object.assign` in the logger
mock. `biome ci` now exits 0 across `packages/common-settings-bridge`.
@rezk2ll rezk2ll changed the title fix(common-settings-bridge): clear leftover lint and harden message validation fix(common-settings-bridge): clear lint and harden message validation May 26, 2026
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: 2


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 79122871-8351-432b-9756-3b863a727bb3

📥 Commits

Reviewing files that changed from the base of the PR and between 3bed56c and cf919ca.

📒 Files selected for processing (7)
  • packages/common-settings-bridge/src/bridge.test.ts
  • packages/common-settings-bridge/src/bridge.ts
  • packages/common-settings-bridge/src/index.ts
  • packages/common-settings-bridge/src/matrix-profile-updater.test.ts
  • packages/common-settings-bridge/src/matrix-profile-updater.ts
  • packages/common-settings-bridge/src/settings-repository.test.ts
  • packages/common-settings-bridge/src/settings-repository.ts
📜 Review details
⏰ 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: Test / Test Affected Packages
  • GitHub Check: Docs / Update Documentation
🧰 Additional context used
📓 Path-based instructions (7)
packages/common-settings-bridge/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (packages/common-settings-bridge/AGENTS.md)

Use express for the HTTP server implementation of the Application Service endpoint

Files:

  • packages/common-settings-bridge/src/matrix-profile-updater.ts
  • packages/common-settings-bridge/src/matrix-profile-updater.test.ts
  • packages/common-settings-bridge/src/settings-repository.test.ts
  • packages/common-settings-bridge/src/settings-repository.ts
  • packages/common-settings-bridge/src/bridge.test.ts
  • packages/common-settings-bridge/src/bridge.ts
  • packages/common-settings-bridge/src/index.ts
**/*.{js,ts,jsx,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.{js,ts,jsx,tsx}: Code must follow the philosophy of simplicity over cleverness - junior developers should understand code in 30 seconds, avoid metaprogramming, deep generics, decorator magic, and prefer readable for loops over complex chains like .reduce().flatMap().filter()
Code must follow the philosophy of explicit over implicit - dependencies must be injected not imported globally, errors must be typed not caught-and-rethrown, data flows must be traceable through function signatures
Code comments must explain why, never what - the code itself explains what
Do not use // comments to disable code - delete dead code instead, as Git has history

Files:

  • packages/common-settings-bridge/src/matrix-profile-updater.ts
  • packages/common-settings-bridge/src/matrix-profile-updater.test.ts
  • packages/common-settings-bridge/src/settings-repository.test.ts
  • packages/common-settings-bridge/src/settings-repository.ts
  • packages/common-settings-bridge/src/bridge.test.ts
  • packages/common-settings-bridge/src/bridge.ts
  • packages/common-settings-bridge/src/index.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.{ts,tsx}: Code must follow the philosophy of boundaries over conventions - use module facades enforced by lint rules instead of comments, prefer #private fields over naming conventions, prefer TypeScript types over JSDoc comments
Do not introduce new any types in TypeScript - warnings are existing tech debt, new ones are blockers

Enforce TypeScript strict mode across all packages in ToM-Server

**/*.{ts,tsx}: Use PascalCase for types, interfaces, classes, and enums
Return types must be explicit on all non-trivial functions
Every function must return a meaningful value (void is forbidden)
Use ActionResult type for functions that perform actions with no natural data return
Use Result<T, E> type for functions that produce data, making failure first-class
any type is forbidden without exception
as unknown as T double casting is forbidden without exception
Use unknown over any for data from external sources (HTTP, JSON.parse, databases)
Prefer type for unions and intersections, interface for object shapes
Avoid TypeScript enum; use string union types for internal values
Provide type guards and validation helpers for string unions that cross system boundaries
Use Result or ActionResult for expected, domain-meaningful failures (not exceptions)
Use Error.cause when wrapping or rethrowing errors to preserve the original error chain
Type caught errors correctly using instanceof checks (not casting unknown to Error)
Prefer functions and plain objects over classes; use classes only for genuine encapsulation with private mutable state and lifecycle
Do not use static-only classes to group related functions; use named exports instead
Classes must have a single responsibility
@ts-ignore and @ts-expect-error must have a written explanation

Files:

  • packages/common-settings-bridge/src/matrix-profile-updater.ts
  • packages/common-settings-bridge/src/matrix-profile-updater.test.ts
  • packages/common-settings-bridge/src/settings-repository.test.ts
  • packages/common-settings-bridge/src/settings-repository.ts
  • packages/common-settings-bridge/src/bridge.test.ts
  • packages/common-settings-bridge/src/bridge.ts
  • packages/common-settings-bridge/src/index.ts
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use ES modules (type: module) throughout the ToM-Server project

Files:

  • packages/common-settings-bridge/src/matrix-profile-updater.ts
  • packages/common-settings-bridge/src/matrix-profile-updater.test.ts
  • packages/common-settings-bridge/src/settings-repository.test.ts
  • packages/common-settings-bridge/src/settings-repository.ts
  • packages/common-settings-bridge/src/bridge.test.ts
  • packages/common-settings-bridge/src/bridge.ts
  • packages/common-settings-bridge/src/index.ts
**/*.{js,ts,tsx,jsx}

📄 CodeRabbit inference engine (CODING_STYLE.md)

**/*.{js,ts,tsx,jsx}: Use 2 spaces for indentation (not 4, not tabs)
Opening braces must go on the same line (never on a new line)
Use trailing commas in multi-line structures
Semicolons are required on all statements
Maximum line length is 120 characters (hard limit)
Use camelCase for variables and functions
Use SCREAMING_SNAKE_CASE only for module-level primitive constants that never change
Boolean variables must use is/has/can prefix (e.g., isLoading, hasPermission, canRetry)
Do not abbreviate variable names beyond accepted list (i, j, e, err, ctx, req, res)
Functions must have a single responsibility (no 'and' in function names)
Use function declarations for named, standalone, exported units; use arrow functions for callbacks and inline helpers
Maximum 5 function arguments (use options object for more parameters)
Keep functions short (25-40 lines maximum, fit on one screen without scrolling)
Recursion must be tail-call only, or use an iterative loop instead
Maximum 2 levels of nesting (no level 3)
Use early returns to reduce nesting and establish preconditions
Avoid else after a return
Throw exceptions only for invariant violations and programming errors
Catch errors at system boundaries (HTTP handlers, job runners, event listeners), not deep in business logic
Never swallow errors silently (no empty catch blocks)
finally is for cleanup only, not for conditional logic
Import from specific files, not barrel exports
Organize imports in order: Node built-ins, external packages, internal absolute paths, internal relative paths (with blank lines between groups)
Comments must explain why, not what
Document function contracts in JSDoc, not implementation mechanics
TODO comments must have an owner name and ticket reference
Prefer async/await over .then() chains
Run independent async operations in parallel using Promise.all
Never fire-and-forget async operations without a .catch() handler
Use === instead of == (never use loose equality)
Do not use mutabl...

Files:

  • packages/common-settings-bridge/src/matrix-profile-updater.ts
  • packages/common-settings-bridge/src/matrix-profile-updater.test.ts
  • packages/common-settings-bridge/src/settings-repository.test.ts
  • packages/common-settings-bridge/src/settings-repository.ts
  • packages/common-settings-bridge/src/bridge.test.ts
  • packages/common-settings-bridge/src/bridge.ts
  • packages/common-settings-bridge/src/index.ts
packages/common-settings-bridge/src/index.ts

📄 CodeRabbit inference engine (packages/common-settings-bridge/AGENTS.md)

The src/index.ts file is the bridge entry point and Application Service handler

Files:

  • packages/common-settings-bridge/src/index.ts
**/index.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (CODING_STYLE.md)

Do not use barrel re-exports of entire directories

Files:

  • packages/common-settings-bridge/src/index.ts
🧠 Learnings (1)
📚 Learning: 2026-03-31T07:26:27.898Z
Learnt from: pm-McFly
Repo: linagora/ToM-server PR: 355
File: packages/matrix-identity-server/src/db/index.ts:413-413
Timestamp: 2026-03-31T07:26:27.898Z
Learning: When reviewing TypeScript code in this repo, follow Biome’s `noDoubleEquals` rule: do not use loose equality (`== null` / `!= null`) as a shorthand. For nullish checks, use explicit strict comparisons instead (e.g., `value === null || value === undefined` or `value !== null && value !== undefined`).

Applied to files:

  • packages/common-settings-bridge/src/matrix-profile-updater.ts
  • packages/common-settings-bridge/src/matrix-profile-updater.test.ts
  • packages/common-settings-bridge/src/settings-repository.test.ts
  • packages/common-settings-bridge/src/settings-repository.ts
  • packages/common-settings-bridge/src/bridge.test.ts
  • packages/common-settings-bridge/src/bridge.ts
  • packages/common-settings-bridge/src/index.ts
🔇 Additional comments (9)
packages/common-settings-bridge/src/bridge.ts (1)

347-352: LGTM!

packages/common-settings-bridge/src/settings-repository.ts (3)

7-17: LGTM!


246-247: LGTM!


264-267: LGTM!

packages/common-settings-bridge/src/matrix-profile-updater.test.ts (1)

6-9: LGTM!

Also applies to: 15-15

packages/common-settings-bridge/src/matrix-profile-updater.ts (1)

5-19: LGTM!

Also applies to: 113-121, 255-260, 300-307

packages/common-settings-bridge/src/settings-repository.test.ts (1)

15-18: LGTM!

Also applies to: 25-27, 176-177, 195-196, 219-221, 233-233, 259-259, 263-263, 273-275, 289-289

packages/common-settings-bridge/src/bridge.test.ts (1)

363-368: LGTM!

Also applies to: 453-458, 473-478

packages/common-settings-bridge/src/index.ts (1)

17-17: LGTM!

setAvatarUrl: jest.fn(),
matrixClient: mockMatrixClient,
} as any;
} as unknown as jest.Mocked<Intent>;
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 | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Read-only check for forbidden double casts in the touched test file
rg -nP 'as\s+unknown\s+as' packages/common-settings-bridge/src/matrix-profile-updater.test.ts

Repository: linagora/ToM-server

Length of output: 151


🏁 Script executed:

#!/bin/bash
set -euo pipefail
nl -ba packages/common-settings-bridge/src/matrix-profile-updater.test.ts | sed -n '15,60p'

Repository: linagora/ToM-server

Length of output: 106


🏁 Script executed:

#!/bin/bash
set -euo pipefail
cat -n packages/common-settings-bridge/src/matrix-profile-updater.test.ts | sed -n '20,55p'

Repository: linagora/ToM-server

Length of output: 1296


Kill the as unknown as ... double-casts in matrix-profile-updater.test.ts (type theater).

Those as unknown as jest.Mocked<Intent> and as unknown as jest.Mocked<Logger> assertions are still present at lines 29 and 44; replace them with narrow mock shapes (only the methods/properties this test uses).

Suggested refactor
-  let mockIntent: jest.Mocked<Intent>;
-  let mockLogger: jest.Mocked<Logger>;
+  type MockIntent = {
+    setDisplayName: jest.MockedFunction<(displayName: string) => Promise<void>>;
+    setAvatarUrl: jest.MockedFunction<(avatarUrl: string) => Promise<void>>;
+    matrixClient: MockMatrixClient;
+  };
+
+  type MockLogger = Pick<jest.Mocked<Logger>, "debug" | "info" | "warn" | "error">;
+
+  let mockIntent: MockIntent;
+  let mockLogger: MockLogger;
@@
-    mockIntent = {
+    mockIntent = {
       setDisplayName: jest.fn(),
       setAvatarUrl: jest.fn(),
       matrixClient: mockMatrixClient,
-    } as unknown as jest.Mocked<Intent>;
+    };
@@
-    mockLogger = {
+    mockLogger = {
       debug: jest.fn(),
       info: jest.fn(),
       warn: jest.fn(),
       error: jest.fn(),
-    } as unknown as jest.Mocked<Logger>;
+    };

Comment on lines +82 to +85
repository = new SettingsRepository(
mockDb as unknown as RealDatabase<UserSettingsTableName>,
mockLogger as unknown as RealLogger,
);
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 | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify no forbidden double-casts remain in this touched test file.
rg -n --type=ts '\bas unknown as\b' packages/common-settings-bridge/src/settings-repository.test.ts

# Expected after fix: no output.

Repository: linagora/ToM-server

Length of output: 176


Remove the remaining as unknown as double-casts in SettingsRepository test setup

packages/common-settings-bridge/src/settings-repository.test.ts (lines 83-84) still uses mockDb as unknown as RealDatabase<UserSettingsTableName> and mockLogger as unknown as RealLogger; replace the casts with properly typed constructor-argument types so there’s no forbidden as unknown as T chain.

@rezk2ll rezk2ll requested a review from pm-McFly May 26, 2026 19:06
@pm-McFly pm-McFly self-assigned this May 27, 2026
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.

2 participants