Send a ping every 20s to keep the WS connection (and MV3 worker) alive
Backend now replies with {"type":"pong"}. See backend commit for the full
rationale: a MV3 background service worker can be idle-killed after ~30s
with no activity reaching its own message handlers, and a disconnect while
a lockout is active used to reset the visible lock state on reconnect
(fixed backend-side) — this keepalive addresses the other half by making
that disconnect much less likely to happen in the first place.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_013qK4zsF5ogU7cjRVL3AQZ2
This commit is contained in:
@@ -32,7 +32,22 @@ interface WireError {
|
|||||||
message: string;
|
message: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type WireMessage = WireLockState | WireAck | WireError;
|
interface WirePong {
|
||||||
|
type: 'pong';
|
||||||
|
}
|
||||||
|
|
||||||
|
type WireMessage = WireLockState | WireAck | WireError | WirePong;
|
||||||
|
|
||||||
|
// JSON-level ping, per the protocol's "ping (optional fallback)" section —
|
||||||
|
// used here specifically (not relying on the browser's native WS ping/pong
|
||||||
|
// alone) because this client runs in a MV3 background service worker, which
|
||||||
|
// Chromium can idle-kill after ~30s with no activity reaching its own
|
||||||
|
// onMessage-style handlers; a plain protocol-level ping/pong frame isn't
|
||||||
|
// guaranteed to count as that activity, but a JSON message our own
|
||||||
|
// 'message' listener below processes is. Sending well under that 30s
|
||||||
|
// budget (not waiting until the deadline) keeps the socket (and the
|
||||||
|
// worker) alive continuously rather than trying to time a wakeup.
|
||||||
|
const PING_INTERVAL_MS = 20_000;
|
||||||
|
|
||||||
const WIRE_TO_INTERNAL_SCOPE: Record<WireScope, LockScope> = {
|
const WIRE_TO_INTERNAL_SCOPE: Record<WireScope, LockScope> = {
|
||||||
buy_only: 'buy-only',
|
buy_only: 'buy-only',
|
||||||
@@ -95,10 +110,14 @@ export function connectWsClient(options: WsClientOptions): WsClientController {
|
|||||||
const socket = new WebSocket(`${BACKEND_WS_URL}?token=${encodeURIComponent(token)}`);
|
const socket = new WebSocket(`${BACKEND_WS_URL}?token=${encodeURIComponent(token)}`);
|
||||||
currentSocket = socket;
|
currentSocket = socket;
|
||||||
let authFailure = false;
|
let authFailure = false;
|
||||||
|
let pingTimer: ReturnType<typeof setInterval> | undefined;
|
||||||
|
|
||||||
socket.addEventListener('open', () => {
|
socket.addEventListener('open', () => {
|
||||||
reconnectAttempt = 0;
|
reconnectAttempt = 0;
|
||||||
options.onStatusChange('connected');
|
options.onStatusChange('connected');
|
||||||
|
pingTimer = setInterval(() => {
|
||||||
|
if (socket.readyState === WebSocket.OPEN) socket.send(JSON.stringify({ type: 'ping' }));
|
||||||
|
}, PING_INTERVAL_MS);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.addEventListener('message', (event) => {
|
socket.addEventListener('message', (event) => {
|
||||||
@@ -129,9 +148,12 @@ export function connectWsClient(options: WsClientOptions): WsClientController {
|
|||||||
// 'ack' has nothing to react to yet — the client doesn't send
|
// 'ack' has nothing to react to yet — the client doesn't send
|
||||||
// unlock_request/escalate_request until lock-decision logic exists
|
// unlock_request/escalate_request until lock-decision logic exists
|
||||||
// server-side (see backend/CLAUDE.md non-goals for this milestone).
|
// server-side (see backend/CLAUDE.md non-goals for this milestone).
|
||||||
|
// 'pong' likewise needs no handling — sending the ping is the whole
|
||||||
|
// point (see PING_INTERVAL_MS above), receiving a reply isn't acted on.
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.addEventListener('close', (event) => {
|
socket.addEventListener('close', (event) => {
|
||||||
|
if (pingTimer) clearInterval(pingTimer);
|
||||||
if (currentSocket === socket) currentSocket = undefined;
|
if (currentSocket === socket) currentSocket = undefined;
|
||||||
|
|
||||||
if (manuallyDisconnected) {
|
if (manuallyDisconnected) {
|
||||||
|
|||||||
Reference in New Issue
Block a user