actions/socket.svelte.ts view source
1.5 Exponential backoff factor: delay = base * factor^(attempt-1).
Frontend WebSocket client — portable, Svelte-reactive, implements WebsocketConnection.
Plain class with $state runes (no Cell inheritance, no app coupling).
Drop into any SvelteKit frontend as the underlying connection for
FrontendWebsocketTransport. Handles auto-reconnect with exponential
backoff, respects WS_CLOSE_SESSION_REVOKED (no reconnect loop after the
server revokes auth), exposes reactive status for UI indicators, and ships
three correctness primitives default-on:
- FrontendWebsocketClient.request — promise-based JSON-RPC with
auto-assigned ids and a pending-id map. Intercepts responses on the
message path so request/response correlation is transport-level rather
than re-invented per consumer.
- Durable queue — request() calls made while disconnected buffer up
to DEFAULT_QUEUE_MAX_SIZE requests and flush on reopen. Overflow
rejects with queue_overflow. Raw FrontendWebsocketClient.send
is drop-on-disconnect (fire-and-forget notifications want that).
- Activity-aware heartbeat — idles fire a shared heartbeat request;
receive-silence past DEFAULT_HEARTBEAT_RECEIVE_TIMEOUT closes
with WS_CLOSE_CLIENT_HEARTBEAT_TIMEOUT and lets auto-reconnect
pick back up.
16 declarations
actions/socket.svelte.ts view source
1.5 Exponential backoff factor: delay = base * factor^(attempt-1).
actions/socket.svelte.ts view source
1000 Default WebSocket close code (normal closure).
actions/socket.svelte.ts view source
30000 Idle interval before sending a heartbeat (ms).
actions/socket.svelte.ts view source
60000 Max receive silence before closing with WS_CLOSE_CLIENT_HEARTBEAT_TIMEOUT (ms).
actions/socket.svelte.ts view source
100 Default bound on buffered requests while disconnected. Overflow rejects.
actions/socket.svelte.ts view source
1000 Base reconnect delay in ms.
actions/socket.svelte.ts view source
10000 Max reconnect delay in ms (cap on exponential backoff).
actions/socket.svelte.ts view source
Reactive WebSocket client implementing WebsocketConnection.
Construct with a URL and optional config; call connect() to open the
socket and begin auto-reconnect. Register message/error handlers via
add_message_handler / add_error_handler — both return unsubscribe
functions. FrontendWebsocketTransport consumes this as its connection.
Session-revocation close codes (WS_CLOSE_SESSION_REVOKED) put the client in a permanently-closed state; reconnecting would just loop on 401.
Disposablewstype WebSocket | null
statustype SocketStatus
reconnect_counttype number
current_reconnect_delaytype number
last_connect_timeEpoch ms of the most recent successful open. Never cleared on close.
type number | null
last_close_timeEpoch ms of the most recent close event or client-initiated close.
type number | null
last_close_codeClose code from the most recent close. Initial null means "never closed."
type number | null
last_close_reasonReason string from the most recent close event (may be empty).
type string | null
last_send_errorThe error thrown by the most recent attempted send(), or null if the
most recent attempt succeeded or none has been attempted yet. Populated
when the underlying ws.send throws (e.g., buffer full, serialization
error); reset to null on the next successful send. Not touched when
send() short-circuits because the socket is not connected — consult
connected for that case. Wrappers surfacing per-message failure
reasons can read this after a false return from send().
type Error | null
connectedtype boolean
constructortype new (url: string, options?: FrontendWebsocketClientOptions): FrontendWebsocketClient
urlstringoptions{}set_reconnectSwap the auto-reconnect policy in place. Accepts the same shape as the
constructor's reconnect option: false disables reconnect, true or
null/omitted restores the defaults, or a config object customizes
specific fields (missing fields fall back to defaults, not "keep
current" — each call defines the whole policy atomically, same as the
constructor).
In-flight reconnect schedules are monotonically shortened: the effective total wait from arm-time never exceeds what the new policy prescribes. If the new target is already past the time already elapsed, the reconnect fires immediately (on the next tick). The wait is never extended.
Turning reconnect off while a reconnect timer is pending cancels that
timer and transitions status to closed (since the lie of
'reconnecting' would be visible to UI indicators). Turning it back on
does not synthesize a reconnect — wait for the next close.
type (reconnect?: boolean | FrontendWebsocketReconnectOptions | null): void
reconnectboolean | FrontendWebsocketReconnectOptions | nullnullvoidset_heartbeatSwap the heartbeat policy in place. Accepts the same shape as the
constructor's heartbeat option: false disables the timer, true or
null/omitted restores the defaults, or a config object customizes
specific fields (missing fields fall back to defaults, not "keep
current" — each call defines the whole policy atomically, same as the
constructor and set_reconnect).
When connected, the live timer is restarted immediately so the new
interval / receive_timeout take effect without a reconnect; when
disconnected, just stashes the policy for the next open.
type (heartbeat?: boolean | FrontendWebsocketHeartbeatOptions | null): void
heartbeatboolean | FrontendWebsocketHeartbeatOptions | nullnullvoidcancel_reconnectCancel a scheduled reconnect without closing the client or disabling
auto-reconnect. Transitions status from reconnecting → closed and
resets the backoff counters — the next close still triggers a fresh
reconnect cycle under the current policy. No-op when no reconnect is
pending.
Use this when UI state asks "stop trying for now" without the finality
of disconnect (which also rejects pending/queued requests and
clears heartbeat) or the policy change of set_reconnect(false)
(which disables future reconnects). The queue stays intact so that
calling connect later flushes buffered work.
type (): void
voidconnectOpen the WebSocket. No-op on SSR, or if the session has been revoked. Cancels any pending reconnect and tears down any existing connection first; an open prior socket is closed with a normal-closure code.
type (): void
voiddisconnectClose the WebSocket, cancel any pending reconnect, and reset the reconnect
backoff counters. Puts the client in closed status; call connect() to
reopen. Safe to call more than once.
type (code?: number): void
codenumberDEFAULT_CLOSE_CODEvoid[Symbol.dispose]Explicit-resource-management hook — supports using client = new FrontendWebsocketClient(url).
type (): void
voidsendtype (data: object): boolean
dataobjectbooleanrequestPromise-based JSON-RPC over the socket. Auto-assigns a monotonic request
id (or uses an explicit one supplied via options.id — used by
FrontendWebsocketTransport which delegates to this method and has its
own peer-minted UUID), tracks the pending promise, and resolves when the
server sends a matching response.
Callers supplying an explicit options.id are responsible for
uniqueness — the pending map is keyed by id, and a duplicate silently
overwrites the prior entry. Auto-minted ids are monotonic and never
collide with themselves or with peer-minted UUIDs (the types differ:
integer vs string).
While the socket is disconnected, the request is buffered in a bounded
queue (default-on, DEFAULT_QUEUE_MAX_SIZE) and flushed on reopen. Pass
{queue: false} to reject immediately when disconnected — used
internally by the heartbeat, which must not fight the queue for the
disconnect-detection slot.
On AbortSignal fire: rejects the local promise *and* sends the shared
cancel notification (cancel_action_spec.method) so the server-side
dispatcher can abort the matching handler's ctx.signal. Suppressed
for queued-but-never-sent (server doesn't know about it) and
response-beat-cancel races.
type <R = unknown>(method: string, params?: unknown, options?: { signal?: AbortSignal | undefined; queue?: boolean | undefined; id?: string | number | undefined; }): Promise<R>
methodstringparamsunknown{}options{ signal?: AbortSignal | undefined; queue?: boolean | undefined; id?: string | number | undefined; }{}Promise<R>ThrownJsonrpcError - on the returned promise — never thrownadd_message_handlertype (handler: SocketMessageHandler): () => void
handler() => voidadd_error_handlertype (handler: SocketErrorHandler): () => void
handler() => voidactions/socket.svelte.ts view source
FrontendWebsocketClientOptions reconnectAuto-reconnect policy. false disables reconnect entirely; true or
omit for default timing; pass an object to customize.
boolean | FrontendWebsocketReconnectOptions | nullheartbeatActivity-aware heartbeat. true/null/omit for defaults; false disables
the timer entirely (only do this if the server side is also running
without heartbeat); pass an object to tune interval / receive_timeout.
boolean | FrontendWebsocketHeartbeatOptions | nullqueueDurable queue for FrontendWebsocketClient.request. true or omit
for defaults; false disables buffering (requests while disconnected
reject immediately). Raw FrontendWebsocketClient.send is never
queued — use request() for RPC semantics.
boolean | FrontendWebsocketQueueOptionslogOptional logger for diagnostic messages.
Logger | nullactions/socket.svelte.ts view source
FrontendWebsocketHeartbeatOptions intervalIdle duration (ms) after which a heartbeat is sent. Reset by any send or receive — chatty clients never emit extras. Defaults to DEFAULT_HEARTBEAT_INTERVAL.
numberreceive_timeoutReceive-silence (ms) after which the client closes the socket with
WS_CLOSE_CLIENT_HEARTBEAT_TIMEOUT, letting auto-reconnect kick
in. Should be a comfortable multiple of interval. Defaults to
DEFAULT_HEARTBEAT_RECEIVE_TIMEOUT.
numberactions/socket.svelte.ts view source
FrontendWebsocketQueueOptions max_sizeMaximum number of requests held while the socket is disconnected.
Enqueue past this rejects the new call with a queue_overflow error.
Defaults to DEFAULT_QUEUE_MAX_SIZE.
numberactions/socket.svelte.ts view source
FrontendWebsocketReconnectOptions delayBase reconnect delay in ms. Defaults to 1000.
numberdelay_maxMax reconnect delay in ms (cap on exponential backoff). Defaults to 10000.
numberfactorExponential backoff factor. Defaults to 1.5.
numberactions/socket.svelte.ts view source
(status: SocketStatus, revoked: boolean): AsyncStatus Project SocketStatus onto fuz_util's AsyncStatus — the
5-way → 4-way mapping every consumer re-derives to surface connection state
to UI (loading indicators, retry banners). Collapses reconnecting into
failure (UI shows "lost, retrying") and splits closed by revoked so
a terminal session-revocation read as failure while a clean client-
initiated close reads as initial (the "not connected, not trying" state).
statusrevokedwhether the session has been permanently revoked
(typically FrontendWebsocketClient.revoked)
booleanAsyncStatus actions/socket.svelte.ts view source
SocketErrorHandler actions/socket.svelte.ts view source
SocketMessageHandler actions/socket.svelte.ts view source
SocketStatus Client-side WebSocket status.
- initial — never connected; connect() has not been called.
- connecting — WebSocket readyState === CONNECTING.
- connected — WebSocket readyState === OPEN.
- reconnecting — close fired; waiting out backoff before next attempt.
- closed — socket is not open. Terminal only when revoked is true
or auto-reconnect is disabled; otherwise connect() reopens.