Skip to content

feat: favicons + custom port during dev#109

Merged
juliusmarminge merged 6 commits intomainfrom
theo/favicons
Feb 27, 2026
Merged

feat: favicons + custom port during dev#109
juliusmarminge merged 6 commits intomainfrom
theo/favicons

Conversation

@t3dotgg
Copy link
Copy Markdown
Member

@t3dotgg t3dotgg commented Feb 27, 2026

image

Note

Add per-project favicon fetching via wsServer.createServer GET /api/project-favicon and change default dev web port to 5733 across desktop, server, web, and scripts

Introduce a server route that discovers and serves project favicons and add a sidebar UI that loads them; update dev tooling and configs to use port 5733 by default.

📍Where to Start

Start with the HTTP handler for GET /api/project-favicon in wsServer.ts.

Changes since #109 opened

  • Implemented /api/project-favicon route handler with path validation, MIME type mapping, and favicon resolution from both well-known locations and source file metadata [2eaed39]
  • Changed isLatestTurnSettled in session-logic module to return false whenever orchestrationStatus is running, regardless of whether active turn matches latest turn [2eaed39]
  • Added four additional asset path candidates to favicon resolution [91dc895]
  • Modified favicon extraction regex patterns to support attribute-order-independent matching [0147ab7]

Macroscope summarized 08ea222.

Summary by CodeRabbit

  • New Features

    • Project favicons displayed in the sidebar with automatic discovery and a new server endpoint to serve them.
    • Sidebar fallback icon when no favicon is available.
  • Tests

    • Added tests for project favicon discovery/serving and for latest-turn settlement logic.
  • Chores

    • Default development server port updated from 5173 to 5733 across dev configs.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 27, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a project favicon discovery and serving endpoint on the server, a Sidebar ProjectFavicon React component that fetches and displays project favicons, a workspace favicon validation ES module test script, new session-turn settlement logic, and updates the default dev server port from 5173 to 5733 across configs and scripts.

Changes

Cohort / File(s) Summary
Port Migration
README.md, apps/desktop/scripts/dev-electron.mjs, apps/server/package.json, apps/web/vite.config.ts, scripts/dev-runner.mjs
Changed default dev web port from 5173 to 5733 in docs, configs, and dev scripts.
Favicon Feature — Backend
apps/server/src/projectFaviconRoute.ts, apps/server/src/projectFaviconRoute.test.ts, apps/server/src/wsServer.ts
Added tryHandleProjectFaviconRequest export implementing two-phase favicon discovery (well-known candidates, then scan source files), MIME and caching handling, 400/500/404 responses; wired handler into HTTP server request path before dev-redirect; added comprehensive tests.
Favicon Feature — Frontend
apps/web/src/components/Sidebar.tsx
Added getServerHttpOrigin() and serverHttpOrigin, new ProjectFavicon({ cwd }) component, imported FolderIcon, and injected favicon rendering into project list UI with load/error fallback.
Favicon Validation Script
.test-favicon.mjs
New ES module test script that scans a project for favicon candidates and inspects known source files to extract and verify referenced favicon hrefs.
Session Logic & ChatView
apps/web/src/session-logic.ts, apps/web/src/session-logic.test.ts, apps/web/src/components/ChatView.tsx
Added exported isLatestTurnSettled with tests; integrated latestTurnSettled checks into ChatView to gate effects and rendering until the latest turn is settled.

Sequence Diagram(s)

