Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

@aionbuilders/helios-protocol

aionbuilders385MIT1.1.1TypeScript support: included

Core protocol implementation for Helios - a lightweight, runtime-agnostic WebSocket messaging protocol with request/response and pub/sub patterns

websocket, protocol, messaging, rpc, request-response, pubsub, events, real-time, bun, runtime-agnostic

readme

@aionbuilders/helios-protocol

Core protocol implementation for Helios - a lightweight, runtime-agnostic WebSocket messaging protocol

npm version License: MIT

Features

  • 🚀 Lightweight - Zero runtime dependencies, pure JavaScript
  • 🔄 Request/Response Pattern - RPC-style async request/response with timeout support
  • 📡 Pub/Sub Events - Topic-based event system with wildcard matching
  • 🎯 Type-Safe - Full TypeScript definitions with JSDoc annotations
  • Runtime Agnostic - Works with Bun, Node.js, Deno, and browsers
  • 🔒 Validation - Built-in message validation with detailed error reporting
  • 🧪 Well Tested - 245+ test cases covering all core functionality

Installation

# Using npm
npm install @aionbuilders/helios-protocol

# Using bun
bun add @aionbuilders/helios-protocol

# Using yarn
yarn add @aionbuilders/helios-protocol

Quick Start

import { Request, Event, Parser, Serializer } from '@aionbuilders/helios-protocol';

// Create a request
const request = Request.outgoing(
  { userId: 123 },
  { method: 'user.get', timeout: 5000 }
);

// Serialize for transport
const serialized = Serializer.serialize(request);

// Parse incoming messages
const message = Parser.parse(serialized);

// Create an event
const event = Event.outgoing(
  { message: 'Hello, World!' },
  { topic: 'chat.room.general', reliable: true }
);

API Reference

Core Components

  • Messages - Request, Response, Event message types
  • MethodManager - RPC method routing with middleware
  • EventManager - Pub/sub event system with topic subscriptions
  • Parser/Serializer - Message parsing and serialization
  • Errors - Structured error hierarchy
  • Utils - PatternMatcher and CapturePatternMatcher

Messages

The protocol supports three message types: Request, Response, and Event.

Request

RPC-style request messages with timeout support.

import { Request } from '@aionbuilders/helios-protocol';

// Create outgoing request
const request = Request.outgoing(
  { userId: 123, action: 'update' },  // payload
  {
    method: 'user.update',            // required
    timeout: 5000,                    // optional (ms)
    metadata: { trace: 'abc' },       // optional
    peer: { service: 'user-service' } // optional routing
  }
);

// Access properties
console.log(request.method);    // 'user.update'
console.log(request.timeout);   // 5000
console.log(request.payload);   // { userId: 123, action: 'update' }
console.log(request.id);        // auto-generated UUID

Method format: Alphanumeric characters, dots, dashes, and underscores (e.g., user.get, chat-room.join)

Response

Response to a request message.

import { Response } from '@aionbuilders/helios-protocol';

// Success response
const success = Response.outgoing(
  { user: { id: 123, name: 'Alice' } },  // payload
  {
    requestId: request.id,                // required
    status: 200                           // required
  }
);

// Error response
const error = Response.outgoing(
  null,
  {
    requestId: request.id,
    status: 404,
    error: 'User not found'
  }
);

// Access properties
console.log(success.status);     // 200
console.log(success.requestId);  // matches request.id
console.log(success.payload);    // { user: {...} }
console.log(error.error);        // 'User not found'

Status codes: Follow HTTP conventions (200, 404, 500, etc.)

Event

Pub/sub style event messages with topic routing.

import { Event } from '@aionbuilders/helios-protocol';

// Create event
const event = Event.outgoing(
  { message: 'Hello!', user: 'Alice' },  // data
  {
    topic: 'chat.room.general',          // required
    reliable: true,                      // optional (default: false)
    metadata: { priority: 'high' }       // optional
  }
);

