# @push.rocks/smartipc ๐Ÿš€ **Rock-solid IPC for Node.js with zero dependencies** [![npm version](https://img.shields.io/npm/v/@push.rocks/smartipc.svg)](https://www.npmjs.com/package/@push.rocks/smartipc) [![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)](https://www.typescriptlang.org/) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./license) SmartIPC delivers bulletproof Inter-Process Communication for Node.js applications. Built for real-world production use, it handles all the edge cases that make IPC tricky - automatic reconnection, race conditions, heartbeat monitoring, and clean shutdowns. All with **zero external dependencies** and full TypeScript support. ## ๐ŸŽฏ Why SmartIPC? - **Zero Dependencies** - Pure Node.js implementation using native modules - **Battle-tested Reliability** - Automatic reconnection, graceful degradation, and timeout handling - **Type-Safe** - Full TypeScript support with generics for compile-time safety - **CI/Test Ready** - Built-in helpers and race condition prevention for testing - **Observable** - Real-time metrics, connection tracking, and health monitoring - **Multiple Patterns** - Request/Response, Pub/Sub, and Fire-and-Forget messaging ## ๐Ÿ“ฆ Installation ```bash npm install @push.rocks/smartipc # or pnpm add @push.rocks/smartipc # or yarn add @push.rocks/smartipc ``` ## ๐Ÿš€ Quick Start ```typescript import { SmartIpc } from '@push.rocks/smartipc'; // Create a server const server = SmartIpc.createServer({ id: 'my-service', socketPath: '/tmp/my-service.sock', autoCleanupSocketFile: true // Clean up stale sockets automatically }); // Handle incoming messages server.onMessage('greet', async (data, clientId) => { console.log(`Client ${clientId} says:`, data.message); return { response: `Hello ${data.name}!` }; }); // Start the server await server.start({ readyWhen: 'accepting' }); // Wait until fully ready console.log('Server is ready to accept connections! โœจ'); // Create a client const client = SmartIpc.createClient({ id: 'my-service', socketPath: '/tmp/my-service.sock', connectRetry: { enabled: true, maxAttempts: 10 } }); // Connect with automatic retry await client.connect(); // Send a request and get a response const response = await client.request('greet', { name: 'World', message: 'Hi there!' }); console.log('Server said:', response.response); // "Hello World!" ``` ## ๐ŸŽฎ Core Concepts ### Transport Types SmartIPC supports multiple transport mechanisms, automatically selecting the best one for your platform: ```typescript // TCP Socket (cross-platform, network-capable) const tcpServer = SmartIpc.createServer({ id: 'tcp-service', host: 'localhost', port: 9876 }); // Unix Domain Socket (Linux/macOS, fastest local IPC) const unixServer = SmartIpc.createServer({ id: 'unix-service', socketPath: '/tmp/my-app.sock' }); // Windows Named Pipe (Windows optimal) // Automatically used on Windows when socketPath is provided const windowsServer = SmartIpc.createServer({ id: 'pipe-service', socketPath: '\\\\.\\pipe\\my-app-pipe' }); ``` ### Message Patterns #### ๐Ÿ”ฅ Fire and Forget Send messages without waiting for a response: ```typescript // Server server.onMessage('log', (data, clientId) => { console.log(`[${clientId}] ${data.level}:`, data.message); // No return needed }); // Client await client.sendMessage('log', { level: 'info', message: 'User logged in', timestamp: Date.now() }); ``` #### ๐Ÿ“ž Request/Response RPC-style communication with type safety: ```typescript interface UserRequest { userId: string; fields?: string[]; } interface UserResponse { id: string; name: string; email?: string; createdAt: number; } // Server server.onMessage('getUser', async (data) => { const user = await db.getUser(data.userId); return { id: user.id, name: user.name, email: data.fields?.includes('email') ? user.email : undefined, createdAt: user.createdAt }; }); // Client - with timeout const user = await client.request( 'getUser', { userId: '123', fields: ['email'] }, { timeout: 5000 } ); ``` #### ๐Ÿ“ข Pub/Sub Pattern Topic-based message broadcasting: ```typescript // Subscribers const subscriber1 = SmartIpc.createClient({ id: 'events-service', socketPath: '/tmp/events.sock' }); await subscriber1.connect(); await subscriber1.subscribe('user.login', (data) => { console.log('User logged in:', data); }); // Publisher const publisher = SmartIpc.createClient({ id: 'events-service', socketPath: '/tmp/events.sock' }); await publisher.connect(); await publisher.publish('user.login', { userId: '123', ip: '192.168.1.1', timestamp: Date.now() }); ``` ## ๐Ÿ’ช Advanced Features ### ๐Ÿ Server Readiness Detection Eliminate race conditions in tests and production: ```typescript const server = SmartIpc.createServer({ id: 'my-service', socketPath: '/tmp/my-service.sock', autoCleanupSocketFile: true }); // Option 1: Wait for full readiness await server.start({ readyWhen: 'accepting' }); // Server is now FULLY ready to accept connections // Option 2: Use ready event server.on('ready', () => { console.log('Server is ready!'); startClients(); }); await server.start(); // Option 3: Check readiness state if (server.getIsReady()) { console.log('Ready to rock! ๐ŸŽธ'); } ``` ### ๐Ÿ”„ Smart Connection Retry Never lose messages due to temporary connection issues: ```typescript const client = SmartIpc.createClient({ id: 'resilient-client', socketPath: '/tmp/service.sock', connectRetry: { enabled: true, initialDelay: 100, // Start with 100ms maxDelay: 1500, // Cap at 1.5 seconds maxAttempts: 20, // Try 20 times totalTimeout: 15000 // Give up after 15 seconds total }, registerTimeoutMs: 8000 // Registration handshake timeout }); // Will retry automatically if server isn't ready yet await client.connect({ waitForReady: true, // Wait for server to exist waitTimeout: 10000 // Wait up to 10 seconds }); ``` ### ๐Ÿ›‘ Client-Only Mode (No Auto-Start) In some setups (CLI + long-running daemon), you want clients to fail fast when no server is available, rather than implicitly becoming the server. Enable client-only mode to prevent the โ€œclient becomes serverโ€ fallback for Unix domain sockets and Windows named pipes. ```typescript // Strict client that never auto-starts a server on connect failure const client = SmartIpc.createClient({ id: 'my-service', socketPath: '/tmp/my-service.sock', clientId: 'my-cli', clientOnly: true, // NEW: disable auto-start fallback connectRetry: { enabled: false } // optional: fail fast }); try { await client.connect(); } catch (err) { // With clientOnly: true, errors become descriptive // e.g. "Server not available (ENOENT); clientOnly prevents auto-start" console.error(err.message); } ``` - Default: `clientOnly` is `false` to preserve backward compatibility. - Env override: set `SMARTIPC_CLIENT_ONLY=1` to enforce client-only behavior without code changes. - Note: `SmartIpc.waitForServer()` internally uses `clientOnly: true` for safe probing. ### ๐Ÿ’“ Graceful Heartbeat Monitoring Keep connections alive without crashing on timeouts: ```typescript const server = SmartIpc.createServer({ id: 'monitored-service', socketPath: '/tmp/monitored.sock', heartbeat: true, heartbeatInterval: 3000, heartbeatTimeout: 10000, heartbeatInitialGracePeriodMs: 5000, // Grace period for startup heartbeatThrowOnTimeout: false // Emit event instead of throwing }); server.on('heartbeatTimeout', (clientId) => { console.log(`Client ${clientId} heartbeat timeout - will handle gracefully`); }); // Client configuration const client = SmartIpc.createClient({ id: 'monitored-service', socketPath: '/tmp/monitored.sock', heartbeat: true, heartbeatInterval: 3000, heartbeatTimeout: 10000, heartbeatInitialGracePeriodMs: 5000, heartbeatThrowOnTimeout: false }); client.on('heartbeatTimeout', () => { console.log('Heartbeat timeout detected, reconnecting...'); // Handle reconnection logic }); ``` ### ๐Ÿงน Automatic Socket Cleanup Never worry about stale socket files: ```typescript const server = SmartIpc.createServer({ id: 'clean-service', socketPath: '/tmp/service.sock', autoCleanupSocketFile: true, // Remove stale socket on start socketMode: 0o600 // Set socket permissions (Unix only) }); // Socket file will be cleaned up automatically on start await server.start(); ``` ### ๐Ÿ“Š Real-time Metrics Monitor your IPC performance: ```typescript // Server stats const serverStats = server.getStats(); console.log({ isRunning: serverStats.isRunning, connectedClients: serverStats.connectedClients, totalConnections: serverStats.totalConnections, metrics: { messagesSent: serverStats.metrics.messagesSent, messagesReceived: serverStats.metrics.messagesReceived, errors: serverStats.metrics.errors } }); // Client stats const clientStats = client.getStats(); console.log({ connected: clientStats.connected, reconnectAttempts: clientStats.reconnectAttempts, metrics: clientStats.metrics }); // Get specific client info const clientInfo = server.getClientInfo('client-123'); console.log({ connectedAt: new Date(clientInfo.connectedAt), lastActivity: new Date(clientInfo.lastActivity), metadata: clientInfo.metadata }); ``` ### ๐ŸŽฏ Broadcasting Send messages to multiple clients: ```typescript // Broadcast to all connected clients await server.broadcast('announcement', { message: 'Server will restart in 5 minutes', severity: 'warning' }); // Send to specific clients await server.broadcastTo( ['client-1', 'client-2'], 'private-message', { content: 'This is just for you two' } ); // Send to one client await server.sendToClient('client-1', 'direct', { data: 'Personal message' }); ``` ## ๐Ÿงช Testing Utilities SmartIPC includes powerful helpers for testing: ### Wait for Server ```typescript import { SmartIpc } from '@push.rocks/smartipc'; // Start your server in another process const serverProcess = spawn('node', ['server.js']); // Wait for it to be ready await SmartIpc.waitForServer({ socketPath: '/tmp/test.sock', timeoutMs: 10000 }); // Now safe to connect clients const client = SmartIpc.createClient({ id: 'test-client', socketPath: '/tmp/test.sock' }); await client.connect(); ``` ### Spawn and Connect ```typescript // Helper that spawns a server and connects a client const { client, serverProcess } = await SmartIpc.spawnAndConnect({ serverScript: './server.js', socketPath: '/tmp/test.sock', clientId: 'test-client', connectRetry: { enabled: true, maxAttempts: 10 } }); // Use the client const response = await client.request('ping', {}); // Cleanup await client.disconnect(); serverProcess.kill(); ``` ## ๐ŸŽญ Event Handling SmartIPC provides comprehensive event emitters: ```typescript // Server events server.on('start', () => console.log('Server started')); server.on('ready', () => console.log('Server ready for connections')); server.on('clientConnect', (clientId, metadata) => { console.log(`Client ${clientId} connected with metadata:`, metadata); }); server.on('clientDisconnect', (clientId) => { console.log(`Client ${clientId} disconnected`); }); server.on('error', (error, clientId) => { console.error(`Error from ${clientId}:`, error); }); // Client events client.on('connect', () => console.log('Connected to server')); client.on('disconnect', () => console.log('Disconnected from server')); client.on('reconnecting', (attempt) => { console.log(`Reconnection attempt ${attempt}`); }); client.on('error', (error) => { console.error('Client error:', error); }); client.on('heartbeatTimeout', (error) => { console.warn('Heartbeat timeout:', error); }); ``` ## ๐Ÿ›ก๏ธ Error Handling Robust error handling with detailed error information: ```typescript // Client-side error handling try { const response = await client.request('riskyOperation', data, { timeout: 5000 }); } catch (error) { if (error.message.includes('timeout')) { console.error('Request timed out'); } else if (error.message.includes('Failed to register')) { console.error('Could not register with server'); } else { console.error('Unknown error:', error); } } // Server-side error boundaries server.onMessage('process', async (data, clientId) => { try { return await riskyProcessing(data); } catch (error) { console.error(`Processing failed for ${clientId}:`, error); throw error; // Will be sent back to client as error } }); ``` ## ๐Ÿ—๏ธ Architecture SmartIPC uses a clean, layered architecture: ``` โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Your Application โ”‚ โ”‚ (Business logic) โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ†• โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ IpcServer / IpcClient โ”‚ โ”‚ (High-level API, Message routing) โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ†• โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ IpcChannel โ”‚ โ”‚ (Connection management, Heartbeat, โ”‚ โ”‚ Reconnection, Request/Response) โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ†• โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” โ”‚ Transport Layer โ”‚ โ”‚ (TCP, Unix Socket, Named Pipe) โ”‚ โ”‚ (Framing, buffering, I/O) โ”‚ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` ## ๐ŸŽฏ Common Use Cases ### Microservices Communication ```typescript // API Gateway const gateway = SmartIpc.createServer({ id: 'api-gateway', socketPath: '/tmp/gateway.sock' }); // User Service const userService = SmartIpc.createClient({ id: 'api-gateway', socketPath: '/tmp/gateway.sock', clientId: 'user-service' }); // Order Service const orderService = SmartIpc.createClient({ id: 'api-gateway', socketPath: '/tmp/gateway.sock', clientId: 'order-service' }); ``` ### Worker Process Management ```typescript // Main process const server = SmartIpc.createServer({ id: 'main', socketPath: '/tmp/workers.sock' }); server.onMessage('job-complete', (result, workerId) => { console.log(`Worker ${workerId} completed job:`, result); }); // Worker process const worker = SmartIpc.createClient({ id: 'main', socketPath: '/tmp/workers.sock', clientId: `worker-${process.pid}` }); await worker.sendMessage('job-complete', { jobId: '123', result: processedData }); ``` ### Real-time Event Distribution ```typescript // Event bus const eventBus = SmartIpc.createServer({ id: 'event-bus', socketPath: '/tmp/events.sock' }); // Services subscribe to events const analyticsService = SmartIpc.createClient({ id: 'event-bus', socketPath: '/tmp/events.sock' }); await analyticsService.subscribe('user.*', (event) => { trackEvent(event); }); ``` ## ๐Ÿ“ˆ Performance SmartIPC is optimized for high throughput and low latency: | Transport | Messages/sec | Avg Latency | Use Case | |-----------|-------------|-------------|----------| | Unix Socket | 150,000+ | < 0.1ms | Local high-performance IPC (Linux/macOS) | | Named Pipe | 120,000+ | < 0.15ms | Windows local IPC | | TCP (localhost) | 100,000+ | < 0.2ms | Local network-capable IPC | | TCP (network) | 50,000+ | < 1ms | Distributed systems | - **Memory efficient**: Streaming support for large payloads - **CPU efficient**: Event-driven, non-blocking I/O ## ๐Ÿ”ง Requirements - Node.js >= 14.x - TypeScript >= 4.x (for development) - Unix-like OS (Linux, macOS) or Windows ## License and Legal Information This repository contains open-source code that is licensed under the MIT License. A copy of the MIT License can be found in the [license](license) file within this repository. **Please note:** The MIT License does not grant permission to use the trade names, trademarks, service marks, or product names of the project, except as required for reasonable and customary use in describing the origin of the work and reproducing the content of the NOTICE file. ### Trademarks This project is owned and maintained by Task Venture Capital GmbH. The names and logos associated with Task Venture Capital GmbH and any related products or services are trademarks of Task Venture Capital GmbH and are not included within the scope of the MIT license granted herein. Use of these trademarks must comply with Task Venture Capital GmbH's Trademark Guidelines, and any usage must be approved in writing by Task Venture Capital GmbH. ### Company Information Task Venture Capital GmbH Registered at District court Bremen HRB 35230 HB, Germany For any legal inquiries or if you require further information, please contact us via email at hello@task.vc. By using this repository, you acknowledge that you have read this section, agree to comply with its terms, and understand that the licensing of the code does not imply endorsement by Task Venture Capital GmbH of any derivative works.