// Type definitions for ZeroNode
// Project: https://github.com/sfast/zeronode
// Definitions by: ZeroNode Team
///
import { EventEmitter } from 'events';
// ============================================================================
// Core Types
// ============================================================================
/**
* Node configuration options
*/
export interface NodeConfig {
/** Global request timeout in milliseconds (default: 10000) */
PROTOCOL_REQUEST_TIMEOUT?: number;
/** Buffer allocation strategy: 'EXACT' or 'POWER_OF_2' */
PROTOCOL_BUFFER_STRATEGY?: 'EXACT' | 'POWER_OF_2';
/** Client ping interval in milliseconds (default: 10000) */
CLIENT_PING_INTERVAL?: number;
/** Server health check interval in milliseconds (default: 30000) */
CLIENT_HEALTH_CHECK_INTERVAL?: number;
/** Client ghost timeout in milliseconds (default: 60000) */
CLIENT_GHOST_TIMEOUT?: number;
/** Enable debug logging (default: false) */
DEBUG?: boolean;
/** Custom logger instance */
logger?: any;
/** ZeroMQ reconnection interval in milliseconds */
reconnectInterval?: number;
/** ZeroMQ maximum reconnection interval in milliseconds */
reconnectMaxInterval?: number;
/** ZeroMQ heartbeat interval in milliseconds */
heartbeatInterval?: number;
/** ZeroMQ heartbeat timeout in milliseconds */
heartbeatTimeout?: number;
/** ZeroMQ heartbeat TTL in milliseconds */
heartbeatTtl?: number;
}
/**
* Node constructor options
*/
export interface NodeOptions {
/** Unique node identifier (auto-generated if not provided) */
id?: string;
/** Initial bind address (optional, can bind later) */
bind?: string;
/** Node metadata for routing and service discovery */
options?: Record;
/** Node configuration */
config?: NodeConfig;
}
/**
* Request options
*/
export interface RequestOptions {
/** Target node ID */
to: string;
/** Event name */
event: string;
/** Request payload */
data?: any;
/** Request timeout in milliseconds (overrides global timeout) */
timeout?: number;
}
/**
* Tick (fire-and-forget) options
*/
export interface TickOptions {
/** Target node ID */
to: string;
/** Event name */
event: string;
/** Message payload */
data?: any;
}
/**
* Request with filter options
*/
export interface RequestAnyOptions {
/** Event name */
event: string;
/** Request payload */
data?: any;
/** Request timeout in milliseconds */
timeout?: number;
/** Filter object or predicate for node selection */
filter?: Record | { predicate: (options: Record) => boolean };
/** Search downstream nodes (default: true) */
down?: boolean;
/** Search upstream nodes (default: true) */
up?: boolean;
}
/**
* Tick with filter options
*/
export interface TickAnyOptions {
/** Event name */
event: string;
/** Message payload */
data?: any;
/** Filter object or predicate for node selection */
filter?: Record | { predicate: (options: Record) => boolean };
/** Search downstream nodes (default: true) */
down?: boolean;
/** Search upstream nodes (default: true) */
up?: boolean;
}
/**
* Connection options
*/
export interface ConnectOptions {
/** Server address to connect to (e.g., 'tcp://127.0.0.1:3000') */
address: string;
/** Connection timeout in milliseconds (optional) */
timeout?: number;
/** Reconnection timeout in milliseconds (optional) */
reconnectionTimeout?: number;
}
/**
* Envelope object passed to handlers
*/
export interface Envelope {
/** Envelope unique ID (BigInt) */
readonly id: bigint;
/** Envelope type: 1=TICK, 2=REQUEST, 3=RESPONSE, 4=ERROR */
readonly type: number;
/** Unix timestamp in seconds */
readonly timestamp: number;
/** Sender node ID (original requester) */
readonly owner: string;
/** Recipient node ID */
readonly recipient: string;
/** Event name */
readonly event: string;
/** Parsed message data (read-only) */
readonly data: any;
}
/**
* Reply function for request handlers
*/
export interface ReplyFunction {
/** Send successful response */
(data: any): void;
/** Send error response */
error(error: any): void;
}
/**
* Next function for middleware
*/
export interface NextFunction {
/** Continue to next handler */
(): void;
/** Pass error to error handlers */
(error: Error): void;
}
/**
* Request handler signatures
*/
export type RequestHandler =
| ((envelope: Envelope, reply: ReplyFunction) => void | Promise)
| ((envelope: Envelope, reply: ReplyFunction, next: NextFunction) => void | Promise)
| ((error: Error, envelope: Envelope, reply: ReplyFunction, next: NextFunction) => void | Promise);
/**
* Tick handler signature
*/
export type TickHandler = (envelope: Envelope) => void | Promise;
// ============================================================================
// Events
// ============================================================================
/**
* Node-level events
*/
export enum NodeEvent {
/** Peer joined the network */
PEER_JOINED = 'node:peer_joined',
/** Peer left the network */
PEER_LEFT = 'node:peer_left',
/** Node stopped */
STOPPED = 'node:stopped',
/** Node error */
ERROR = 'node:error'
}
/**
* Client-level events
*/
export enum ClientEvent {
/** Transport ready (can send/receive bytes) */
READY = 'client:ready',
/** Transport reported NOT_READY (lost connection, may recover) */
NOT_READY = 'client:not_ready',
/** Transport permanently closed */
CLOSED = 'client:closed',
/** Server joined (handshake complete, session established) */
SERVER_JOINED = 'client:server_joined',
/** Server left (shutdown, disconnect, timeout) */
SERVER_LEFT = 'client:server_left',
/** Client error */
ERROR = 'client:error'
}
/**
* Server-level events
*/
export enum ServerEvent {
/** Server ready to accept clients */
READY = 'server:ready',
/** Server not ready */
NOT_READY = 'server:not_ready',
/** Server closed */
CLOSED = 'server:closed',
/** Client joined */
CLIENT_JOINED = 'server:client_joined',
/** Client left */
CLIENT_LEFT = 'server:client_left'
}
/**
* Transport-level events
*/
export enum TransportEvent {
/** Transport ready */
READY = 'transport:ready',
/** Transport not ready */
NOT_READY = 'transport:not_ready',
/** Message received */
MESSAGE = 'transport:message',
/** Transport error */
ERROR = 'transport:error',
/** Transport closed */
CLOSED = 'transport:closed'
}
// ============================================================================
// Errors
// ============================================================================
/**
* Node error codes
*/
export enum NodeErrorCode {
/** Node not found */
NODE_NOT_FOUND = 'NODE_NOT_FOUND',
/** No nodes match filter */
NO_NODES_MATCH_FILTER = 'NO_NODES_MATCH_FILTER',
/** Invalid address */
INVALID_ADDRESS = 'INVALID_ADDRESS'
}
/**
* Node error class
*/
export class NodeError extends Error {
code: NodeErrorCode;
nodeId?: string;
cause?: Error;
context?: any;
constructor(options: {
code: NodeErrorCode;
message: string;
nodeId?: string;
cause?: Error;
context?: any;
});
toJSON(): any;
}
/**
* Assert that address is valid
* @throws {NodeError} If address is invalid
*/
export function assertValidAddress(address: string): void;
/**
* Protocol error codes
*/
export enum ProtocolErrorCode {
/** Protocol not ready */
NOT_READY = 'PROTOCOL_NOT_READY',
/** Request timeout */
REQUEST_TIMEOUT = 'REQUEST_TIMEOUT',
/** Invalid envelope */
INVALID_ENVELOPE = 'INVALID_ENVELOPE',
/** Invalid response */
INVALID_RESPONSE = 'INVALID_RESPONSE',
/** Invalid event */
INVALID_EVENT = 'INVALID_EVENT',
/** Handler error */
HANDLER_ERROR = 'HANDLER_ERROR'
}
/**
* Protocol error class
*/
export class ProtocolError extends Error {
code: ProtocolErrorCode;
protocolId?: string;
envelopeId?: bigint;
cause?: Error;
context?: any;
constructor(options: {
code: ProtocolErrorCode;
message: string;
protocolId?: string;
envelopeId?: bigint;
cause?: Error;
context?: any;
});
toJSON(): any;
}
/**
* Transport error codes
*/
export enum TransportErrorCode {
/** Already connected */
ALREADY_CONNECTED = 'TRANSPORT_ALREADY_CONNECTED',
/** Bind failed */
BIND_FAILED = 'TRANSPORT_BIND_FAILED',
/** Already bound */
ALREADY_BOUND = 'TRANSPORT_ALREADY_BOUND',
/** Unbind failed */
UNBIND_FAILED = 'TRANSPORT_UNBIND_FAILED',
/** Send failed */
SEND_FAILED = 'TRANSPORT_SEND_FAILED',
/** Receive failed */
RECEIVE_FAILED = 'TRANSPORT_RECEIVE_FAILED',
/** Invalid address */
INVALID_ADDRESS = 'TRANSPORT_INVALID_ADDRESS',
/** Close failed */
CLOSE_FAILED = 'TRANSPORT_CLOSE_FAILED'
}
/**
* Transport error class
*/
export class TransportError extends Error {
code: TransportErrorCode;
transportId?: string;
address?: string;
cause?: Error;
context?: any;
constructor(options: {
code: TransportErrorCode;
message: string;
transportId?: string;
address?: string;
cause?: Error;
context?: any;
});
toJSON(): any;
isCode(code: string): boolean;
isConnectionError(): boolean;
isBindError(): boolean;
isSendError(): boolean;
}
// ============================================================================
// Event Payloads
// ============================================================================
export interface PeerJoinedPayload {
peerId: string;
peerOptions: Record;
direction: 'upstream' | 'downstream';
}
export interface PeerLeftPayload {
peerId: string;
direction: 'upstream' | 'downstream';
reason?: string;
}
export interface NodeErrorPayload {
source?: string;
stage?: string;
address?: string;
serverId?: string;
category?: string;
code?: string;
message?: string;
error?: Error;
}
export interface ClientReadyPayload {
serverId: string;
serverOptions: Record;
}
export interface ClientDisconnectedPayload {
serverId: string;
address: string;
}
export interface ClientFailedPayload {
serverId: string;
address: string;
error?: Error;
}
export interface ClientStoppedPayload {
serverId: string;
address: string;
}
export interface ServerReadyPayload {
serverId: string;
}
export interface ServerClientJoinedPayload {
clientId: string;
clientOptions: Record;
}
export interface ServerClientLeftPayload {
clientId: string;
}
export interface ServerClientTimeoutPayload {
clientId: string;
lastSeen: number;
timeSinceLastSeen: number;
final: boolean;
}
// ============================================================================
// Main Node Class
// ============================================================================
/**
* Node - Main class for ZeroNode network nodes
*
* A Node can simultaneously:
* - Bind as a server (accept downstream connections)
* - Connect as a client (to upstream servers)
* - Route messages between peers
* - Load balance across available nodes
*
* @example
* ```typescript
* import Node from 'zeronode';
*
* const node = new Node({
* id: 'my-service',
* options: { role: 'api', version: 1 }
* });
*
* await node.bind('tcp://0.0.0.0:3000');
*
* node.onRequest('user:get', async (envelope, reply) => {
* const user = await getUser(envelope.data.userId);
* return user;
* });
* ```
*/
export default class Node extends EventEmitter {
/**
* Create a new Node
*/
constructor(options?: NodeOptions);
// ============================================================================
// Node Identity & State
// ============================================================================
/**
* Get node ID
*/
getId(): string;
/**
* Get bind address (if server is bound)
*/
getAddress(): string | null;
/**
* Get node options (metadata)
*/
getOptions(): Record;
/**
* Update node options (for dynamic routing)
* @returns Promise that resolves when options are updated
*/
setOptions(options: Record): Promise;
/**
* Get filtered nodes by options or predicate
* @param options - Filter criteria
* @returns Array of node IDs matching the filter
*/
getFilteredNodes(options?: {
options?: Record;
predicate?: (nodeOptions: Record) => boolean;
up?: boolean;
down?: boolean;
}): string[];
/**
* Get peer options by ID
* @param peerId - Peer ID
* @returns Peer options or null if peer not joined
*/
getPeerOptions(peerId: string): any | null;
/**
* Get server ID by connection address
* @param address - Server address (e.g., 'tcp://127.0.0.1:5000')
* @returns Server ID or null if not connected
*/
getServerIdByAddress(address: string): string | null;
// ============================================================================
// Connection Management
// ============================================================================
/**
* Bind as server (accept connections)
* @param address - Bind address (e.g., 'tcp://0.0.0.0:3000')
*/
bind(address: string): Promise;
/**
* Unbind server (stop accepting connections)
*/
unbind(): Promise;
/**
* Connect to remote server
* @param options - Connection options
*/
connect(options: ConnectOptions): Promise;
/**
* Disconnect from remote server
* @param address - Server address to disconnect from
*/
disconnect(address: string): Promise;
/**
* Close the node and all its connections.
*
* This permanently closes:
* - The server (if bound)
* - All client connections
* - All underlying transport sockets
*
* After closing, the node cannot be reused.
* Pending requests will be rejected.
* All handlers will be removed.
*/
close(): Promise;
// ============================================================================
// Handler Registration
// ============================================================================
/**
* Register request handler
* @param pattern - Event name (string) or pattern (RegExp)
* @param handler - Request handler function
*/
onRequest(pattern: string | RegExp, handler: RequestHandler): void;
/**
* Unregister request handler
* @param pattern - Event name (string) or pattern (RegExp)
* @param handler - Handler function to remove (optional)
*/
offRequest(pattern: string | RegExp, handler?: RequestHandler): void;
/**
* Register tick handler
* @param pattern - Event name (string) or pattern (RegExp)
* @param handler - Tick handler function
*/
onTick(pattern: string | RegExp, handler: TickHandler): void;
/**
* Unregister tick handler
* @param pattern - Event name (string) or pattern (RegExp)
* @param handler - Handler function to remove (optional)
*/
offTick(pattern: string | RegExp, handler?: TickHandler): void;
// ============================================================================
// Messaging API
// ============================================================================
/**
* Send request to specific node
* @param options - Request options
* @returns Promise resolving to response data
*/
request(options: RequestOptions): Promise;
/**
* Send tick (fire-and-forget) to specific node
* @param options - Tick options
*/
tick(options: TickOptions): void;
/**
* Send request to any matching node (load balanced)
* @param options - Request options with filter
* @returns Promise resolving to response data
*/
requestAny(options: RequestAnyOptions): Promise;
/**
* Send request to any downstream node
* @param options - Request options with filter
* @returns Promise resolving to response data
*/
requestDownAny(options: Omit): Promise;
/**
* Send request to any upstream node
* @param options - Request options with filter
* @returns Promise resolving to response data
*/
requestUpAny(options: Omit): Promise;
/**
* Send tick to any matching node
* @param options - Tick options with filter
*/
tickAny(options: TickAnyOptions): void;
/**
* Send tick to any downstream node
* @param options - Tick options with filter
*/
tickDownAny(options: Omit): void;
/**
* Send tick to any upstream node
* @param options - Tick options with filter
*/
tickUpAny(options: Omit): void;
/**
* Send tick to all matching nodes
* @param options - Tick options with filter
* @returns Promise resolving to an array of results
*/
tickAll(options: TickAnyOptions): Promise;
/**
* Send tick to all downstream nodes
* @param options - Tick options with filter
* @returns Promise resolving to an array of results
*/
tickDownAll(options: Omit): Promise;
/**
* Send tick to all upstream nodes
* @param options - Tick options with filter
* @returns Promise resolving to an array of results
*/
tickUpAll(options: Omit): Promise;
// ============================================================================
// Event Emitter (typed events)
// ============================================================================
on(event: NodeEvent.PEER_JOINED, listener: (payload: PeerJoinedPayload) => void): this;
on(event: NodeEvent.PEER_LEFT, listener: (payload: PeerLeftPayload) => void): this;
on(event: NodeEvent.STOPPED, listener: () => void): this;
on(event: NodeEvent.ERROR, listener: (payload: NodeErrorPayload) => void): this;
on(event: string | symbol, listener: (...args: any[]) => void): this;
once(event: NodeEvent.PEER_JOINED, listener: (payload: PeerJoinedPayload) => void): this;
once(event: NodeEvent.PEER_LEFT, listener: (payload: PeerLeftPayload) => void): this;
once(event: NodeEvent.STOPPED, listener: () => void): this;
once(event: NodeEvent.ERROR, listener: (payload: NodeErrorPayload) => void): this;
once(event: string | symbol, listener: (...args: any[]) => void): this;
off(event: string | symbol, listener: (...args: any[]) => void): this;
removeListener(event: string | symbol, listener: (...args: any[]) => void): this;
removeAllListeners(event?: string | symbol): this;
}
// ============================================================================
// Transport Abstraction
// ============================================================================
/**
* Transport interface for custom transport implementations
*/
export interface ITransport {
createClientSocket(config?: any): any;
createServerSocket(config?: any): any;
}
/**
* Transport factory and registry
*/
export class Transport {
/**
* Register a custom transport
* @param name - Transport name
* @param transportImpl - Transport implementation
*/
static register(name: string, transportImpl: ITransport): void;
/**
* Set default transport
* @param name - Transport name
*/
static setDefault(name: string): void;
/**
* Create client socket using default transport
* @param config - Socket configuration
*/
static createClientSocket(config?: any): any;
/**
* Create server socket using default transport
* @param config - Socket configuration
*/
static createServerSocket(config?: any): any;
}
// ============================================================================
// Utilities
// ============================================================================
/**
* Build predicate function from options object
* @param options - Filter options
* @returns Predicate function
*/
export function optionsPredicateBuilder(options: Record): (nodeOptions: Record) => boolean;
// ============================================================================
// Module Exports
// ============================================================================
// Note: All exports are already declared with 'export' keyword above
// No need for re-export block