sequenceDiagram
    participant Browser
    participant DevHTTP as Dev HTTP Server
    participant FS as FileSystem
    participant Vite as Vite Dev Server

    Browser->>DevHTTP: GET /api/project-favicon?cwd=/project
    activate DevHTTP
    DevHTTP->>FS: Phase 1 — check well-known favicon candidate paths
    FS-->>DevHTTP: found / not found

    alt Found in Phase 1
        DevHTTP->>FS: read file bytes
        FS-->>DevHTTP: file bytes
    else Not found
        DevHTTP->>FS: Phase 2 — read known source files (index.html, app files)
        FS-->>DevHTTP: file contents
        DevHTTP->>DevHTTP: extract href via regex and normalize
        DevHTTP->>FS: resolve href in public/ then project root
        FS-->>DevHTTP: resolved path / not found

        alt Resolved
            DevHTTP->>FS: read file bytes
            FS-->>DevHTTP: file bytes
        end
    end

    DevHTTP-->>Browser: 200 + favicon bytes (with Content-Type/Cache) or 404
    deactivate DevHTTP

    Browser->>Vite: dev server requests (unchanged) — dev redirect occurs elsewhere if not handled
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: favicons + custom port during dev' accurately summarizes the two main changes: adding favicon support and updating the development port.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch theo/favicons

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

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
apps/server/src/wsServer.ts (1)

314-320: Consider validating cwd is an absolute path to limit exposure.

The cwd parameter is user-provided and passed directly to path.join. While the path traversal checks (lines 330, 376) prevent serving files outside projectCwd, a malicious client could potentially probe the filesystem structure by checking response times or errors for arbitrary paths.

Consider adding validation that cwd is an absolute path and optionally checking it against known project directories:

Suggested validation
       const projectCwd = url.searchParams.get("cwd");
       if (!projectCwd) {
         res.writeHead(400, { "Content-Type": "text/plain" });
         res.end("Missing cwd parameter");
         return;
       }
+
+      // Validate cwd is an absolute path
+      if (!path.isAbsolute(projectCwd)) {
+        res.writeHead(400, { "Content-Type": "text/plain" });
+        res.end("Invalid cwd parameter: must be absolute path");
+        return;
+      }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/server/src/wsServer.ts` around lines 314 - 320, Validate the
user-provided cwd before using it: in the request handler that checks
url.pathname === "/api/project-favicon", ensure projectCwd (from
url.searchParams.get("cwd")) is an absolute path (use path.isAbsolute
equivalent) and reject non-absolute values with a 400; additionally, if your app
has a list or resolver for allowed project roots (e.g., knownProjectDirs or a
function that maps project IDs to paths), verify projectCwd is inside that
allowlist or canonicalize and compare against allowed roots before calling
path.join and the favicon-serving code, returning 403/400 for invalid/untrusted
paths.
apps/web/src/components/Sidebar.tsx (1)

167-185: Consider showing a placeholder during loading state.

The component currently renders a hidden <img> during loading, which means no visual feedback until the image loads or errors. While this avoids flicker for fast loads, it leaves an empty space in the layout.

If this is intentional to prevent layout shift, the current approach is fine. Otherwise, consider showing the FolderIcon as a placeholder during loading:

Optional: Show placeholder during loading
 function ProjectFavicon({ cwd }: { cwd: string }) {
   const [status, setStatus] = useState<"loading" | "loaded" | "error">("loading");

   const src = `${serverHttpOrigin}/api/project-favicon?cwd=${encodeURIComponent(cwd)}`;

-  if (status === "error") {
+  if (status === "error" || status === "loading") {
     return <FolderIcon className="size-3.5 shrink-0 text-muted-foreground/50" />;
   }

   return (
-    <img
-      src={src}
-      alt=""
-      className={`size-3.5 shrink-0 rounded-sm object-contain ${status === "loading" ? "hidden" : ""}`}
-      onLoad={() => setStatus("loaded")}
-      onError={() => setStatus("error")}
-    />
+    <>
+      <img
+        src={src}
+        alt=""
+        className="size-3.5 shrink-0 rounded-sm object-contain"
+        onLoad={() => setStatus("loaded")}
+        onError={() => setStatus("error")}
+      />
+    </>
   );
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/Sidebar.tsx` around lines 167 - 185, ProjectFavicon
currently renders a hidden <img> while loading which provides no visual
feedback; update the ProjectFavicon component so it shows the FolderIcon
placeholder when status is "loading" (and still when "error"), and only render
the <img> when status is "loaded" (keeping the existing onLoad/onError handlers
to flip status and preserving the size classes to avoid layout shift). This
change should be made inside the ProjectFavicon function, toggling between
FolderIcon and the img based on the status state.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.test-favicon.mjs:
- Around line 52-54: The test file .test-favicon.mjs contains hardcoded
developer-specific paths in the test(...) calls; remove those absolute paths and
make the inputs configurable instead: either delete the two lines and only call
test(process.cwd()), or change the test invocation to read paths from CLI args
(process.argv) or an environment variable (e.g., process.env.TEST_PATHS) and
fall back to process.cwd(), or move these checks into a proper test with mocked
directories; update the test(...) calls to use the chosen configurable source so
no machine-specific absolute paths remain.

