@devgrid/netron
A powerful TypeScript library for building distributed systems with event bus, streams, and remote object invocation capabilities. Built with WebSocket for real-time bidirectional communication between Node.js and browser environments.
Features
- 🌐 Cross-platform (Node.js and Browser)
- 🔄 Bidirectional WebSocket communication
- 📦 Remote object invocation (RPC)
- 🚀 Event bus with parallel and serial execution
- 💫 Streaming support
- 🛡️ TypeScript decorators for service definitions
- 🔍 Automatic service discovery
- ⚡ MessagePack serialization
- 🔒 Type-safe service interfaces
- 🔄 Automatic reconnection handling
- 📡 Service versioning support
Installation
npm install @devgrid/netron
yarn add @devgrid/netron
Basic Usage
Creating a Server
import { Netron, Service, Public } from '@devgrid/netron';
@Service('calculator@1.0.0')
class Calculator {
@Public()
add(a: number, b: number): number {
return a + b;
}
@Public()
multiply(a: number, b: number): number {
return a * b;
}
@Public({ readonly: true })
version: string = '1.0.0';
}
const server = await Netron.create({
listenHost: 'localhost',
listenPort: 8080,
taskTimeout: 5000,
connectTimeout: 5000,
requestTimeout: 5000,
streamTimeout: 5000,
allowServiceEvents: true,
maxReconnectAttempts: 5
});
await server.peer.exposeService(new Calculator());
Connecting from Client
import { Netron } from '@devgrid/netron';
const client = await Netron.create();
const remotePeer = await client.connect('ws://localhost:8080');
const calculator = await remotePeer.queryInterface<ICalculator>('calculator@1.0.0');
const latestCalculator = await peer.queryInterface<ICalculator>('calculator');
const sum = await calculator.add(5, 3);
const product = await calculator.multiply(4, 2);
const version = await calculator.version;
Advanced Features
Event Bus
@Service('notifications')
class NotificationService {
@Public()
async broadcast(message: string) {
await this.netron.emitParallel('notification', { message });
}
}
remotePeer.subscribe('notification', (data) => {
console.log('New notification:', data.message);
});
await netron.emitParallel('event');
await netron.emitSerial('event');
await netron.emitReduce('event');
await netron.emitReduceRight('event');
Streaming Support
@Service('fileService')
class FileService {
@Public()
async streamFile(filename: string) {
const stream = createReadStream(filename);
return new ReadableStream(stream);
}
@Public()
async uploadFile(filename: string) {
const stream = createWriteStream(filename);
return new WritableStream(stream);
}
}
const fileService = await remotePeer.queryInterface('fileService');
const readStream = await fileService.streamFile('large.file');
for await (const chunk of readStream) {
console.log('Received chunk:', chunk);
}
Task Management
netron.addTask(async function pingTask(peer) {
return { status: 'ok', timestamp: Date.now() };
});
const result = await remotePeer.runTask('pingTask');
Service Decorators
@Service('users')
class UserService {
@Public()
async getUser(id: number): Promise<User> {
return this.database.findUser(id);
}
@Public({ readonly: true })
currentUser: User;
private async validateUser(user: User) {
}
}
Configuration Options
interface NetronOptions {
id?: string;
listenHost?: string;
listenPort?: number;
taskTimeout?: number;
taskOverwriteStrategy?: 'replace' | 'skip' | 'throw';
connectTimeout?: number;
requestTimeout?: number;
streamTimeout?: number;
allowServiceEvents?: boolean;
maxReconnectAttempts?: number;
}
API Reference
Netron Class
class Netron {
static create(options?: NetronOptions): Promise<Netron>;
connect(address: string): Promise<RemotePeer>;
disconnect(peerId: string): void;
addTask(fn: Task): string;
getServiceNames(): string[];
emitParallel(event: string, ...args: any[]): Promise<void>;
emitSerial(event: string, ...args: any[]): Promise<void>;
emitReduce(event: string, ...args: any[]): Promise<any>;
emitReduceRight(event: string, ...args: any[]): Promise<any>;
}
Service Decorator Options
interface ServiceMetadata {
name: string;
version: string;
properties: Record<string, PropertyInfo>;
methods: Record<string, MethodInfo>;
}
interface PropertyInfo {
type: string;
readonly: boolean;
}
interface MethodInfo {
type: string;
arguments: ArgumentInfo[];
}
interface ArgumentInfo {
index: number;
type: string;
}
RemotePeer Methods
class RemotePeer {
queryInterface<T>(qualifiedName: string): Promise<T>;
queryInterfaceByDefId<T>(defId: string, def?: Definition): T;
subscribe(eventName: string, handler: EventSubscriber): Promise<void>;
unsubscribe(eventName: string, handler: EventSubscriber): Promise<void>;
runTask(name: string, ...args: any[]): Promise<any>;
disconnect(): void;
getServiceNames(): string[];
}
Performance Considerations
- Uses MessagePack for efficient serialization
- Supports parallel execution of event handlers
- Automatic cleanup of unused services
- Memory-efficient stream handling
- Connection pooling for multiple peers
- Automatic reconnection with exponential backoff
- Efficient binary protocol for WebSocket communication
Browser Compatibility
- Modern browsers (Chrome, Firefox, Safari, Edge)
- WebSocket support required
- ES2018+ features used
- Some features may require polyfills in older browsers
License
MIT
Credits
Built with: