fix(dashboard): close SSE connections on outbound backpressure
The global SSE broadcast called res.write() without checking the return value, so a paused or backgrounded client would silently accumulate every store event for every entity (tasks, missions, plugins, agents, chat, ...) into res.outputData until the dashboard process OOMed. Add a 4 MB writableLength threshold; when exceeded, tear down the connection so the OS releases the buffer. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,7 @@ const PROJECT: ProjectInfo = {
|
||||
function createOptions(overrides: Partial<Parameters<typeof useViewState>[0]> = {}): Parameters<typeof useViewState>[0] {
|
||||
return {
|
||||
projectsLoading: false,
|
||||
projectsError: null,
|
||||
currentProjectLoading: false,
|
||||
currentProject: null,
|
||||
projectsLength: 1,
|
||||
@@ -189,6 +190,29 @@ describe("useViewState", () => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("does NOT call openSetupWizard when the initial projects fetch failed", async () => {
|
||||
vi.useFakeTimers();
|
||||
const openSetupWizard = vi.fn();
|
||||
|
||||
renderHook(() =>
|
||||
useViewState(
|
||||
createOptions({
|
||||
projectsLength: 0,
|
||||
currentProject: null,
|
||||
projectsError: "Failed to fetch projects",
|
||||
openSetupWizard,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
vi.advanceTimersByTime(500);
|
||||
});
|
||||
|
||||
expect(openSetupWizard).not.toHaveBeenCalled();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
// ── Insights view persistence ─────────────────────────────────────
|
||||
|
||||
it("reads saved insights taskView from scoped localStorage on init", async () => {
|
||||
|
||||
@@ -32,6 +32,7 @@ function normalizeTaskView(value: TaskView): TaskView {
|
||||
|
||||
interface UseViewStateOptions {
|
||||
projectsLoading: boolean;
|
||||
projectsError: string | null;
|
||||
currentProjectLoading: boolean;
|
||||
currentProject: ProjectInfo | null;
|
||||
projectsLength: number;
|
||||
@@ -53,6 +54,7 @@ export interface UseViewStateResult {
|
||||
export function useViewState(options: UseViewStateOptions): UseViewStateResult {
|
||||
const {
|
||||
projectsLoading,
|
||||
projectsError,
|
||||
currentProjectLoading,
|
||||
currentProject,
|
||||
projectsLength,
|
||||
@@ -112,6 +114,7 @@ export function useViewState(options: UseViewStateOptions): UseViewStateResult {
|
||||
useEffect(() => {
|
||||
if (projectsLoading || currentProjectLoading) return;
|
||||
if (setupWizardOpen) return;
|
||||
if (projectsError) return;
|
||||
if (projectsLength > 0 || currentProject) return;
|
||||
|
||||
const timer = window.setTimeout(() => {
|
||||
@@ -121,6 +124,7 @@ export function useViewState(options: UseViewStateOptions): UseViewStateResult {
|
||||
return () => window.clearTimeout(timer);
|
||||
}, [
|
||||
projectsLoading,
|
||||
projectsError,
|
||||
projectsLength,
|
||||
currentProjectLoading,
|
||||
currentProject,
|
||||
|
||||
Reference in New Issue
Block a user