---

Nitpick comments:
In `@apps/server/src/wsServer.ts`:
- Around line 314-320: Validate the user-provided cwd before using it: in the
request handler that checks url.pathname === "/api/project-favicon", ensure
projectCwd (from url.searchParams.get("cwd")) is an absolute path (use
path.isAbsolute equivalent) and reject non-absolute values with a 400;
additionally, if your app has a list or resolver for allowed project roots
(e.g., knownProjectDirs or a function that maps project IDs to paths), verify
projectCwd is inside that allowlist or canonicalize and compare against allowed
roots before calling path.join and the favicon-serving code, returning 403/400
for invalid/untrusted paths.

In `@apps/web/src/components/Sidebar.tsx`:
- Around line 167-185: ProjectFavicon currently renders a hidden <img> while
loading which provides no visual feedback; update the ProjectFavicon component
so it shows the FolderIcon placeholder when status is "loading" (and still when
"error"), and only render the <img> when status is "loaded" (keeping the
existing onLoad/onError handlers to flip status and preserving the size classes
to avoid layout shift). This change should be made inside the ProjectFavicon
function, toggling between FolderIcon and the img based on the status state.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3a43047 and 8d23482.

📒 Files selected for processing (8)
  • .test-favicon.mjs
  • README.md
  • apps/desktop/scripts/dev-electron.mjs
  • apps/server/package.json
  • apps/server/src/wsServer.ts
  • apps/web/src/components/Sidebar.tsx
  • apps/web/vite.config.ts
  • scripts/dev-runner.mjs

Comment thread .test-favicon.mjs Outdated
Comment thread apps/server/src/wsServer.ts Outdated

