mirror of
https://github.com/nvms/prsm.git
synced 2025-12-16 16:10:54 +00:00
174 lines
4.4 KiB
Markdown
174 lines
4.4 KiB
Markdown
# keepalive-ws
|
|
|
|
[](https://www.npmjs.com/package/@prsm/keepalive-ws)
|
|
|
|
A command server and client for simplified WebSocket communication, with built-in ping and latency messaging. Provides reliable, Promise-based communication with automatic reconnection and command queueing.
|
|
|
|
For a TCP-based, node-only solution with a similar API, see [duplex](https://github.com/node-prism/duplex).
|
|
|
|
## Features
|
|
|
|
- **Promise-based API** - All operations return Promises for easy async/await usage
|
|
- **Command queueing** - Commands are automatically queued when offline
|
|
- **Reliable connections** - Robust error handling and reconnection
|
|
- **Bidirectional communication** - Full-duplex WebSocket communication
|
|
- **Latency monitoring** - Built-in ping/pong and latency measurement
|
|
- **Room-based messaging** - Group connections into rooms for targeted broadcasts
|
|
- **Lightweight** - Minimal dependencies
|
|
|
|
## Server
|
|
|
|
```typescript
|
|
import { KeepAliveServer, WSContext } from "@prsm/keepalive-ws/server";
|
|
|
|
// Create a server instance
|
|
const server = new KeepAliveServer({
|
|
port: 8080,
|
|
pingInterval: 30000,
|
|
latencyInterval: 5000,
|
|
});
|
|
|
|
// Register command handlers
|
|
server.registerCommand("echo", async (context) => {
|
|
return `Echo: ${context.payload}`;
|
|
});
|
|
|
|
// Error handling
|
|
server.registerCommand("throws", async () => {
|
|
throw new Error("Something went wrong");
|
|
});
|
|
|
|
// Room-based messaging
|
|
server.registerCommand("join-room", async (context) => {
|
|
const { roomName } = context.payload;
|
|
server.addToRoom(roomName, context.connection);
|
|
server.broadcastRoom(roomName, "user-joined", {
|
|
id: context.connection.id
|
|
});
|
|
return { success: true };
|
|
});
|
|
|
|
// Broadcasting to all clients
|
|
server.registerCommand("broadcast", async (context) => {
|
|
server.broadcast("announcement", context.payload);
|
|
return { sent: true };
|
|
});
|
|
```
|
|
|
|
## Client
|
|
|
|
```typescript
|
|
import { KeepAliveClient } from "@prsm/keepalive-ws/client";
|
|
|
|
// Create a client instance
|
|
const client = new KeepAliveClient("ws://localhost:8080", {
|
|
pingTimeout: 30000,
|
|
maxLatency: 2000,
|
|
shouldReconnect: true,
|
|
reconnectInterval: 2000,
|
|
maxReconnectAttempts: Infinity,
|
|
});
|
|
|
|
// Connect to the server (returns a Promise)
|
|
await client.connect();
|
|
|
|
// Using Promise-based API
|
|
try {
|
|
const response = await client.command("echo", "Hello world", 5000);
|
|
console.log("Response:", response);
|
|
} catch (error) {
|
|
console.error("Error:", error);
|
|
}
|
|
|
|
// Join a room
|
|
await client.command("join-room", { roomName: "lobby" });
|
|
|
|
// Listen for events
|
|
client.on("user-joined", (event) => {
|
|
console.log("User joined:", event.detail.id);
|
|
});
|
|
|
|
// Monitor latency
|
|
client.on("latency", (event) => {
|
|
console.log("Current latency:", event.detail.latency, "ms");
|
|
});
|
|
|
|
// Graceful shutdown
|
|
await client.close();
|
|
```
|
|
|
|
## Extended Server API
|
|
|
|
### Room Management
|
|
```typescript
|
|
// Add a connection to a room
|
|
server.addToRoom("roomName", connection);
|
|
|
|
// Remove a connection from a room
|
|
server.removeFromRoom("roomName", connection);
|
|
|
|
// Get all connections in a room
|
|
const roomConnections = server.getRoom("roomName");
|
|
|
|
// Clear all connections from a room
|
|
server.clearRoom("roomName");
|
|
```
|
|
|
|
### Broadcasting
|
|
```typescript
|
|
// Broadcast to all connections
|
|
server.broadcast("eventName", payload);
|
|
|
|
// Broadcast to specific connections
|
|
server.broadcast("eventName", payload, connections);
|
|
|
|
// Broadcast to all connections except one
|
|
server.broadcastExclude(connection, "eventName", payload);
|
|
|
|
// Broadcast to all connections in a room
|
|
server.broadcastRoom("roomName", "eventName", payload);
|
|
|
|
// Broadcast to all connections in a room except one
|
|
server.broadcastRoomExclude("roomName", "eventName", payload, connection);
|
|
|
|
// Broadcast to all connections with the same IP
|
|
server.broadcastRemoteAddress(connection, "eventName", payload);
|
|
```
|
|
|
|
### Middleware
|
|
```typescript
|
|
// Global middleware for all commands
|
|
server.globalMiddlewares.push(async (context) => {
|
|
// Validate authentication, etc.
|
|
if (!isAuthenticated(context)) {
|
|
throw new Error("Unauthorized");
|
|
}
|
|
});
|
|
|
|
// Command-specific middleware
|
|
server.registerCommand(
|
|
"protected-command",
|
|
async (context) => {
|
|
return "Protected data";
|
|
},
|
|
[
|
|
async (context) => {
|
|
// Command-specific validation
|
|
if (!hasPermission(context)) {
|
|
throw new Error("Forbidden");
|
|
}
|
|
}
|
|
]
|
|
);
|
|
```
|
|
|
|
## Graceful Shutdown
|
|
|
|
```typescript
|
|
// Close client connection
|
|
await client.close();
|
|
|
|
// Close server
|
|
server.close();
|
|
```
|