Vix.cpp docs moved to vixcpp.com
Skip to content

Client

Client is the main public object of the Softadastra JavaScript SDK.

It gives JavaScript applications one stable interface for local storage, persistent options, sync state, sync ticks, transport, discovery, peers, and node metadata.

Most JavaScript applications should start with:

js
import { Client, ClientOptions } from "@softadastra/sdk";

const client = new Client(
  ClientOptions.local("node-a"),
);

Why Client exists

Softadastra is modular internally. The SDK exposes a simpler public API so applications do not need to manually wire store, sync, transport, discovery, metadata, and errors.

Client gives you one entry point for all of these.

Basic lifecycle

txt
construct

open

use

close

Example:

js
import { Client, ClientOptions } from "@softadastra/sdk";

const client = new Client(
  ClientOptions.local("node-a"),
);

const opened = await client.open();

if (opened.isErr()) {
  console.error(opened.error().message);
  process.exit(1);
}

await client.put("hello", "world");

await client.close();

Create a client

js
const options = ClientOptions.local("node-a");

options.enableWal = true;
options.walPath = "data/node-a.wal";
options.autoFlush = true;

options.enableTransport = true;
options.transportHost = "127.0.0.1";
options.transportPort = 4041;

options.enableDiscovery = false;

const client = new Client(options);

Open the client

js
const result = await client.open();

if (result.isErr()) {
  console.error(`failed to open client: ${result.error().message}`);
  process.exit(1);
}

Opening the client prepares the local SDK runtime. Opening does not automatically start transport or discovery — those are explicit operations.

Close the client

js
await client.close();

A good lifecycle is:

js
const client = new Client(options);

const opened = await client.open();

if (opened.isErr()) {
  console.error(opened.error().message);
  process.exit(1);
}

// use client

await client.close();

Local store methods

These operations are local-first and should not require a server, peer, transport, discovery, or cloud access.

put

js
const result = await client.put("profile/name", "Ada");

if (result.isErr()) {
  console.error(result.error().message);
  await client.close();
  process.exit(1);
}

get

js
const result = await client.get("profile/name");

if (result.isOk()) {
  console.log(result.value().toString());
}

if (result.isErr()) {
  console.log(result.error().codeString());
}

A missing key is a normal store error, not a crash.

remove

js
const result = await client.remove("profile/name");

if (result.isErr()) {
  console.error(result.error().message);
}

contains

js
if (client.contains("settings/theme")) {
  console.log("theme exists");
}

size

js
console.log(client.size());

empty

js
if (client.empty()) {
  console.log("store is empty");
}

Working with values

The SDK accepts strings directly:

js
await client.put("message", "hello");

You can also use Value explicitly:

js
import { Value } from "@softadastra/sdk";

const value = Value.fromString("hello");

await client.put("message", value);

Read values can be converted back to strings:

js
const result = await client.get("message");

if (result.isOk()) {
  console.log(result.value().toString());
}

Sync methods

syncStateInfo

js
const state = await client.syncStateInfo();

if (state.isOk()) {
  console.log(`outbox : ${state.value().outboxSize}`);
  console.log(`queued : ${state.value().queuedCount}`);
  console.log(`failed : ${state.value().failedCount}`);
}

tick

js
const tick = await client.tick();

if (tick.isErr()) {
  console.error(tick.error().message);
  await client.close();
  process.exit(1);
}

console.log(`retried : ${tick.value().retriedCount}`);
console.log(`pruned  : ${tick.value().prunedCount}`);
console.log(`batch   : ${tick.value().batchSize}`);

A tick can retry expired operations, produce the next batch, and prune completed work when requested.

If supported:

js
const tick = await client.tick({ prune: true });

Transport methods

startTransport

js
const result = await client.startTransport();

if (result.isErr()) {
  console.error(`failed to start transport: ${result.error().message}`);
  await client.close();
  process.exit(1);
}

Transport requires transport options:

js
options.enableTransport = true;
options.transportHost = "127.0.0.1";
options.transportPort = 4041;

transportRunning

js
if (client.transportRunning()) {
  console.log("transport is running");
}