// Topic matching with wildcards
Event.matchTopic('chat.room.general', 'chat.*');        // true (single level)
Event.matchTopic('chat.room.general', 'chat.**');       // true (multi level)
Event.matchTopic('chat.room.general', 'user.*');        // false
Event.matchTopic('chat.room.general.dm', 'chat.*');     // false
Event.matchTopic('chat.room.general.dm', 'chat.**');    // true

// Access properties
console.log(event.topic);     // 'chat.room.general'
console.log(event.reliable);  // true
console.log(event.data);      // { message: 'Hello!', user: 'Alice' }

Topic format: Alphanumeric characters, dots, dashes, underscores, and wildcards (*, **)

  • * matches exactly one level (e.g., chat.* matches chat.room but not chat.room.general)
  • ** matches one or more levels (e.g., chat.** matches chat.room.general)

MethodManager

RPC method routing with middleware support and pattern matching.

import { MethodManager, Request } from '@aionbuilders/helios-protocol';

const methods = new MethodManager();

// Register methods
methods.register('user.get', async (context) => {
  // Access request data
  const userId = context.payload.userId;

  // Return response data directly
  return { id: userId, name: 'Alice', email: 'alice@example.com' };
});

methods.register('user.create', async (context) => {
  // Return custom status code using context.createResponse
  return context.createResponse({ id: 456 }, 201);
});

// Register with options
methods.register('user.update', async (context) => {
  return { updated: true };
}, { timeout: 10000 });

// Handle incoming requests
const request = Request.outgoing({ userId: 123 }, { method: 'user.get' });
const response = await methods.handle(request);

console.log(response.status);  // 200
console.log(response.data);    // { id: 123, name: 'Alice', ... }

Middleware

Add cross-cutting concerns like logging, auth, validation:

// Global middleware (runs for all methods)
methods.use('**', async (context, next) => {
  console.log(`[${context.method}] Start`);
  const result = await next();
  console.log(`[${context.method}] Done`);
  return result;
});

// Pattern-based middleware (runs for matching methods)
methods.use('user.*', async (context, next) => {
  // Auth check for all user.* methods
  if (!context.clientId) {
    throw new Error('Unauthorized');
  }
  return await next();
});

// Specific method middleware
methods.use('user.delete', async (context, next) => {
  // Audit log for user deletions
  console.log('Deleting user:', context.payload.userId);
  return await next();
});

Pattern matching:

  • user.get - Exact match
  • user.* - Single level wildcard (matches user.get, user.create)
  • user.** - Multi-level wildcard (matches user.get, user.settings.update)
  • **.admin - Prefix wildcard (matches user.admin, system.admin)

Context Data

Pass additional data to handlers (like client ID, auth info):

const response = await methods.handle(request, {
  clientId: 'client-123',
  userId: 456,
  permissions: ['read', 'write']
});

methods.register('post.create', async (context) => {
  // Access custom context data
  const { clientId, userId, permissions } = context;

  if (!permissions.includes('write')) {
    return context.createResponse(null, 403);
  }

  return { postId: 789, author: userId };
});

Namespaces

Organize methods into logical groups:

// Create namespace
const userMethods = methods.namespace('user');

// Register methods with automatic prefix
userMethods.register('get', async (context) => {
  return { id: 123 };
});

userMethods.register('create', async (context) => {
  return { id: 456 };
});

// Accessible as 'user.get' and 'user.create'
const response = await methods.handle(
  Request.outgoing({}, { method: 'user.get' })
);

EventManager

Pub/sub event system with topic routing and middleware.

import { EventManager, Event } from '@aionbuilders/helios-protocol';

const events = new EventManager();

// Subscribe to events
events.on('user:created', async (context) => {
  console.log('New user:', context.data.userId);
});

events.on('user:deleted', async (context) => {
  console.log('User deleted:', context.data.userId);
});

// Subscribe with wildcards
events.on('user:*', async (context) => {
  // Matches user:created, user:deleted, user:updated, etc.
  console.log('User event:', context.topic);
});

