From bd1bcb5aba46b4aaa39efdd261b35bf2ac2755c6 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Tue, 23 Jun 2026 08:22:42 -0300 Subject: [PATCH] fix(dashboard): remove dead unconditional useLiveRequests call in HomePageClient (#4759, #4745, #4596) (#4761) Co-authored-by: Diego Rodrigues de Sa e Souza --- CHANGELOG.md | 1 + .../(dashboard)/dashboard/HomePageClient.tsx | 7 ++- ...home-page-client-hook-imports-4759.test.ts | 55 +++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 tests/unit/home-page-client-hook-imports-4759.test.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 322f0ec9ec..cd9ccb77f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ _In development — bullets added per PR; finalized at release._ - **db-backups**: make the database-import size cap configurable via `OMNIROUTE_DB_IMPORT_MAX_MB` (default 100 MB, 4 GB ceiling) so backups larger than 100 MB can be restored; error message now points to the env var and to VACUUM (#4719). - **Onboarding**: add the missing `onboarding.tiers` step-title translation so the setup wizard no longer crashes with `MISSING_MESSAGE: onboarding.tiers` (#4698). - **deepseek-web**: fold `role:"tool"` results into the single-prompt transcript (`messagesToPrompt`) so tool outputs reach the model instead of being silently dropped when a follow-up turn omits the `tools[]` array (#4712). +- **Dashboard**: remove the dead, unconditional `useLiveRequests()` call from `HomePageClient.tsx` — it crashed the `/home` page in production builds with `ReferenceError: useLiveRequests is not defined` (#4759, #4745) and opened the live-dashboard WebSocket even when Provider Topology was hidden (#4596). The live feed remains owned by the settings-gated `HomeProviderTopologySection`. --- diff --git a/src/app/(dashboard)/dashboard/HomePageClient.tsx b/src/app/(dashboard)/dashboard/HomePageClient.tsx index 1461f2215b..37ff007715 100644 --- a/src/app/(dashboard)/dashboard/HomePageClient.tsx +++ b/src/app/(dashboard)/dashboard/HomePageClient.tsx @@ -114,8 +114,11 @@ export default function HomePageClient({ machineId }: HomePageClientProps) { Array<{ id?: string; prefix?: string; name?: string }> >([]); - // Live in-flight requests for Provider Topology pulse animation (#3507) - const { activeRequests: liveActiveRequests } = useLiveRequests(); + // The live in-flight request feed for the Provider Topology pulse animation is owned by + // , which subscribes to it (gated by the `enabled` prop) + // only when the topology is actually shown. HomePageClient must NOT open its own + // unconditional live socket: the binding here was unused (ReferenceError in prod, + // #4759/#4745) and the socket opened even when topology was hidden (#4596). const [versionInfo, setVersionInfo] = useState(null); const [updating, setUpdating] = useState(false); diff --git a/tests/unit/home-page-client-hook-imports-4759.test.ts b/tests/unit/home-page-client-hook-imports-4759.test.ts new file mode 100644 index 0000000000..af836e21dc --- /dev/null +++ b/tests/unit/home-page-client-hook-imports-4759.test.ts @@ -0,0 +1,55 @@ +// #4759 / #4745 — the /home dashboard crashed in production builds with +// "ReferenceError: useLiveRequests is not defined" because HomePageClient.tsx called the +// hook but never imported it (and the binding was unused). typecheck:core does not cover +// the Next dashboard .tsx files and ESLint's no-undef is off (TS owns that), so nothing +// but `next build` caught it. +// +// #4596 — that same top-level useLiveRequests() call opened the live-dashboard WebSocket +// unconditionally, even when Provider Topology was hidden. The live feed is owned by the +// settings-gated (useLiveRequests({ enabled })), so +// HomePageClient must not open its own unconditional socket. +// +// Fix: remove the dead, unconditional useLiveRequests() call from HomePageClient. These +// static guards lock both regressions down. +import test from "node:test"; +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; + +const src = readFileSync( + fileURLToPath( + new URL("../../src/app/(dashboard)/dashboard/HomePageClient.tsx", import.meta.url) + ), + "utf8" +); + +function collect(re: RegExp, group = 1): Set { + return new Set([...src.matchAll(re)].map((m) => m[group])); +} + +const called = collect(/\b(use[A-Z]\w*)\s*\(/g); +const importedNames = new Set( + [...src.matchAll(/import\s+(?:type\s+)?\{([^}]*)\}/g)].flatMap((m) => + m[1].split(",").map((s) => s.trim().split(/\s+as\s+/).pop()!.trim()) + ) +); +const declared = collect(/(?:function|const|let|var)\s+(use[A-Z]\w*)/g); + +test("every hook called in HomePageClient.tsx is imported or declared (#4759/#4745)", () => { + const missing = [...called].filter((h) => !importedNames.has(h) && !declared.has(h)); + assert.deepEqual( + missing, + [], + `hooks used without import (ReferenceError in production build): ${missing.join(", ")}` + ); +}); + +test("HomePageClient does not open its own unconditional live socket (#4596)", () => { + // The live feed belongs to the settings-gated . A bare + // useLiveRequests( call at the page level would open the WebSocket even when topology + // is hidden — exactly the regression #4596 reports (and the dead binding behind #4759). + assert.ok( + !/\buseLiveRequests\s*\(/.test(src), + "HomePageClient.tsx must not call useLiveRequests directly; delegate it to HomeProviderTopologySection" + ); +});