JavaScript SDK
Type-safe client for browsers and Node.js.
Installation
npm install @raucheacho/konet-jsQuick Start
import { createClient } from '@raucheacho/konet-js'
const client = createClient('ws://localhost:4000/socket', {
token: '<anon_key>',
})
const channel = client.channel('room:lobby')
await channel.subscribe()
channel.on('message', (payload) => {
console.log('Received:', payload)
})
channel.send('message', { text: 'Hello!' })createClient connects immediately. Call client.disconnect() to close the
socket.
Client Options
interface KonetClientOptions {
token: string
heartbeatIntervalMs?: number // default: 30000
reconnectDelayMs?: number // default: 1000
maxReconnectAttempts?: number // default: 10
heartbeatTimeoutMs?: number // default: 10000
}heartbeatTimeoutMs is how long the client waits for a heartbeat reply before
declaring the socket dead. Keep it below the server's socket timeout (45s).
Reconnection
The client reconnects with exponential backoff and re-joins every channel you
subscribed to, so a dropped connection is transparent to your code. A channel
you left with unsubscribe() is never re-joined.
While disconnected, send() throws rather than writing into a socket the
server no longer associates with your topic. After a re-join the server replays
presence_state, so getPresence() is accurate again on its own.
Suspended Environments
setInterval is a scheduler, not a clock: browsers throttle timers in inactive
tabs and mobile runtimes freeze them. A suspended client wakes up with a socket
that still reports OPEN long after the server timed it out.
Call checkConnection() whenever the host may have suspended the client. It
probes the connection, reconnects if the probe goes unanswered, and revives a
client that exhausted maxReconnectAttempts while suspended.
document.addEventListener('visibilitychange', () => {
if (!document.hidden) client.checkConnection()
})In React Native, use @raucheacho/konet-rn, which wires
this to AppState for you.
Presence
Presence updates arrive as a presence event with the current member list:
channel.on('presence', (users) => {
console.log('Online:', users.length)
})
// or read the current snapshot on demand
const users = channel.getPresence().list()Error Handling
A join that the server refuses rejects the subscribe() promise:
try {
await channel.subscribe()
} catch (err) {
console.error('Join failed:', err.reason)
}Konet acknowledges a broadcast only when it refuses it — rate limiting, an
unsupported payload. There is no ack on success, so send() stays
fire-and-forget rather than returning a promise that could never settle.
Refusals surface per call and per channel:
channel.send('audio', chunk, (err) => {
console.warn('dropped:', err.reason) // e.g. "rate_limited"
})
channel.on('send_error', (err) => metrics.increment(err.reason))interface KonetSendError {
topic: string
event: string // the application event you passed to send()
payload: unknown
reason: string // server-supplied, e.g. "rate_limited"
}This matters most on high-frequency streams, where the server's per-socket rate
limit is otherwise reached silently. Raise it with KONET_RATE_LIMIT on the
server if you see rate_limited under normal load.
Binary Frames & Floor Control
For anything at a media rate. Take the floor before sending — a binary frame without it is refused. See the protocol page for the guarantees.
const channel = client.channel('room:team-42:ptt')
await channel.subscribe()
channel.on('konet:floor', ({ holder }) => {
// holder is a user id, or null when the floor is free
})
channel.on('a', (data: Uint8Array) => {
// an incoming frame; the view is only valid for this call
})
try {
await channel.acquireFloor()
channel.sendBinary('a', frame) // frame: Uint8Array
} catch (error) {
// someone else is talking; the error names them
} finally {
await channel.releaseFloor()
}sendBinary copies its argument, so a caller may reuse one buffer for every
frame — which an audio path does, every 20 ms.
Unlike send(), binary frames are never buffered across a reconnect. Replaying
audio recorded seconds ago into a live channel is worse than losing it.
Types
The SDK is written in TypeScript and ships with declaration files.