@fainthit/reserve
v1.0.1
Published
Node.js WebSocket server for the reserve protocol.
Readme
@fainthit/reserve
Node.js WebSocket server for the reserve protocol.
Install
npm install @fainthit/reserveBasic usage
import { Reserver } from "@fainthit/reserve";
const server = new Reserver({
ws: { port: 3000 },
format: "json",
});
server.on("connection", (client) => {
console.log("connected", client.name, client.token);
client.on("device.ready", (...data) => {
console.log("device ready", data);
});
});
server.send("game.start", { room: 1 });
server.sendTo("door.open", "door-a", { duration: 3000 });Constructor
new Reserver(options?: ReserverOption)Options:
{
ws?: WebSocket.ServerOptions;
format?: "json" | "msgpack";
pingInterval?: number;
pingTimeout?: number;
retryInterval?: number;
disconnectedClientTtl?: number;
whitelist?: Iterable<string>;
duplicateName?: "allow" | "keep-old" | "disconnect-old";
}Defaults:
ws.port = 3000
format = json
pingInterval = 10000
pingTimeout = 5000
retryInterval = 5000
disconnectedClientTtl = 300000
whitelist = undefined
duplicateName = allowws is passed to new WebSocketServer(). Compression is disabled by default.
disconnectedClientTtl is the time, in milliseconds, to keep a disconnected
client session by token. Reconnecting with the same token before the TTL expires
resumes the same client. Set it to -1 to keep disconnected sessions until the
server is closed.
whitelist restricts accepted client names. If omitted or empty, clients may
connect with any name, including no name. If non-empty, hello.name is
required and must be included in the whitelist; otherwise the socket is closed.
duplicateName controls live sessions that use the same name:
allow: keep existing behavior and allow multiple live sessions with the same name.keep-old: keep the existing live session and reject the new connection.disconnect-old: disconnect and remove the existing live session, then accept the new one.
Reconnects with the same known token resume that session. In unique-name modes,
disconnected stale sessions with the same name are removed when a new accepted
session claims that name.
Server events
connection
server.on("connection", (client) => {});Emitted when a new client session is created. Reconnects with a known token do
not emit connection; they emit reconnected on the existing client.
error
server.on("error", (error) => {});Emitted when decoding or socket sending fails.
Server methods
send
Broadcast to every known client.
server.send(channel, ...data);Example:
server.send("game.timer", 120);sendTo
Send to one or more client names.
server.sendTo(channel, target, ...data);target can be a string or string array.
server.sendTo("door.open", "door-a", { duration: 3000 });
server.sendTo("lights.off", ["light-a", "light-b"]);reset
Reset every connected client session.
server.reset();This clears pending reliable messages and restarts sequence numbers.
close
Close all clients and the WebSocket server.
server.close();Client object
The connection event gives a Client.
Public fields:
client.name?: string;
client.token: string;
client.connected: boolean;
client.delay: number;
client.socket: WebSocket;Client events
channel events
Client messages are emitted by channel name.
client.on("sensor.trigger", (...data) => {});reconnected
client.on("reconnected", () => {});Emitted when a client reconnects with a known token.
disconnected
client.on("disconnected", (reason) => {});Emitted when the socket closes, errors, or heartbeat times out.
pong
client.on("pong", (delay) => {});Emitted when a heartbeat response is received. delay is measured in
milliseconds.
Client methods
sendMessage
Send a reliable protocol message to this client.
client.sendMessage({
channel: "door.open",
data: [{ duration: 3000 }],
});Most applications should use server.send() or server.sendTo() instead.
reset
Reset this client session.
client.reset();disconnect
Close this client connection.
client.disconnect();Reliability
Every msg frame has a 32-bit sequence key and must be ACKed. If no ACK arrives,
the server retries the same frame with the same key. Duplicate incoming messages
are ACKed again without re-emitting the channel event.
The dedupe window is 32 messages. If sync cannot be recovered inside that window, reset is used.
