mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-11 14:00:29 +00:00
Stabilize browser WebSocket heartbeat
Refresh browser-side WebSocket activity tracking on valid inbound messages so normal server heartbeat traffic prevents unnecessary reconnect churn. Cover the quiet-period timeout path with a regression test.
This commit is contained in:
@@ -289,6 +289,10 @@ AI-only summary payloads, or page-local heuristics.
|
||||
from websocket `state.resources` instead of layering confirmatory
|
||||
route-local REST refetch loops over already-owned resource
|
||||
updates.
|
||||
Browser WebSocket liveness tracking is part of that same store boundary:
|
||||
valid inbound server messages, including heartbeat `ping`/`pong` traffic,
|
||||
must refresh the browser-side activity timestamp so quiet periods between
|
||||
resource snapshots do not cause avoidable reconnect churn.
|
||||
That shared store/adapter/hook path must also preserve canonical row shape
|
||||
across transport boundaries: thinner realtime `state.resources` payloads
|
||||
must merge into the existing canonical resource snapshot instead of
|
||||
|
||||
@@ -143,6 +143,25 @@ describe('websocket store resilience', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('keeps the websocket open when server activity arrives before heartbeat timeout', async () => {
|
||||
const { dispose } = await createStoreHarness();
|
||||
try {
|
||||
vi.advanceTimersByTime(1); // run onopen tick
|
||||
expect(currentInstance).not.toBeNull();
|
||||
|
||||
vi.advanceTimersByTime(89_000);
|
||||
currentInstance!.onmessage?.({
|
||||
data: JSON.stringify({ type: 'pong', data: { timestamp: Date.now() } }),
|
||||
} as MessageEvent);
|
||||
vi.advanceTimersByTime(2_000);
|
||||
|
||||
expect(currentInstance!.close).not.toHaveBeenCalledWith(4000, 'Heartbeat timeout');
|
||||
expect(currentInstance!.close).not.toHaveBeenCalled();
|
||||
} finally {
|
||||
dispose();
|
||||
}
|
||||
});
|
||||
|
||||
it('manual reconnect avoids duplicate reconnect scheduling', async () => {
|
||||
const { store, dispose } = await createStoreHarness();
|
||||
try {
|
||||
|
||||
@@ -411,6 +411,7 @@ export function createWebSocketStore(url: string) {
|
||||
logger.error('Failed to parse WebSocket message', parseError);
|
||||
return;
|
||||
}
|
||||
lastServerActivityAt = Date.now();
|
||||
|
||||
try {
|
||||
const message = data as TimestampedWSMessage;
|
||||
|
||||
Reference in New Issue
Block a user