// Serve project favicons from their working directories.
// Handled before the dev redirect so it works in both modes.
if (url.pathname === "/api/project-favicon") {
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.

🟡 Medium src/wsServer.ts:314

The /api/project-favicon endpoint bypasses auth (only WebSocket upgrades check authToken) and reads user-controlled paths into memory without size limits. Consider adding auth validation and a file size check before fs.readFile to prevent memory exhaustion attacks.

🚀 Reply "fix it for me" or copy this AI Prompt for your agent:
In file apps/server/src/wsServer.ts around line 314:

The `/api/project-favicon` endpoint bypasses auth (only WebSocket upgrades check `authToken`) and reads user-controlled paths into memory without size limits. Consider adding auth validation and a file size check before `fs.readFile` to prevent memory exhaustion attacks.

Evidence trail:
apps/server/src/wsServer.ts lines 306-391 (HTTP server handler for /api/project-favicon - no auth check), lines 749-762 (WebSocket upgrade auth check using authToken), lines 288-303 (serveFaviconFile function with fs.readFile and no size limit), lines 315 (user-controlled cwd from query params), commit REVIEWED_COMMIT

Comment thread .test-favicon.mjs
Comment thread .test-favicon.mjs
Comment thread apps/server/src/wsServer.ts
t3dotgg and others added 3 commits February 27, 2026 14:50
- Add `isLatestTurnSettled` to avoid treating an active running turn as complete
- Use the guard in `ChatView` for visited timestamps and completion summary/divider rendering
- Add unit tests covering running, ready, and incomplete-timestamp cases
- move `/api/project-favicon` handling into a dedicated server route with coverage for root and source-linked icons
- wire `wsServer` to use the new route helper
- treat any running session as unsettled in `isLatestTurnSettled` to prevent stale latest-turn banners, with test coverage
Copy link
Copy Markdown

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/server/src/wsServer.ts`:
- Around line 315-331: The code trusts the user-supplied projectCwd and uses
brittle string-prefix containment checks in tryCandidates and trySourceFiles,
allowing path traversal; fix by normalizing/resolving and validating projectCwd
and candidate paths before any filesystem access: call
path.resolve/path.normalize on projectCwd and each candidate, ensure projectCwd
is absolute and exists (fs.stat) and then verify containment with path.relative
(reject if relative starts with '..' or resolves to outside projectCwd), and
replace string-prefix checks (e.g., candidate.startsWith(projectCwd + path.sep))
with this robust containment check in the functions tryCandidates and
trySourceFiles and where FAVICON_CANDIDATES are used.
- Around line 295-298: When handling the file read error in
apps/server/src/wsServer.ts (the branch that checks readErr after reading a
file), change the response to return 404 when readErr.code === 'ENOENT' instead
of a generic 500; otherwise keep the 500 behavior. Specifically, in the block
that currently does "if (readErr) { res.writeHead(500...); res.end('Read
error'); }", inspect readErr.code and call res.writeHead(404, { "Content-Type":
"text/plain" }) with a "Not Found" body for ENOENT, and fall back to 500 for
other errors. Ensure you reference the existing variables readErr and res so the
change is localized.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 48a4b2d and 08ea222.

📒 Files selected for processing (11)
  • .test-favicon.mjs
  • README.md
  • apps/desktop/scripts/dev-electron.mjs
  • apps/server/package.json
  • apps/server/src/wsServer.ts
  • apps/web/src/components/ChatView.tsx
  • apps/web/src/components/Sidebar.tsx
  • apps/web/src/session-logic.test.ts
  • apps/web/src/session-logic.ts
  • apps/web/vite.config.ts
  • scripts/dev-runner.mjs
🚧 Files skipped from review as they are similar to previous changes (7)
  • scripts/dev-runner.mjs
  • .test-favicon.mjs
  • apps/web/src/components/Sidebar.tsx
  • apps/server/package.json
  • apps/web/vite.config.ts
  • README.md
  • apps/desktop/scripts/dev-electron.mjs

Comment thread apps/server/src/wsServer.ts Outdated
Comment on lines +295 to +298
if (readErr) {
res.writeHead(500, { "Content-Type": "text/plain" });
res.end("Read error");
return;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Return 404 for ENOENT read races instead of generic 500.

If a file disappears between stat and read, this path currently reports a server error; that should be a not-found response.

🩹 Suggested error mapping
       if (readErr) {
-        res.writeHead(500, { "Content-Type": "text/plain" });
-        res.end("Read error");
+        const code = (readErr as NodeJS.ErrnoException).code;
+        const status = code === "ENOENT" ? 404 : 500;
+        res.writeHead(status, { "Content-Type": "text/plain" });
+        res.end(status === 404 ? "Not Found" : "Read error");
         return;
       }
📝 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
if (readErr) {
res.writeHead(500, { "Content-Type": "text/plain" });
res.end("Read error");
return;
if (readErr) {
const code = (readErr as NodeJS.ErrnoException).code;
const status = code === "ENOENT" ? 404 : 500;
res.writeHead(status, { "Content-Type": "text/plain" });
res.end(status === 404 ? "Not Found" : "Read error");
return;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/server/src/wsServer.ts` around lines 295 - 298, When handling the file
read error in apps/server/src/wsServer.ts (the branch that checks readErr after
reading a file), change the response to return 404 when readErr.code ===
'ENOENT' instead of a generic 500; otherwise keep the 500 behavior.
Specifically, in the block that currently does "if (readErr) {
res.writeHead(500...); res.end('Read error'); }", inspect readErr.code and call
res.writeHead(404, { "Content-Type": "text/plain" }) with a "Not Found" body for
ENOENT, and fall back to 500 for other errors. Ensure you reference the existing
variables readErr and res so the change is localized.