events.on('chat:**', async (context) => {
  // Matches chat:message, chat:room:join, chat:room:leave, etc.
  console.log('Chat event:', context.data);
});

// Dispatch events
const event = Event.outgoing(
  { userId: 123, name: 'Alice' },
  { topic: 'user:created' }
);

await events.handle(event);

One-time Listeners

Subscribe to events that auto-remove after first execution:

events.once('system:ready', async (context) => {
  console.log('System initialized');
  // This listener will be removed after first execution
});

Event Middleware

Add cross-cutting logic to event handling:

// Global middleware
events.use('**', async (context, next) => {
  console.log(`Event: ${context.topic}`);
  await next();
});

// Pattern-based middleware
events.use('user:*', async (context, next) => {
  // Log all user events
  console.log('User event data:', context.data);
  await next();
});

// Specific topic middleware
events.use('chat:message', async (context, next) => {
  // Filter profanity for chat messages
  context.data.message = filterProfanity(context.data.message);
  await next();
});

Context Data

Pass additional context when handling events:

await events.handle(event, {
  clientId: 'client-123',
  server: 'ws-server-1'
});

events.on('user:action', async (context) => {
  // Access custom context
  console.log('Client:', context.clientId);
  console.log('Server:', context.server);
});

Event Namespaces

Organize event listeners into logical groups:

// Create namespace
const userEvents = events.namespace('user');

// Register listeners with automatic prefix
userEvents.on('created', async (context) => {
  console.log('User created');
});

userEvents.on('deleted', async (context) => {
  console.log('User deleted');
});

// Accessible as 'user:created' and 'user:deleted'
await events.handle(
  Event.outgoing({}, { topic: 'user:created' })
);

Management API

// List all registered topics
const topics = events.topics();  // ['user:created', 'chat:message', ...]

// Get listeners for a topic
const listeners = events.listeners('user:created');

// Check if topic has listeners
if (events.has('user:created')) {
  console.log('Has listeners');
}

// Remove specific listener
events.off('user:created', myHandler);

// Remove all listeners for topic
events.offAll('user:created');

// Clear all listeners
events.clear();

Parser & Serializer

Serializer

Convert messages to wire format (JSON or binary).

import { Serializer } from '@aionbuilders/helios-protocol';

// Serialize to JSON (default)
const jsonString = Serializer.serialize(message);

// Serialize to binary (Uint8Array)
const binary = Serializer.serialize(message, 'binary');

// Check if message can be serialized
if (Serializer.canSerialize(message)) {
  const data = Serializer.serialize(message);
}

Parser

Parse incoming data into typed message instances.

import { Parser } from '@aionbuilders/helios-protocol';

// Parse from different formats
const message = Parser.parse(jsonString);           // from JSON string
const message = Parser.parse(uint8Array);           // from Uint8Array
const message = Parser.parse(arrayBuffer);          // from ArrayBuffer

// Returns the correct message type
if (message instanceof Request) {
  console.log('Received request:', message.method);
} else if (message instanceof Response) {
  console.log('Received response:', message.status);
} else if (message instanceof Event) {
  console.log('Received event:', message.topic);
}

Errors

The protocol provides a structured error hierarchy.

import {
  HeliosError,       // Base error class
  ValidationError,   // Invalid data provided (4xx equivalent)
  ProtocolError,     // Invalid message format (4xx equivalent)
  TimeoutError       // Request timeout (5xx equivalent)
} from '@aionbuilders/helios-protocol';

try {
  const request = Request.outgoing({}, { /* missing method */ });
} catch (error) {
  if (error instanceof ValidationError) {
    console.log(error.message);  // "Request requires a method"
    console.log(error.details);  // [{ field: "method", message: "Method is required" }]
  }
}

try {
  const message = Parser.parse(invalidData);
} catch (error) {
  if (error instanceof ProtocolError) {
    console.log(error.code);     // "INVALID_MESSAGE_TYPE"
    console.log(error.message);  // "Invalid message type"
  }
}

