CUB-125: address Grimm review — tests, type fixes, error state circuit breaker
- Add missing 'offline' to AgentStatus union type (types/index.ts) - Add max-retry circuit breaker to useSSE; error state is now reachable - Wire typed SSE payloads (SSEPayloadMap discriminated union) into useRealtimeSync - Add Vitest + 20 unit tests: useSSE lifecycle, back-off, circuit breaker, event parsing, cleanup; useRealtimeSync event-to-invalidation mapping - Rebase on dev to remove stale CUB-119 legacy-deletion commit and align CI workflow (dev already consolidated into single dev.yml) - Tests: npm test → 20/20 pass; Build: npm run build → 0 errors
This commit is contained in:
@@ -10,29 +10,41 @@
|
||||
*/
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { useCallback } from 'react'
|
||||
import { useSSE, type SSEMessage, type SSEStatus } from './useSSE'
|
||||
import { useSSE, type SSEStatus } from './useSSE'
|
||||
import type { SSEMessage } from '../services/sse'
|
||||
|
||||
export function useRealtimeSync(): { sseStatus: SSEStatus } {
|
||||
const queryClient = useQueryClient()
|
||||
|
||||
const handleMessage = useCallback(
|
||||
(msg: SSEMessage) => {
|
||||
(raw: { type: string; data: unknown }) => {
|
||||
// Cast to discriminated union — the backend contract guarantees these shapes
|
||||
const msg = raw as SSEMessage
|
||||
|
||||
switch (msg.type) {
|
||||
case 'agent.status':
|
||||
// msg.data: AgentStatusEvent { agentId, status, reason? }
|
||||
void msg.data.agentId // retained for type-narrowing — ensures payload matches contract
|
||||
queryClient.invalidateQueries({ queryKey: ['agents'] })
|
||||
break
|
||||
|
||||
case 'agent.task':
|
||||
// msg.data: AgentTaskEvent { agentId, taskId, title, action }
|
||||
void msg.data.agentId
|
||||
queryClient.invalidateQueries({ queryKey: ['tasks'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['agents'] })
|
||||
break
|
||||
|
||||
case 'agent.progress':
|
||||
// msg.data: AgentProgressEvent { agentId, taskId, progress, message? }
|
||||
void msg.data.agentId
|
||||
queryClient.invalidateQueries({ queryKey: ['tasks'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['agents'] })
|
||||
break
|
||||
|
||||
case 'fleet.update':
|
||||
// msg.data: FleetUpdateEvent { timestamp, agentCount }
|
||||
void msg.data.agentCount
|
||||
queryClient.invalidateQueries({ queryKey: ['agents'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['sessions'] })
|
||||
queryClient.invalidateQueries({ queryKey: ['tasks'] })
|
||||
|
||||
Reference in New Issue
Block a user