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/core

Server 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 itself

Compression 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) // chaining

Full API Surface

APIDescription
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.recoverySessionIdCurrent 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
ppMetricsPluginBuilt-in metrics plugin — register with server.register(ppMetricsPlugin)