Comment thread apps/server/src/wsServer.ts Outdated
Comment thread apps/server/src/wsServer.ts
Copy link
Copy Markdown

@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

🧹 Nitpick comments (3)
apps/web/src/session-logic.test.ts (1)

156-202: Add coverage for the remaining isLatestTurnSettled branches.

The new cases cover the running-session regression well, but this suite still misses explicit checks for session === null (should be true when timestamps are complete) and completedAt === null (should be false).

Suggested test additions
 describe("isLatestTurnSettled", () => {
@@
   it("returns false when turn timestamps are incomplete", () => {
@@
     ).toBe(false);
   });
+
+  it("returns true when session is null and latest turn is complete", () => {
+    expect(isLatestTurnSettled(latestTurn, null)).toBe(true);
+  });
+
+  it("returns false when completedAt is missing", () => {
+    expect(
+      isLatestTurnSettled(
+        {
+          turnId: TurnId.makeUnsafe("turn-1"),
+          startedAt: "2026-02-27T21:10:00.000Z",
+          completedAt: null,
+        },
+        null,
+      ),
+    ).toBe(false);
+  });
 });

Based on learnings: "For integration tests: Prefer deterministic inputs and explicit state checks; avoid relying on logs or timing assumptions."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/session-logic.test.ts` around lines 156 - 202, Add two missing
test cases for isLatestTurnSettled: one where session is null and the latestTurn
has both startedAt and completedAt (expect true), and one where
latestTurn.completedAt is null with any session (expect false). Locate the
describe("isLatestTurnSettled") suite in session-logic.test.ts and add an
it(...) that passes session === null with the complete latestTurn timestamps and
asserts true, and another it(...) that passes a latestTurn with completedAt:
null (startedAt present) and asserts false; reference TurnId.makeUnsafe as used
in other cases to construct turnIds.
apps/server/src/projectFaviconRoute.ts (2)

5-10: Consider adding .jpeg extension support.

The MIME type mapping handles .jpg but not .jpeg, which is a valid alternative extension for JPEG images. Some projects may use .jpeg.

Proposed fix
 const FAVICON_MIME_TYPES: Record<string, string> = {
   ".png": "image/png",
   ".jpg": "image/jpeg",
+  ".jpeg": "image/jpeg",
   ".svg": "image/svg+xml",
   ".ico": "image/x-icon",
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/server/src/projectFaviconRoute.ts` around lines 5 - 10, The
FAVICON_MIME_TYPES mapping in projectFaviconRoute.ts is missing the ".jpeg"
extension; update the FAVICON_MIME_TYPES constant to include the ".jpeg":
"image/jpeg" entry so the route/function that reads this map
(FAVICON_MIME_TYPES) correctly recognizes both .jpg and .jpeg favicon files.

82-92: Consider validating projectCwd against the known projects registry for defense-in-depth.

The cwd query parameter accepts any filesystem path. While isPathWithinProject prevents directory traversal within a selected project, it doesn't restrict which project directories can be targeted. The system maintains a registry of known projects in the projection snapshot (snapshot.projects with workspaceRoot comparison). Validating the incoming cwd against this registry would add an extra layer of security.

Since this is a local development server, the current implementation is acceptable, but validation against known workspaces would be a beneficial safeguard against misconfigured clients or unintended filesystem access.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/server/src/projectFaviconRoute.ts` around lines 82 - 92, Validate the
incoming cwd in tryHandleProjectFaviconRequest against the known projects
registry: look up snapshot.projects and ensure there is a project whose
workspaceRoot equals (or is a parent of) the provided projectCwd (or use
isPathWithinProject to confirm containment), and if no match is found respond
with 400 and an error body; update the early-check that currently only verifies
presence of projectCwd to also perform this registry check so only known
workspace roots are accepted.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/server/src/projectFaviconRoute.test.ts`:
- Around line 35-43: The promise wrapper around server.listen is wrong because
the listen callback never receives an Error; instead attach an 'error' event
listener to the same server before calling server.listen and use the 'listening'
event (or the zero-arg listen callback) to resolve. Concretely: in the Promise
created around server.listen (the code that calls server.listen(0, "127.0.0.1",
...)), remove the error parameter check in the callback, add
server.once('error', err => { cleanup listeners; reject(err); }) before calling
server.listen, and on the listen callback (or server.once('listening', ...))
remove the error listener and resolve; also ensure you remove the listening
handler on error to avoid leaks.

