"use client"; import { useEffect } from "react"; import { useSWRConfig } from "swr"; /** * A headless component that maintains a Server-Sent Events (SSE) connection * to the Postgres database. When the backend agent mutates this project, * this component receives a ping and aggressively revalidates SWR. */ export function ProjectStreamHandler({ projectId }: { projectId: string }) { const { mutate } = useSWRConfig(); useEffect(() => { if (!projectId) return; let eventSource: EventSource | null = null; let retryTimeout: NodeJS.Timeout; const connect = () => { eventSource = new EventSource(`/api/projects/${projectId}/stream`); eventSource.onmessage = (event) => { try { const data = JSON.parse(event.data); if (data.event === "updated") { // The database row for this project was mutated! // Find all SWR cache keys that belong to this project and invalidate them. // This instantly updates the Plan tab, Task Kanban, and Anatomy overview. mutate( (key) => typeof key === "string" && key.includes(projectId), undefined, { revalidate: true } ); } } catch (err) { console.error("[SSE] Parse error", err); } }; eventSource.onerror = () => { eventSource?.close(); // If the connection drops (e.g. server restart or network dip), back off and try again retryTimeout = setTimeout(connect, 5000); }; }; connect(); return () => { eventSource?.close(); clearTimeout(retryTimeout); }; }, [projectId, mutate]); return null; }