Arterial is a transport-agnostic RPC and Event bridge designed to create resilient, "venous" communication graphs. It allows you to invoke methods and propagate events across boundaries (processes, threads, or networks) without your application logic knowing how the message travels.
It is designed to support high-availability failover (e.g., swapping from a local MessagePort to a remote WebSocket) without interrupting the application flow.
- Transport Agnostic: Run the exact same logic over
MessagePort(Electron/Workers) orWebSocket(Network). - Venous Graph: Nodes are isolated by a
venousId, allowing multiple private networks to coexist on the same transport. - Typed RPC: Strict TypeScript support for method invocation and return types.
- Resilience: (Coming Soon) Automatic failover to backup transports if the primary "artery" is severed.
npm install arterialArterial uses a Stem (Originator/Server) and Consumer (Receiver/Client) model to establish the connection, but once connected, communication is bidirectional.
This acts as the "Server" or the provider of the heavy compute.
import { WebSocketServer } from 'ws';
import createArterial, { websocketStem } from 'arterial';
// 1. Setup the physical transport
const wss = new WebSocketServer({ port: 8080 });
const stemTransport = websocketStem({ wss });
// 2. Create the Arterial Node
const computeNode = createArterial({
id: 'compute-node-1',
venousId: 'sys-core-v1', // The unique ID of this "vein"
primaryDestinationId: 'client-app',
transports: [stemTransport]
});
// 3. Register methods other nodes can call
computeNode.registerMethod('math-sum', (numbers: number[]) => {
return numbers.reduce((a, b) => a + b, 0);
});
// 4. Open the artery
await computeNode.init();
console.log('Compute Node Ready');This acts as the "Client" or the UI thread.
import createArterial, { websocketConsumer } from 'arterial';
// 1. Setup the transport (Dialing the Stem)
const consumerTransport = websocketConsumer({ url: 'ws://localhost:8080' });
// 2. Create the Arterial Node
const appNode = createArterial({
id: 'client-app',
venousId: 'sys-core-v1', // Must match the Stem!
primaryDestinationId: 'compute-node-1',
transports: [consumerTransport]
});
// 3. Open the artery
await appNode.init();
// 4. Invoke a method on the remote node
// Generic: <MethodSignature> (DestinationID, MethodName, Args)
const result = await appNode.invoke<(nums: number[]) => number>('compute-node-1', 'math-sum', [[10, 20, 30]]);
console.log(result); // 60Instead of using string-based invocation every time, you can create a stable proxy function for cleaner code:
// Create a reusable function handle
const sumRemote = appNode.method<typeof localSumSignature>('compute-node-1', 'math-sum');
// Use it like a normal async function
const total = await sumRemote([1, 2, 3]);| Option | Type | Description |
|---|---|---|
id |
string |
The unique identifier for this specific node. |
venousId |
string |
The network ID. Nodes with different venousIds ignore each other. |
primaryDestinationId |
string |
The default target for initial handshakes. |
transports |
Transport[] |
An array of initialized transports (e.g., websocketStem, messagePortConsumer). |
Arterial ships with two core transports:
Ideal for Electron UtilityProcess, Worker threads, or Iframe communication.
import { messagePortStem, messagePortConsumer } from 'arterial/transports/messagePort';Ideal for communicating between a browser and a backend service, or between microservices.
import { websocketStem, websocketConsumer } from 'arterial/transports/websocket';