In `@apps/server/src/projectFaviconRoute.ts`:
- Around line 43-45: The LINK_ICON_HTML_RE currently assumes rel appears before
href so tags like <link href="..." rel="icon"> are missed; update
LINK_ICON_HTML_RE to be order-independent by using lookahead assertions that
ensure the element has a rel=("icon"|"shortcut icon") attribute and a href=...
attribute in any order (e.g., a pattern using
<link\b(?=[^>]*\brel=...)(?=[^>]*\bhref=...)[^>]*> and capture the href value).
Do the same for LINK_ICON_OBJ_RE (use lookaheads to require both rel and href in
any order within the object-like text) or replace the regex approach with a
simple DOM-like parser if preferred; ensure the updated regexes still capture
the href value and remain case-insensitive like the originals.

---

Nitpick comments:
In `@apps/server/src/projectFaviconRoute.ts`:
- Around line 5-10: The FAVICON_MIME_TYPES mapping in projectFaviconRoute.ts is
missing the ".jpeg" extension; update the FAVICON_MIME_TYPES constant to include
the ".jpeg": "image/jpeg" entry so the route/function that reads this map
(FAVICON_MIME_TYPES) correctly recognizes both .jpg and .jpeg favicon files.
- Around line 82-92: Validate the incoming cwd in tryHandleProjectFaviconRequest
against the known projects registry: look up snapshot.projects and ensure there
is a project whose workspaceRoot equals (or is a parent of) the provided
projectCwd (or use isPathWithinProject to confirm containment), and if no match
is found respond with 400 and an error body; update the early-check that
currently only verifies presence of projectCwd to also perform this registry
check so only known workspace roots are accepted.

In `@apps/web/src/session-logic.test.ts`:
- Around line 156-202: Add two missing test cases for isLatestTurnSettled: one
where session is null and the latestTurn has both startedAt and completedAt
(expect true), and one where latestTurn.completedAt is null with any session
(expect false). Locate the describe("isLatestTurnSettled") suite in
session-logic.test.ts and add an it(...) that passes session === null with the
complete latestTurn timestamps and asserts true, and another it(...) that passes
a latestTurn with completedAt: null (startedAt present) and asserts false;
reference TurnId.makeUnsafe as used in other cases to construct turnIds.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 08ea222 and 2eaed39.

📒 Files selected for processing (5)
  • apps/server/src/projectFaviconRoute.test.ts
  • apps/server/src/projectFaviconRoute.ts
  • apps/server/src/wsServer.ts
  • apps/web/src/session-logic.test.ts
  • apps/web/src/session-logic.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/web/src/session-logic.ts
  • apps/server/src/wsServer.ts

