@m4ike1/ion-client
v0.1.1
Published
Transport-neutral client for remote ion sessions over framed CBOR bytes
Maintainers
Readme
@m4ike1/ion-client
Transport-neutral client for remote ion sessions over framed CBOR bytes. Bring your own ordered byte transport (WebSocket, Unix socket, or equivalent); the client handles the hello handshake, serverId verification, request correlation, service subscriptions, and attachment routing.
import { Client, type ByteTransportFactory } from "@m4ike1/ion-client";
const transportFactory: ByteTransportFactory = (handlers) => {
// Open a fresh authenticated connection; call
// handlers.onData(chunk) / onClose() / onError(err) as events arrive.
return { send: async (chunk) => {}, close: () => {} };
};
const client = await Client.connect({
serverId: "01234567-89ab-4def-8123-456789abcdef",
transportFactory,
});
const result = await client.request(
{ serverId: client.hello!.serverId },
{ serviceId: "example.service", member: "read", args: [] },
);What it does
- Verifies the physical endpoint reports the expected logical
serverIdduring the hello handshake. A mismatch fails the connection. - Sends low-level Chord service calls via
request()andsubscribeService()against an explicit routed target:{ serverId }for server-wide calls, or the full live{ serverId, sessionId, attachmentId }for session calls. - Tracks the live attachment from out-of-band server messages (
client.attachment,onAttachmentChange()). The server-generatedattachmentIdrejects delayed frames after switching or reattaching. - Adapts to typed Chord service bindings via
createClientServiceTransport(client, getTarget). The client itself never constructs typed proxies or interprets application contracts.
Lifecycle
Client.connect(options)constructs and connects;new Client(options)+connect()defers the handshake.connect()rejects while alreadyconnecting/connected.- On disconnect or
dispose(), pending requests reject locally; accepted work may still complete remotely. The client clears the live attachment, drops service listeners, and never reconnects or replays automatically. Callreconnect(), re-attach through the application's management service, and repeat only operations known to be safe. disconnect()anddispose()are idempotent.dispose()also supportsawait using.connectionStateisdisconnected | connecting | connected; observe withonConnectionStateChange(). Listener errors go toonListenerErrorand never corrupt client state.
Errors
ServerError(with.code) for server-rejected calls, including handshakehello_errorrejections.DisconnectedErrorwhen sending while not connected, or when the transport closes/fails.ClientDisposedErrorafterdispose().- Abort a single call with an
AbortSignal; the client sends acancelframe when the request was already sent.
Limits
ClientOptions.maxFrameLengthbounds inbound protocol payloads.- Unix transports add
maxPendingBytesfor queued output. Configure matching limits on both peers.
Unix-domain sockets
Node.js and Bun consumers can use the separate entrypoint:
import { Client } from "@m4ike1/ion-client";
import { createUnixTransportFactory, discoverUnixServers } from "@m4ike1/ion-client/unix";
const client = new Client({
serverId: "01234567-89ab-4def-8123-456789abcdef",
transportFactory: createUnixTransportFactory({ path: "/tmp/ion.sock" }),
});
await client.connect();
const routes = await discoverUnixServers({ directory: "/run/user/1000/ion" });
// [{ serverId: "...", path: "/run/user/1000/ion/<serverId>.sock" }]Discovery probes <serverId>.sock files (at most 16 concurrently, 1s default per-probe timeout via timeoutMs), ignores malformed/non-socket/stale/mismatched endpoints, and throws on unexpected filesystem or socket errors. Not supported on Windows.
Further reading
docs/reference.md— full API reference forClient, transports, subscriptions, and errors.docs/how-to.md— recipes: custom transports, Unix setup and discovery, subscriptions, cancellation, reconnect.docs/concepts.md— why transport neutrality,serverIdverification, and attachment routing work the way they do.