connect

js
import { Peer } from "@softadastra/sdk";

const peer = new Peer("node-b", "127.0.0.1", 4042);

const result = await client.connect(peer);

if (result.isErr()) {
  console.log(`peer connection failed: ${result.error().message}`);
}

A failed connection should not prevent local writes:

js
await client.put("draft/1", "still local");

stopTransport

If exposed:

js
await client.stopTransport();

Discovery methods

startDiscovery

js
const result = await client.startDiscovery();

if (result.isErr()) {
  console.error(`failed to start discovery: ${result.error().message}`);
  await client.close();
  process.exit(1);
}

discoveryRunning

js
if (client.discoveryRunning()) {
  console.log("discovery is running");
}

peers

js
const peers = await client.peers();

if (peers.isOk()) {
  for (const peer of peers.value()) {
    console.log(`${peer.nodeId} ${peer.host}:${peer.port}`);
  }
}

No peers is a valid state. Local writes should still work.

stopDiscovery

If exposed:

js
await client.stopDiscovery();

Metadata methods

refreshNodeInfo

js
const result = await client.refreshNodeInfo();

if (result.isErr()) {
  console.error(result.error().message);
  await client.close();
  process.exit(1);
}

const node = result.value();

console.log(`node id      : ${node.nodeId}`);
console.log(`display name : ${node.displayName}`);
console.log(`hostname     : ${node.hostname}`);
console.log(`os           : ${node.osName}`);
console.log(`version      : ${node.version}`);
console.log(`uptime ms    : ${node.uptime_ms()}`);

nodeInfo

If exposed, returns cached local node information without forcing a refresh:

js
const info = await client.nodeInfo();

Error handling pattern

js
const result = await client.get("key");

if (result.isErr()) {
  console.error(result.error().message);
  await client.close();
  process.exit(1);
}

const value = result.value();

Avoid assuming success:

js
const value = (await client.get("key")).value(); // wrong — hides failure

Common result methods: isOk(), isErr(), value(), error().

Common error fields: message, codeString().

Client method reference

MethodPurpose
open()Initialize the client runtime
close()Close the client runtime
put(key, value)Write a local value
get(key)Read a local value
remove(key)Remove a local value
contains(key)Check if a key exists
size()Return local store size
empty()Check if local store is empty
syncStateInfo()Return sync state
tick()Run one sync tick
startTransport()Start transport
stopTransport()Stop transport, if available
transportRunning()Check transport state
connect(peer)Connect to a peer
startDiscovery()Start discovery
stopDiscovery()Stop discovery, if available
discoveryRunning()Check discovery state
peers()List known peers
nodeInfo()Read cached node metadata, if available
refreshNodeInfo()Refresh and read node metadata

CamelCase versus snake_case

Prefer JavaScript-style camelCase:

js
await client.startDiscovery();
await client.syncStateInfo();
await client.refreshNodeInfo();

Use snake_case aliases only if you need compatibility with examples or internal naming:

js
await client.start_discovery();
await client.sync_state_info();
await client.refresh_node_info();

Common mistakes

Forgetting open()

js
// wrong
const client = new Client(options);
await client.put("key", "value");

// correct
const client = new Client(options);

const opened = await client.open();

if (opened.isErr()) {
  process.exit(1);
}

await client.put("key", "value");

Ignoring result values

js
// wrong
const value = await client.get("key");
console.log(value.value().toString());

// correct
const value = await client.get("key");

if (value.isErr()) {
  console.error(value.error().message);
  await client.close();
  process.exit(1);
}

console.log(value.value().toString());

Expecting peers to be required for local writes

js
// this should still work with no peers:
await client.put("draft/1", "hello");
await client.get("draft/1");

Summary

Client is the main JavaScript SDK API.

It provides lifecycle, local store, sync state, manual ticks, transport, discovery, peers, metadata, and explicit errors.

The most important pattern is:

js
const client = new Client(options);

const opened = await client.open();

if (opened.isErr()) {
  process.exit(1);
}

// use client

await client.close();

Next step

Continue with client options:

Go to Client Options

Released under the Apache License 2.0.