From 1ac8ee8cef68c3ad43e390cec0c69e068d55a8ae Mon Sep 17 00:00:00 2001 From: GitHub Copilot <223556219+Copilot@users.noreply.github.com> Date: Sun, 19 Jul 2026 10:06:37 -0400 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=8C=B1=20fix:=20log=20errors=20previo?= =?UTF-8?q?usly=20swallowed=20in=20silent=20catches=20(#21287)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: GitHub Copilot <223556219+Copilot@users.noreply.github.com> --- web/src/components/cards/cardRegistry.index.ts | 5 +++-- web/src/components/layout/SidebarCustomizer.tsx | 3 ++- web/src/hooks/useStellarSource.ts | 17 +++++++---------- web/src/lib/cache/cacheCore.ts | 13 +++++-------- 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/web/src/components/cards/cardRegistry.index.ts b/web/src/components/cards/cardRegistry.index.ts index 1a4b6cb767..7c02343a1b 100644 --- a/web/src/components/cards/cardRegistry.index.ts +++ b/web/src/components/cards/cardRegistry.index.ts @@ -1,4 +1,5 @@ import { isDynamicCardRegistered } from '../../lib/dynamic-cards/dynamicCardRegistry' +import { logger } from '../../lib/logger' import { registerAllDescriptorCards } from './cardDescriptors.registry' import { CARD_TITLES, CARD_DESCRIPTIONS, DEMO_EXEMPT_CARDS } from './cardMetadata' import { quantumCardRegistry } from './cardRegistry.quantum' @@ -83,7 +84,7 @@ registerAllDescriptorCards({ export function prefetchCardChunks(cardTypes: string[]): void { for (const type of cardTypes) { - CARD_CHUNK_PRELOADERS[type]?.()?.catch(() => {}) + CARD_CHUNK_PRELOADERS[type]?.()?.catch((e) => logger.warn('[CardRegistry] Failed to prefetch card chunk:', type, e)) } } @@ -123,7 +124,7 @@ export function prefetchDemoCardChunks(): void { () => import('./VClusterStatus'), ] - startupChunks.forEach(load => load().catch(() => {})) + startupChunks.forEach(load => load().catch((e) => logger.warn('[CardRegistry] Failed to prefetch demo card chunk:', e))) } const DEFAULT_CARD_WIDTH = 4 diff --git a/web/src/components/layout/SidebarCustomizer.tsx b/web/src/components/layout/SidebarCustomizer.tsx index 62f11ee903..d73fb58909 100644 --- a/web/src/components/layout/SidebarCustomizer.tsx +++ b/web/src/components/layout/SidebarCustomizer.tsx @@ -43,6 +43,7 @@ import { RouteSearchPanel } from './sidebar-customizer/RouteSearchPanel' import { renderIcon } from './sidebar-customizer/renderIcon' import { SidebarItemsPanel } from './sidebar-customizer/SidebarItemsPanel' import { SortableItem } from './sidebar-customizer/SortableItem' +import { logger } from '../../lib/logger' const createLocalDashboardId = () => `${LOCAL_DASHBOARD_ID_PREFIX}${crypto.randomUUID()}` @@ -204,7 +205,7 @@ export function SidebarCustomizer({ isOpen, onClose, embedded = false }: Sidebar ) if (item) updateItem(item.id, { icon: aiIcon }) }) - .catch(() => {}) + .catch((e) => logger.warn('[SidebarCustomizer] Failed to suggest dashboard icon:', e)) } const renderItemList = (items: SidebarItem[], target: 'primary' | 'secondary') => ( diff --git a/web/src/hooks/useStellarSource.ts b/web/src/hooks/useStellarSource.ts index 375b3f6341..e60365ace4 100644 --- a/web/src/hooks/useStellarSource.ts +++ b/web/src/hooks/useStellarSource.ts @@ -1,4 +1,5 @@ import { useCallback, useEffect, useRef, useState } from 'react' +import { logger } from '../lib/logger' import { getNextBatchTime, resolveStellarBatchIntervalMs } from '../components/stellar/lib/time' import { STORAGE_KEY_STELLAR_BATCH_INTERVAL_MS } from '../lib/constants/storage' import { areOptionalPollersSuppressed } from '../lib/constants/network' @@ -246,9 +247,7 @@ export function useStellarSource() { }) on<{ id: string; summary: string; suggest?: string }>('observation', payload => { setNudge({ id: payload.id, summary: payload.summary, suggest: payload.suggest, ts: new Date().toISOString() }) - stellarApi.getWatches().then(setWatches).catch(() => {}) - }) - on<{ notifications?: StellarNotification[]; watches?: StellarWatch[]; pendingActions?: StellarAction[]; operationalState?: StellarOperationalState }>('initial_batch', batch => { + stellarApi.getWatches().then(setWatches).catch((e) => logger.warn('[StellarSource] Failed to refresh watches on observation:', e)); pendingActions?: StellarAction[]; operationalState?: StellarOperationalState }>('initial_batch', batch => { if (batch.notifications) setNotifications(sortNotificationsByCreatedAt(batch.notifications)) if (batch.watches) setWatches(batch.watches) if (batch.pendingActions) setPendingActions(batch.pendingActions) @@ -256,7 +255,7 @@ export function useStellarSource() { }) on('watches', updated => setWatches(updated || [])) on('watch_update', updated => setWatches(prev => prev.map(watch => watch.id === updated.id ? updated : watch))) - es.addEventListener('watch_created', () => { stellarApi.getWatches().then(setWatches).catch(() => {}) }) + es.addEventListener('watch_created', () => { stellarApi.getWatches().then(setWatches).catch((e) => logger.warn('[StellarSource] Failed to refresh watches on watch_created:', e)) }) on('action_update', updated => { setPendingActions(prev => { const exists = prev.some(action => action.id === updated.id) @@ -273,17 +272,15 @@ export function useStellarSource() { ...prev, [payload.eventId]: { solveId: payload.solveId, eventId: payload.eventId, step: 'reading', message: 'Solve started — Stellar is on it.', actionsTaken: 0, status: 'running' }, })) - stellarApi.listSolves().then(setSolves).catch(() => {}) - }) - on('solve_progress', payload => setSolveProgress(prev => ({ ...prev, [payload.eventId]: payload }))) + stellarApi.listSolves().then(setSolves).catch((e) => logger.warn('[StellarSource] Failed to refresh solves on solve_started:', e)), payload => setSolveProgress(prev => ({ ...prev, [payload.eventId]: payload }))) on<{ solveId: string; eventId: string; status: string; summary: string }>('solve_complete', payload => { setSolveProgress(prev => { const copy = { ...prev } delete copy[payload.eventId] return copy }) - stellarApi.listSolves().then(setSolves).catch(() => {}) - stellarApi.listActivity(STELLAR_ACTIVITY_LIMIT).then(setActivity).catch(() => {}) + stellarApi.listSolves().then(setSolves).catch((e) => logger.warn('[StellarSource] Failed to refresh solves on solve_complete:', e)) + stellarApi.listActivity(STELLAR_ACTIVITY_LIMIT).then(setActivity).catch((e) => logger.warn('[StellarSource] Failed to refresh activity on solve_complete:', e)) }) on<{ id: string }>('action_bumped', payload => { setPendingActions(prev => { @@ -297,7 +294,7 @@ export function useStellarSource() { on('activity', entry => { setActivity(prev => (prev.some(item => item.id === entry.id) ? prev : [entry, ...prev].slice(0, STELLAR_ACTIVITY_LIMIT))) }) - es.addEventListener('digest_fired', () => { stellarApi.listSolves().then(setSolves).catch(() => {}) }) + es.addEventListener('digest_fired', () => { stellarApi.listSolves().then(setSolves).catch((e) => logger.warn('[StellarSource] Failed to refresh solves on digest_fired:', e)) }) on('catchup', payload => setCatchUp(payload)) on<{ content: string; period: string }>('digest', digest => { setNudge({ id: crypto.randomUUID(), summary: digest.content, ts: new Date().toISOString() }) diff --git a/web/src/lib/cache/cacheCore.ts b/web/src/lib/cache/cacheCore.ts index ad5dd0d8fe..525a026b05 100644 --- a/web/src/lib/cache/cacheCore.ts +++ b/web/src/lib/cache/cacheCore.ts @@ -23,6 +23,7 @@ import { type Subscriber, workerRpc, } from './cacheStorage' +import { logger } from '../logger' import { isDemoMode, isEquivalentToInitial, @@ -139,7 +140,7 @@ export class CacheStore { try { await cacheStorage.set(this.key, data) if (workerRpc) { - _idbStorage.set(this.key, data).catch(() => {}) + _idbStorage.set(this.key, data).catch((e) => logger.warn(`[Cache] IDB worker write failed for ${this.key}:`, e)) } } catch (e: unknown) { console.error(`[Cache] Failed to save ${this.key}:`, e) @@ -593,16 +594,12 @@ export function useCache({ const dataAge = lastRefresh ? Date.now() - lastRefresh : Infinity const hasFreshData = !state.isLoading && !state.isRefreshing && dataAge < baseInterval if (!hasFreshData) { - refetch().catch(() => {}) - } - } - - const unregisterRefetch = registerRefetch(`cache:${key}`, refetch) + refetch().catch((e) => logger.warn(`[Cache] Initial refetch failed for ${key}:`, e))(`cache:${key}`, refetch) if (autoRefresh && !autoRefreshGloballyPaused && keepAliveActive) { if (!autoRefreshTimerRef.current) { autoRefreshTimerRef.current = setInterval(() => { - refetch().catch(() => {}) + refetch().catch((e) => logger.warn(`[Cache] Auto-refresh failed for ${key}:`, e)) }, effectiveInterval) } } else if (autoRefreshTimerRef.current) { @@ -619,7 +616,7 @@ export function useCache({ if (!autoRefreshTimerRef.current || !autoRefresh || autoRefreshGloballyPaused || !keepAliveActive) return clearInterval(autoRefreshTimerRef.current) autoRefreshTimerRef.current = setInterval(() => { - refetch().catch(() => {}) + refetch().catch((e) => logger.warn(`[Cache] Auto-refresh interval failed for ${key}:`, e)) }, effectiveInterval) }, [effectiveInterval, autoRefresh, autoRefreshGloballyPaused, keepAliveActive, refetch]) From 05e224ee9ffd89664fc2b027914453ff09c6603b Mon Sep 17 00:00:00 2001 From: Copilot <223556219+Copilot@users.noreply.github.com> Date: Sun, 19 Jul 2026 13:06:30 -0400 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=8C=B1=20fix:=20restore=20code=20clob?= =?UTF-8?q?bered=20by=20prior=20edit=20in=20useStellarSource=20and=20cache?= =?UTF-8?q?Core?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prior commit's substitutions accidentally merged separate SSE handlers and the initial-refetch block, leaving 22 TypeScript syntax errors. Restore the deleted lines so build-gate passes. Signed-off-by: Copilot <223556219+Copilot@users.noreply.github.com> --- web/src/hooks/useStellarSource.ts | 8 ++++++-- web/src/lib/cache/cacheCore.ts | 6 +++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/web/src/hooks/useStellarSource.ts b/web/src/hooks/useStellarSource.ts index e60365ace4..c067ef14bd 100644 --- a/web/src/hooks/useStellarSource.ts +++ b/web/src/hooks/useStellarSource.ts @@ -247,7 +247,9 @@ export function useStellarSource() { }) on<{ id: string; summary: string; suggest?: string }>('observation', payload => { setNudge({ id: payload.id, summary: payload.summary, suggest: payload.suggest, ts: new Date().toISOString() }) - stellarApi.getWatches().then(setWatches).catch((e) => logger.warn('[StellarSource] Failed to refresh watches on observation:', e)); pendingActions?: StellarAction[]; operationalState?: StellarOperationalState }>('initial_batch', batch => { + stellarApi.getWatches().then(setWatches).catch((e) => logger.warn('[StellarSource] Failed to refresh watches on observation:', e)) + }) + on<{ notifications?: StellarNotification[]; watches?: StellarWatch[]; pendingActions?: StellarAction[]; operationalState?: StellarOperationalState }>('initial_batch', batch => { if (batch.notifications) setNotifications(sortNotificationsByCreatedAt(batch.notifications)) if (batch.watches) setWatches(batch.watches) if (batch.pendingActions) setPendingActions(batch.pendingActions) @@ -272,7 +274,9 @@ export function useStellarSource() { ...prev, [payload.eventId]: { solveId: payload.solveId, eventId: payload.eventId, step: 'reading', message: 'Solve started — Stellar is on it.', actionsTaken: 0, status: 'running' }, })) - stellarApi.listSolves().then(setSolves).catch((e) => logger.warn('[StellarSource] Failed to refresh solves on solve_started:', e)), payload => setSolveProgress(prev => ({ ...prev, [payload.eventId]: payload }))) + stellarApi.listSolves().then(setSolves).catch((e) => logger.warn('[StellarSource] Failed to refresh solves on solve_started:', e)) + }) + on('solve_progress', payload => setSolveProgress(prev => ({ ...prev, [payload.eventId]: payload }))) on<{ solveId: string; eventId: string; status: string; summary: string }>('solve_complete', payload => { setSolveProgress(prev => { const copy = { ...prev } diff --git a/web/src/lib/cache/cacheCore.ts b/web/src/lib/cache/cacheCore.ts index 525a026b05..b44e99eb91 100644 --- a/web/src/lib/cache/cacheCore.ts +++ b/web/src/lib/cache/cacheCore.ts @@ -594,7 +594,11 @@ export function useCache({ const dataAge = lastRefresh ? Date.now() - lastRefresh : Infinity const hasFreshData = !state.isLoading && !state.isRefreshing && dataAge < baseInterval if (!hasFreshData) { - refetch().catch((e) => logger.warn(`[Cache] Initial refetch failed for ${key}:`, e))(`cache:${key}`, refetch) + refetch().catch((e) => logger.warn(`[Cache] Initial refetch failed for ${key}:`, e)) + } + } + + const unregisterRefetch = registerRefetch(`cache:${key}`, refetch) if (autoRefresh && !autoRefreshGloballyPaused && keepAliveActive) { if (!autoRefreshTimerRef.current) { From 316569cc5b9c275a543c8df1e2b1db71079350b9 Mon Sep 17 00:00:00 2001 From: Scanner Date: Sun, 19 Jul 2026 15:58:14 -0400 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=8C=B1=20fix:=20resolve=20exhaustive-?= =?UTF-8?q?deps=20regression=20in=20cacheCore=20useEffect=20split?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add missing 'key' dep to reset-interval-on-refetch useEffect, which was introduced in the useEffect split but omitted from the dependency array. This brings the react-hooks/exhaustive-deps count back to the baseline. Also rebased onto origin/main (0f81cb019) to pick up PR #21290 changes. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Signed-off-by: Scanner --- web/src/lib/cache/cacheCore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/src/lib/cache/cacheCore.ts b/web/src/lib/cache/cacheCore.ts index b44e99eb91..c828e21b04 100644 --- a/web/src/lib/cache/cacheCore.ts +++ b/web/src/lib/cache/cacheCore.ts @@ -622,7 +622,7 @@ export function useCache({ autoRefreshTimerRef.current = setInterval(() => { refetch().catch((e) => logger.warn(`[Cache] Auto-refresh interval failed for ${key}:`, e)) }, effectiveInterval) - }, [effectiveInterval, autoRefresh, autoRefreshGloballyPaused, keepAliveActive, refetch]) + }, [effectiveInterval, autoRefresh, autoRefreshGloballyPaused, keepAliveActive, refetch, key]) useEffect(() => { return () => {