Message Properties

All messages share common properties:

// Common to all messages
message.id          // Unique identifier (UUID v4)
message.type        // 'request' | 'response' | 'event'
message.protocol    // Protocol version (e.g., 'helios/1.0.0')
message.timestamp   // Unix timestamp in milliseconds
message.direction   // 'outgoing' | 'incoming'
message.metadata    // Optional metadata object
message.peer        // Optional peer routing { id?, service?, metadata? }

// Methods
message.clone()     // Create a deep copy
message.validate()  // Validate message integrity
message.toJSON()    // Convert to plain object

Complete Export List

// Messages
import {
  Message,
  Request,
  Response,
  Event
} from '@aionbuilders/helios-protocol';

// Method Management (RPC)
import {
  MethodManager,
  MethodHandler,
  RequestContext,
  MethodNamespaceManager
} from '@aionbuilders/helios-protocol';

// Event Management (Pub/Sub)
import {
  EventManager,
  EventListener,
  EventContext,
  EventNamespaceManager
} from '@aionbuilders/helios-protocol';

// Parser & Serializer
import {
  Parser,
  Serializer
} from '@aionbuilders/helios-protocol';

// Errors
import {
  HeliosError,
  ValidationError,
  ProtocolError,
  TimeoutError
} from '@aionbuilders/helios-protocol';

// Utils
import {
  PatternMatcher,
  CapturePatternMatcher
} from '@aionbuilders/helios-protocol';

TypeScript Support

Full TypeScript definitions are included:

import type {
  MessageType,
  MessageHeaders,
  RequestHeaders,
  ResponseHeaders,
  EventHeaders,
  MessageOptions,
  PeerRouting,
  SerializedMessage
} from '@aionbuilders/helios-protocol';

// All classes are fully typed
const request = Request.outgoing<{ userId: number }>(
  { userId: 123 },
  { method: 'user.get' }
);

// Type-safe access
const userId: number = request.payload.userId;

Peer Routing

Messages can include peer routing information for multi-peer scenarios:

// Route by peer ID
const request = Request.outgoing(
  { action: 'ping' },
  {
    method: 'health.check',
    peer: { id: 'client-abc-123' }
  }
);

// Route by service type
const request = Request.outgoing(
  { userId: 123 },
  {
    method: 'user.get',
    peer: { service: 'user-service' }
  }
);

// Route with metadata
const request = Request.outgoing(
  { query: 'data' },
  {
    method: 'search',
    peer: {
      service: 'search-service',
      metadata: { region: 'eu-west-1', version: '2.0' }
    }
  }
);

Note: At least one of id or service is required when using peer routing.

Validation

The protocol uses a two-phase validation approach:

Outgoing (Creation) - Strict

When creating messages, validation is strict and throws immediately:

// ❌ Throws ValidationError: method is required
Request.outgoing({}, {});

// ❌ Throws ValidationError: invalid method format
Request.outgoing({}, { method: 'invalid method!' });

// ❌ Throws ValidationError: timeout must be positive
Request.outgoing({}, { method: 'test', timeout: -1 });

Incoming (Parsing) - Permissive

When parsing received messages, validation is permissive and only throws for critical errors:

// Parses successfully, logs warning for unusual values
const message = Parser.parse(slightlyInvalidData);

Manual Validation

You can manually validate a message at any time:

try {
  message.validate();
  console.log('Message is valid');
} catch (error) {
  console.error('Validation failed:', error.details);
}

Protocol Version

Current protocol version: helios/1.0.0

The protocol follows semantic versioning:

  • Major version changes indicate breaking protocol changes
  • Minor version changes add backward-compatible features
  • Patch version changes are bug fixes

Messages with incompatible major versions are rejected automatically.

Examples

Full Request/Response Cycle

import { Request, Response, Parser, Serializer } from '@aionbuilders/helios-protocol';

// Client side: create and send request
const request = Request.outgoing(
  { userId: 123 },
  { method: 'user.get', timeout: 5000 }
);

