From 9fdd77e67dc8bbfc514fd60ec8b3ebec9111a4f6 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Wed, 29 Jul 2026 20:43:06 -0700 Subject: [PATCH] fix(mock-server): clear the terminal stream interval on resubscribe and unsubscribe --- mobile/scripts/mock-server-terminal-stream.ts | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/mobile/scripts/mock-server-terminal-stream.ts b/mobile/scripts/mock-server-terminal-stream.ts index a1e6c9bd86b..1859ed4f60f 100644 --- a/mobile/scripts/mock-server-terminal-stream.ts +++ b/mobile/scripts/mock-server-terminal-stream.ts @@ -6,6 +6,32 @@ import { STREAMING_CHUNKS } from './mock-server-terminal-fixtures' +// Why: the client resubscribes on every viewport change; without cancellation +// each resubscribe would stack another interval streaming under a dead request. +const streamIntervals = new WeakMap>>() + +function clearTerminalStream(ws: WebSocket, terminal: string): void { + const perTerminal = streamIntervals.get(ws) + const interval = perTerminal?.get(terminal) + if (interval !== undefined) { + clearInterval(interval) + perTerminal?.delete(terminal) + } +} + +function trackTerminalStream( + ws: WebSocket, + terminal: string, + interval: ReturnType +): void { + let perTerminal = streamIntervals.get(ws) + if (!perTerminal) { + perTerminal = new Map() + streamIntervals.set(ws, perTerminal) + } + perTerminal.set(terminal, interval) +} + /** Terminal list/stream/input backend for the mock server. Returns false for * methods it does not own. */ export function handleMockTerminalRequest( @@ -51,17 +77,20 @@ export function handleMockTerminalRequest( }) ) + const terminal = String(request.params?.terminal ?? 'term-1') + clearTerminalStream(ws, terminal) let chunkIndex = 0 const interval = setInterval(() => { if (chunkIndex >= STREAMING_CHUNKS.length || ws.readyState !== ws.OPEN) { // Why: no `end` event - a live terminal stream stays open, and `end` // makes the client tear the subscription down and blank the pane. - clearInterval(interval) + clearTerminalStream(ws, terminal) return } respond(success(request.id, { type: 'data', chunk: STREAMING_CHUNKS[chunkIndex] }, true)) chunkIndex++ }, 500) + trackTerminalStream(ws, terminal, interval) return true } @@ -74,6 +103,7 @@ export function handleMockTerminalRequest( return true case 'terminal.unsubscribe': + clearTerminalStream(ws, String(request.params?.terminal ?? 'term-1')) respond(success(request.id, { unsubscribed: true })) return true