DocumentationCore Protocol
@painda/core
The blazing fast binary backbone of the PaindaProtocol ecosystem. Now with full Socket.io feature parity.
Why @painda/core?
- Zero-Copy Architecture: Reads direct ArrayBuffers instead of expensive string parsing.
- Namespaces: Multiplex
server.of("/admin")over a single connection. - Middleware Pipeline: Express-style
server.use()chains for auth & validation. - Acknowledgements: Request-response with
client.send(msg, callback). - Typed Contracts: Schema registry ensures binary type safety across the wire.
- Connection Recovery: Missed messages replayed automatically after reconnect.
- Horizontal Scaling: Adapter interface for Redis / Postgres multi-instance deployments.
Installation
npm install @painda/coreServer with Namespaces & Middleware
import { PPServer } from "@painda/core";
const server = new PPServer({
port: 7001,
registry,
heartbeatInterval: 30000, // Ping every 30s
maxMessagesPerSecond: 100, // Rate limiting
recovery: true, // Enable state recovery
});
// Global middleware — runs for every connection
server.use((socket, next) => {
console.log("Client connected:", socket.id);
next();
});
// Namespaces — split logic over one connection
const admin = server.of("/admin");
admin.use((socket, next) => {
// Namespace-specific auth
if (isAdmin(socket)) next();
else next(new Error("Unauthorized"));
});
admin.on("connection", (nsSocket) => {
nsSocket.on("dashboard:stats", (msg) => {
// Handle admin-only events
});
});
// Default namespace
server.on("connection", (client) => {
client.on("message", (msg) => {
server.broadcast(msg, client); // Exclude sender
});
});Client with Acks & Recovery
import { PPClient } from "@painda/client";
const client = new PPClient({
url: "ws://localhost:7001",
registry,
reconnect: true,
ackTimeout: 5000,
});
// Send with acknowledgement callback
client.send(
{ type: "save", payload: { name: "test" } },
(err, response) => {
if (err) console.error("Server didn't respond:", err);
else console.log("Confirmed:", response);
}
);
// Volatile send — dropped if disconnected (no queue)
client.send(
{ type: "cursor", payload: { x: 100, y: 200 } },
{ volatile: true }
);
// Catch-all for debugging
client.onAny((event, ...args) => {
console.log("Event:", event, args);
});
// Recovery event after reconnect
client.on("recovery", ({ missedCount }) => {
console.log(`Recovered ${missedCount} missed messages`);
});Compression
Frame-level deflate compression is active by default for payloads over 1 KB. Uses node:zlib on the server side and the DecompressionStream Web API in the browser client. Saves 60–80% bandwidth on JSON payloads (game state, chat history, etc.).
const server = new PPServer({
port: 3000,
compression: {
algorithm: "deflate", // "deflate" | "none" (default: "deflate")
threshold: 1024, // min bytes before compressing (default: 1024)
},
});
// Compression is per-frame: only applied if compressed.byteLength < original.byteLength
// perMessageDeflate is disabled on the WS layer — PP handles compression itselfCompression is applied at the PP frame level, not the WebSocket level (perMessageDeflate: false). This means compression and PP's binary framing work independently of the WebSocket transport.
Room API (Socket.io-compatible)
// On socket (Socket.io-style):
socket.join("game-1")
socket.leave("game-1")
socket.to("game-1").emit("update", delta) // to room, excluding self
socket.broadcast.emit("event", data) // to all, excluding self
// On server:
server.to("game-1").emit("update", delta)
server.in("game-1").fetchSockets() // Promise<PPClientSocket[]>
server.in("game-1").disconnectSockets()
server.in(["r1", "r2"]).emit("event", data) // multiple rooms
server.except("admins").emit("msg", data) // all except a room
server.in("r1").except("r2").emit("msg", d) // chainingFull API Surface
| API | Description |
|---|---|
| server.of(name) | Create / get namespace |
| server.use(fn) | Global connection middleware |
| server.useMessage(fn) | Global message middleware |
| server.broadcast(msg) | Encode-once broadcast to all |
| server.broadcastVolatile(msg) | Broadcast, drop on failure |
| server.onAny(fn) | Catch-all event listener |
| client.send(msg, cb) | Send with ack callback |
| client.send(msg, {volatile}) | Volatile send (drop if busy) |
| client.onAny(fn) | Catch-all listener |
| client.recoverySessionId | Current recovery session |
| server.getClients() | All connected PPClientSocket[] |
| server.getPluginNames() | Names of all registered plugins |
| server.getStats() | Live server stats (clients, rooms, uptime, presenceTracked) |
| server.getPlugin<T>(name) | Get typed plugin API by name |
| ppMetricsPlugin | Built-in metrics plugin — register with server.register(ppMetricsPlugin) |