fix: improve event stream reconnect reliability

Recover stalled event streams without dropping the session
Wait briefly for reconnection before showing connection lost errors
Persist Electron server logs for easier disconnect debugging
This commit is contained in:
Bohdan Triapitsyn
2026-04-22 21:03:02 +03:00
parent 17dd526731
commit f24e6de21b
10 changed files with 493 additions and 139 deletions
@@ -666,6 +666,62 @@ describe('createEventPipeline', () => {
},
]);
});
it('marks the pipeline disconnected on heartbeat timeout and recovers on the next websocket connect', async () => {
installDomStubs();
globalThis.WebSocket = FakeWebSocket;
const disconnectReasons = [];
let reconnectCount = 0;
const sdk = {
global: {
event: async () => {
throw new Error('SSE should not be used in ws mode');
},
},
};
const recovered = new Promise((resolve) => {
const { cleanup } = createEventPipeline({
sdk,
transport: 'ws',
heartbeatTimeoutMs: 20,
reconnectDelayMs: 0,
wsReadyTimeoutMs: 20,
onEvent: () => {},
onDisconnect: (reason) => {
disconnectReasons.push(reason);
},
onReconnect: () => {
reconnectCount += 1;
if (reconnectCount === 2) {
cleanup();
resolve();
}
},
});
});
await Promise.resolve();
const firstSocket = FakeWebSocket.instances[0];
firstSocket.emitOpen();
firstSocket.emitMessage({ type: 'ready', scope: 'global' });
await new Promise((resolve) => setTimeout(resolve, 35));
const secondSocket = FakeWebSocket.instances[1];
expect(secondSocket).toBeDefined();
secondSocket.emitOpen();
secondSocket.emitMessage({ type: 'ready', scope: 'global' });
await recovered;
expect(disconnectReasons).toEqual(['ws_heartbeat_timeout']);
expect(reconnectCount).toBe(2);
});
});
// ---------------------------------------------------------------------------