const requestData = Serializer.serialize(request);
// Send requestData over WebSocket...

// Server side: receive and parse request
const receivedRequest = Parser.parse(requestData);

// Process request and create response
const response = Response.outgoing(
  { id: 123, name: 'Alice', email: 'alice@example.com' },
  { requestId: receivedRequest.id, status: 200 }
);

const responseData = Serializer.serialize(response);
// Send responseData back over WebSocket...

// Client side: receive response
const receivedResponse = Parser.parse(responseData);
console.log(receivedResponse.payload); // { id: 123, name: 'Alice', ... }

Pub/Sub with Events

import { Event, Parser, Serializer } from '@aionbuilders/helios-protocol';

// Publisher: create and broadcast event
const event = Event.outgoing(
  { message: 'New user joined!', user: 'Bob' },
  { topic: 'chat.room.lobby', reliable: true }
);

const eventData = Serializer.serialize(event);
// Broadcast eventData to all subscribers...

// Subscriber: receive and handle event
const receivedEvent = Parser.parse(eventData);

if (receivedEvent instanceof Event) {
  // Check topic match
  if (Event.matchTopic(receivedEvent.topic, 'chat.**')) {
    console.log('Chat event:', receivedEvent.data);
  }
}

Runtime Compatibility

This protocol package is runtime agnostic and works with:

  • Bun (recommended, optimized for Bun runtime)
  • Node.js (v16+)
  • Deno
  • Browsers (modern browsers with ES2020+ support)

Contributing

Contributions are welcome! Please read DEVELOPMENT.md for development guidelines and architectural decisions.

License

MIT © Killian Di Vincenzo (AION Builders)


Made with ❤️ by Killian Di Vincenzo with AION Builders

changelog

Changelog

All notable changes to this project will be documented in this file.

The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.

1.0.0 - 2025-12-28

Added

Core Protocol

  • Message Types: Request, Response, and Event message implementations
  • Request/Response Pattern: RPC-style async messaging with timeout support
  • Pub/Sub Events: Topic-based event system with wildcard matching (* and **)
  • MethodManager: RPC method routing with middleware support and pattern matching
  • EventManager: Pub/sub event system with topic subscriptions and middleware
  • Parser & Serializer: JSON and binary serialization support
  • Validation System: Two-phase validation (strict outgoing, permissive incoming)

Features

  • Middleware System: Onion-style middleware with pattern matching for both methods and events
  • Namespace Support: Organize methods and events into logical groups with automatic prefixing
  • Pattern Matching: Wildcards (*, **, ++) for flexible routing
  • Capture Groups: Extract values from method/topic patterns
  • Peer Routing: Support for routing messages by peer ID or service type
  • Message Metadata: Extensible metadata support for all message types
  • Protocol Versioning: Semantic versioning with backward compatibility checks
  • Type Safety: Full TypeScript definitions with JSDoc annotations
  • Runtime Agnostic: Works with Bun, Node.js, Deno, and browsers
  • Context Data: Pass custom data to handlers and listeners (auth, client info, etc.)

Error Handling

  • HeliosError: Base error class for all protocol errors
  • ValidationError: Structured validation errors with field-level details
  • ProtocolError: Protocol format and compatibility errors
  • TimeoutError: Request timeout handling

Developer Experience

  • Comprehensive test suite (245+ tests)
  • Complete API documentation
  • Usage examples for all message types
  • Development guidelines (DEVELOPMENT.md)

Technical Details

  • Protocol Version: helios/1.0.0
  • Message Formats: JSON (default), Binary (Uint8Array)
  • Message Direction: Outgoing (created) and Incoming (received)
  • Topic Wildcards: * (single level), ** (multi-level)
  • Method Format: Alphanumeric with dots, dashes, underscores (e.g., user.get)
  • Status Codes: HTTP-style status codes (200, 404, 500, etc.)

Dependencies

  • Runtime: Zero runtime dependencies
  • Development: TypeScript (for type generation), Bun (for testing)