Comment on lines +35 to +43
await new Promise<void>((resolve, reject) => {
server.listen(0, "127.0.0.1", (error?: Error) => {
if (error) {
reject(error);
return;
}
resolve();
});
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🌐 Web query:

Node.js http.Server.listen() callback signature error parameter

💡 Result:

http.Server extends net.Server, and server.listen(..., callback) simply adds callback as a listener for the 'listening' event—it is not an error-first callback and receives no parameters:

server.listen(3000, () => {
  // no (err) argument here
  console.log('listening');
});

If binding fails (e.g., EADDRINUSE, EACCES), you must handle it via the 'error' event:

server.on('error', (err) => { /* handle err */ });
server.listen(3000);

Sources: Node.js net.Server.listen() docs (callback is a 'listening' listener) [1]; Node.js http.Server.listen() is identical to net.Server.listen() [2].


🏁 Script executed:

# Find and examine the projectFaviconRoute.test.ts file
find . -name "projectFaviconRoute.test.ts" -type f

Repository: pingdotgg/t3code

Length of output: 105


🏁 Script executed:

# Read the projectFaviconRoute.test.ts file, focusing on the relevant section
cat -n ./apps/server/src/projectFaviconRoute.test.ts | head -70 | tail -50

Repository: pingdotgg/t3code

Length of output: 1693


server.listen callback does not receive an error parameter—errors are emitted via the 'error' event.

The Node.js server.listen() callback signature is () => void. It is registered as a listener for the 'listening' event and receives no parameters. Binding errors (e.g., EADDRINUSE, EACCES) are emitted via the 'error' event on the server, not passed to the callback. The if (error) check will never be true, and binding failures will not be caught.

Proposed fix to handle errors correctly
   await new Promise<void>((resolve, reject) => {
+    server.once("error", reject);
-    server.listen(0, "127.0.0.1", (error?: Error) => {
-      if (error) {
-        reject(error);
-        return;
-      }
+    server.listen(0, "127.0.0.1", () => {
       resolve();
     });
   });
📝 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
await new Promise<void>((resolve, reject) => {
server.listen(0, "127.0.0.1", (error?: Error) => {
if (error) {
reject(error);
return;
}
resolve();
});
});
await new Promise<void>((resolve, reject) => {
server.once("error", reject);
server.listen(0, "127.0.0.1", () => {
resolve();
});
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/server/src/projectFaviconRoute.test.ts` around lines 35 - 43, The
promise wrapper around server.listen is wrong because the listen callback never
receives an Error; instead attach an 'error' event listener to the same server
before calling server.listen and use the 'listening' event (or the zero-arg
listen callback) to resolve. Concretely: in the Promise created around
server.listen (the code that calls server.listen(0, "127.0.0.1", ...)), remove
the error parameter check in the callback, add server.once('error', err => {
cleanup listeners; reject(err); }) before calling server.listen, and on the
listen callback (or server.once('listening', ...)) remove the error listener and
resolve; also ensure you remove the listening handler on error to avoid leaks.

Comment thread apps/server/src/projectFaviconRoute.ts Outdated
- Update favicon regex parsing to support rel/href in either order
- Add route tests for HTML link tags and object-style metadata with href-first ordering
Copy link
Copy Markdown

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

🧹 Nitpick comments (1)
apps/server/src/projectFaviconRoute.ts (1)

5-10: Consider adding .jpeg alias for completeness.

The MIME type map includes .jpg but not .jpeg. Both extensions are common for JPEG images.

Proposed addition
 const FAVICON_MIME_TYPES: Record<string, string> = {
   ".png": "image/png",
   ".jpg": "image/jpeg",
+  ".jpeg": "image/jpeg",
   ".svg": "image/svg+xml",
   ".ico": "image/x-icon",
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/server/src/projectFaviconRoute.ts` around lines 5 - 10, The
FAVICON_MIME_TYPES map is missing the ".jpeg" alias; update the const
FAVICON_MIME_TYPES to include an entry for ".jpeg": "image/jpeg" alongside
".jpg" so lookups for ".jpeg" return the correct MIME type (modify the
FAVICON_MIME_TYPES Record in projectFaviconRoute.ts where it's declared).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/server/src/projectFaviconRoute.ts`:
- Around line 5-10: The FAVICON_MIME_TYPES map is missing the ".jpeg" alias;
update the const FAVICON_MIME_TYPES to include an entry for ".jpeg":
"image/jpeg" alongside ".jpg" so lookups for ".jpeg" return the correct MIME
type (modify the FAVICON_MIME_TYPES Record in projectFaviconRoute.ts where it's
declared).

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2eaed39 and 0147ab7.

📒 Files selected for processing (2)
  • apps/server/src/projectFaviconRoute.test.ts
  • apps/server/src/projectFaviconRoute.ts

@juliusmarminge juliusmarminge merged commit e6c5a4a into main Feb 27, 2026
3 checks passed
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