@vxil/realtime
v0.2.2
Published
Reconnecting browser/Node WebSocket client for Vxil realtime channels (auto-reconnect, token refresh, offline buffer, presence helpers).
Maintainers
Readme
@vxil/realtime
Reconnecting realtime client for Vxil channels — auto-reconnect with backoff, pre-expiry token refresh, an offline send buffer, presence helpers, and a typed event API. Runs in the browser and in Node.
npm install @vxil/realtimeOr zero-install in the browser — the same client is served as a self-contained module:
<script type="module">
import { RealtimeClient } from 'https://vxil.com/realtime.mjs';
</script>The one rule: connect tokens, not API keys
A server key is a secret — never put it in a browser bundle. A browser connects with a short-lived, scoped connect token instead, minted via POST /v1/realtime/tokens either by your backend (which holds the server key) or, with no backend of your own, by the browser itself using a public end_user_required key plus the signed-in user's session: that mint runs in end-user mode and the token subject is forced to the verified user. RealtimeClient takes the minting step as a tokenProvider callback and re-mints automatically whenever the cached token nears expiry.
import { RealtimeClient } from '@vxil/realtime';
const rt = new RealtimeClient({
// Here: your backend's endpoint (it holds the server key). With a public
// end_user_required key + the user's session, use the @vxil/sdk glue below.
tokenProvider: async ({ channel }) => {
const res = await fetch('/api/realtime-token', {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({ channel }),
});
return res.json(); // { connect_path, expires_at } from POST /v1/realtime/tokens
},
});
const room = rt.channel('room-7');
await room.subscribe();
room.on('message.created', (frame) => console.log(frame.data));
room.send({ type: 'message', text: 'hello' }); // buffered while offline, flushed on reconnectThe tokenProvider result may be any one of connect_url (full URL), connect_path (as returned by the tokens endpoint), or a raw token; include expires_at to enable pre-expiry re-minting. The vxil.realtime.tokenProvider({ user_id }) glue from @vxil/sdk returns a function matching this shape — on a server with a server key, or in the browser with a public end_user_required key and endUserToken set to the user's session (the subject is then the verified user, whatever user_id says).
What it adds over a raw WebSocket
- Auto-reconnect — exponential backoff with jitter, per channel (
reconnect: { baseDelayMs, maxDelayMs, maxAttempts }); each channel owns its own socket, so reconnecting is re-subscribing. - Token refresh — re-mints via your
tokenProviderbeforeexpires_at(tunable withexpirySkewMs) and after any close, so an expired token never strands a client. - Offline send buffer —
send()while disconnected buffers FIFO (default 100 frames, drop-oldest;sendBufferSize: 0disables) and flushes on the next open.send()returnsfalsewhen a frame was dropped instead of queued. - Stays inside the server's limits — the server drops frames past 20 a second or over 1,024 characters, and is moving to closing such a socket (close code
1008). The client paces its own frames to 15 a second (extrasend()frames wait in the buffer, in order; extratyping()/ping()are dropped), refuses a frame over 1,024 characters (send()returnsfalse), and after a1008close reconnects with a delay that keeps growing across such closes. - Presence —
channel.presence.membersis a live roster map, re-synced from the server on every (re)connect;channel.presence.on('sync' | 'join' | 'leave' | 'typing', cb)for events.channel.typing()sends an ephemeral (never-buffered) typing signal. - Typed events —
channel.on('<event>', cb)orchannel.on('*', cb), each returning a disposer;channel.onState(cb)observesidle → connecting → open → reconnecting → closed.
Node
Node ≥ 22 has a global WebSocket and works out of the box. On older Node, inject a constructor:
import WebSocket from 'ws';
const rt = new RealtimeClient({ tokenProvider, WebSocket });(ws is not a dependency of this package.)
Docs: vxil.com · Dashboard: vxil.com/dashboard
MIT © techmaker.io
