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

JavaScript API Reference

This page is the compact reference for the Softadastra JavaScript SDK.

Use it when you already understand the JavaScript SDK and need to quickly check public types, method names, option fields, result fields, or common usage patterns.

For explanations, read the SDK JS section first:

The core rule is:

txt
The JavaScript SDK exposes a stable async API over the Softadastra runtime model.

Package

The main package is:

txt
@softadastra/sdk

Use the main import:

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

Other public types can be imported when needed:

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

ESM requirement

The JavaScript SDK uses modern JavaScript modules.

Your package.json should include:

json
{
  "type": "module"
}

Then you can run files with:

bash
node main.js

Main public types

The JavaScript SDK exposes these main public types:

TypePurpose
ClientMain SDK object
ClientOptionsRuntime configuration
ValueLocal store value
PeerRemote peer description
NodeInfoLocal node metadata
ResultSuccess or failure result
SoftadastraErrorStructured error
SyncResultSync state result value
TickResultSync tick result value

Minimal client

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

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

const opened = await client.open();

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

const written = await client.put("app/name", "Softadastra");

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

const value = await client.get("app/name");

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

await client.close();

Client

Client is the main SDK object.

It provides one async public API for local store, persistence, sync, transport, discovery, peers, and metadata.

Lifecycle methods

MethodPurpose
open()Open and initialize the local runtime
close()Close the runtime and release resources

Example:

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();

Store methods

MethodPurpose
put(key, value)Write or update a local value
get(key)Read a local value
remove(key)Remove a local value
contains(key)Check whether a key exists
size()Return the number of local entries
empty()Check whether the local store is empty

Example:

js
await client.put("settings/theme", "dark");

const value = await client.get("settings/theme");

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

Sync methods

MethodPurpose
syncStateInfo()Return current synchronization state
sync_state_info()Snake_case alias, if exposed
tick()Run one sync tick
tick({ prune: true })Run one sync tick and prune completed work, if supported

Example:

js
const state = await client.syncStateInfo();

if (state.isOk()) {
  console.log(state.value().outboxSize);
}

const tick = await client.tick();

if (tick.isOk()) {
  console.log(tick.value().batchSize);
}

Transport methods

MethodPurpose
startTransport()Start local transport
start_transport()Snake_case alias, if exposed
stopTransport()Stop transport, if exposed
stop_transport()Snake_case alias, if exposed
transportRunning()Check whether transport is running
transport_running()Snake_case alias, if exposed
connect(peer)Connect to a peer

Example:

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

const connected = await client.connect(peer);

if (connected.isErr()) {
  console.log(connected.error().message);
}

Discovery methods

MethodPurpose
startDiscovery()Start peer discovery
start_discovery()Snake_case alias, if exposed
stopDiscovery()Stop discovery, if exposed
stop_discovery()Snake_case alias, if exposed
discoveryRunning()Check whether discovery is running
discovery_running()Snake_case alias, if exposed
peers()List known peers

Example:

js
const peers = await client.peers();

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

Metadata methods

MethodPurpose
refreshNodeInfo()Refresh and return local node metadata
refresh_node_info()Snake_case alias, if exposed
nodeInfo()Return cached node metadata, if exposed
node_info()Snake_case alias, if exposed

Example:

js
const info = await client.refreshNodeInfo();

if (info.isOk()) {
  console.log(info.value().nodeId);
  console.log(info.value().displayName);
  console.log(info.value().hostname);
  console.log(info.value().osName);
  console.log(info.value().version);
}

ClientOptions

ClientOptions configures the SDK runtime.

Common pattern:

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

const client = new Client(options);

Factory helpers

HelperPurpose
ClientOptions.local(nodeId)Create local options
ClientOptions.persistent(nodeId, walPath)Create WAL-backed persistent options
ClientOptions.memoryOnly(nodeId)Create memory-only options, if exposed

Example:

js
const options = ClientOptions.persistent("node-a", "data/node-a.wal");

Identity fields

FieldPurpose
nodeIdLocal node identifier
displayNameHuman-friendly node label
versionRuntime or application version

Example:

js
options.displayName = "Node A";
options.version = "0.1.0";

Persistence fields

FieldPurpose
enableWalEnable WAL-backed persistence
walPathWAL file path
autoFlushFlush WAL writes automatically when configured

Example:

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

Persistent helper:

js
const options = ClientOptions.persistent("node-a", "data/node-a.wal");

options.autoFlush = true;

Transport fields

FieldPurpose
enableTransportEnable transport configuration
transportHostLocal transport bind host
transportPortLocal transport bind port

Example:

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

Discovery fields

FieldPurpose
enableDiscoveryEnable discovery configuration
discoveryHostLocal discovery bind host
discoveryPortLocal discovery bind port
discoveryBroadcastHostDiscovery target host
discoveryBroadcastPortDiscovery target port

Example:

js
options.enableDiscovery = true;
options.discoveryHost = "127.0.0.1";
options.discoveryPort = 5051;
options.discoveryBroadcastHost = "127.0.0.1";
options.discoveryBroadcastPort = 5052;

Common configurations

Local-only

No WAL, no transport, no discovery.

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

options.enableWal = false;
options.enableTransport = false;
options.enableDiscovery = false;

Use for tests, demos, temporary state, and first examples.

Persistent local

WAL enabled, no transport, no discovery.

js
const options = ClientOptions.persistent(
  "node-persistent",
  "data/node-persistent.wal",
);

options.autoFlush = true;
options.enableTransport = false;
options.enableDiscovery = false;

Use when local writes should survive restart.

Transport-enabled

js
const options = ClientOptions.persistent("node-a", "data/node-a.wal");

options.autoFlush = true;

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

options.enableDiscovery = false;

Use when the node should connect to peers manually.

Discovery-enabled

js
const options = ClientOptions.persistent(
  "node-discovery-a",
  "data/node-discovery-a.wal",
);

options.autoFlush = true;

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

options.enableDiscovery = true;
options.discoveryHost = "127.0.0.1";
options.discoveryPort = 5051;
options.discoveryBroadcastHost = "127.0.0.1";
options.discoveryBroadcastPort = 5052;

Use when the node should find peers automatically.

Value

Value represents a local store value.

The SDK accepts strings directly:

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

You can also create a value explicitly:

js
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());
}

Peer

Peer describes another node.

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

Common fields:

FieldPurpose
nodeIdRemote node identifier
hostRemote host
portRemote transport port

Use with:

js
await client.connect(peer);

A failed peer connection should not invalidate local state.

NodeInfo

NodeInfo describes the local node.

Common fields:

FieldPurpose
nodeIdLocal node identifier
displayNameHuman-friendly label
hostnameMachine hostname
osNameOperating system
versionRuntime or app version
capabilitiesSupported runtime capabilities
uptime_ms()Runtime uptime in milliseconds

Example:

js
const result = await client.refreshNodeInfo();

if (result.isOk()) {
  const node = result.value();

  console.log(node.nodeId);
  console.log(node.displayName);
  console.log(node.hostname);
  console.log(node.osName);
  console.log(node.version);
  console.log(node.uptime_ms());
}

Result

Most SDK operations return a Result.

Conceptually:

txt
Result
  -> success: value
  -> failure: SoftadastraError

Common methods:

MethodPurpose
isOk()Check whether the result is successful
isErr()Check whether the result failed
value()Access success value
error()Access error

Correct pattern:

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

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

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

Avoid:

js
const value = (await client.get("app/name")).value();

That assumes the operation succeeded.

SoftadastraError

SoftadastraError describes what failed.

Common fields and methods:

Field or methodPurpose
messageHuman-readable error message
codeString()Stable error code string, when exposed

Example:

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

if (result.isErr()) {
  console.log(`code    : ${result.error().codeString()}`);
  console.log(`message : ${result.error().message}`);
}

Expected output style:

txt
code    : not_found
message : key not found

SyncResult

SyncResult describes current sync state.

Common fields:

FieldMeaning
outboxSizeLocal operations waiting for synchronization
queuedCountOperations ready to be selected for sending
inFlightCountOperations prepared or sent, possibly waiting for ACK
acknowledgedCountOperations confirmed by the remote side
failedCountOperations that exceeded retry policy or hit a sync error
totalRetriesTotal retry attempts
lastSubmittedVersionLast local submitted version, if exposed
lastAppliedRemoteVersionLast remote applied version, if exposed

Example:

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}`);
}

If exposed:

js
if (state.isOk() && state.value().hasFailed()) {
  console.error("sync has failed work");
}

TickResult

TickResult describes the result of one sync tick.

Common fields:

FieldMeaning
retriedCountOperations retried during the tick
prunedCountCompleted entries removed during the tick
batchSizeOperations produced in the current batch

Example:

js
const tick = await client.tick();

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

Local store reference

put

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

Success means the value is available locally.

If persistence is enabled, the operation can also be persisted.

If sync is enabled, the operation can also be tracked for propagation.

get

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

Success returns a Value.

Missing key returns an explicit error.

remove

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

Removes the local value.

If sync is enabled, a delete operation can be tracked for propagation.

contains

js
const exists = client.contains("profile/name");

Returns whether the key exists locally.

size

js
const count = client.size();

Returns the number of local entries.

empty

js
const isEmpty = client.empty();

Returns whether the local store is empty.

Sync reference

syncStateInfo

js
const state = await client.syncStateInfo();

Use it to inspect pending sync work.

sync_state_info

If exposed:

js
const state = await client.sync_state_info();

Snake_case alias for syncStateInfo().

tick

js
const tick = await client.tick();

Runs one sync tick.

tick with pruning

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

Runs one sync tick and asks the runtime to prune completed work, if supported.

Transport reference

startTransport

js
const result = await client.startTransport();

Starts local transport.

Transport requires:

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

start_transport

If exposed:

js
const result = await client.start_transport();

Snake_case alias for startTransport().

transportRunning

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

connect

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

const result = await client.connect(peer);

Connects to a peer.

A connection failure should not make local store data invalid.

Discovery reference

startDiscovery

js
const result = await client.startDiscovery();

Starts peer discovery.

Discovery requires:

js
options.enableDiscovery = true;
options.discoveryHost = "127.0.0.1";
options.discoveryPort = 5051;
options.discoveryBroadcastHost = "127.0.0.1";
options.discoveryBroadcastPort = 5052;

start_discovery

If exposed:

js
const result = await client.start_discovery();

Snake_case alias for startDiscovery().

discoveryRunning

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

peers

js
const result = await client.peers();

Returns known peers.

No peers is a valid state.

Metadata reference

refreshNodeInfo

js
const result = await client.refreshNodeInfo();

Refreshes and returns local node metadata.

refresh_node_info

If exposed:

js
const result = await client.refresh_node_info();

Snake_case alias for refreshNodeInfo().

nodeInfo

If exposed:

js
const result = await client.nodeInfo();

Returns cached node metadata without forcing a refresh.

Common flows

Local-only flow

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

options.enableWal = false;
options.enableTransport = false;
options.enableDiscovery = false;

const client = new Client(options);

const opened = await client.open();

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

await client.put("app/name", "Softadastra");

const value = await client.get("app/name");

await client.close();

Persistent flow

js
const options = ClientOptions.persistent(
  "node-persistent",
  "data/node-persistent.wal",
);

options.autoFlush = true;
options.enableTransport = false;
options.enableDiscovery = false;

const client = new Client(options);

const opened = await client.open();

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

await client.put("settings/theme", "dark");

await client.close();

Sync flow

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

const state = await client.syncStateInfo();

const tick = await client.tick();

Transport flow

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

const client = new Client(options);

await client.open();
await client.startTransport();

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

await client.connect(peer);
await client.tick();

await client.close();

Metadata flow

js
const node = await client.refreshNodeInfo();

if (node.isOk()) {
  console.log(node.value().nodeId);
}

Naming style

The JavaScript SDK uses camelCase for public fields and methods.

Examples:

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

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

Snake_case aliases can exist for compatibility, but camelCase is the preferred JavaScript style.

This differs from the C++ SDK, which uses snake_case.

Async behavior

Most runtime operations are async.

Use await with:

  • open
  • close
  • put
  • get
  • remove
  • syncStateInfo
  • tick
  • startTransport
  • connect
  • startDiscovery
  • peers
  • refreshNodeInfo

Wrong:

js
const result = client.put("app/name", "Softadastra");

Correct:

js
const result = await client.put("app/name", "Softadastra");

Methods like contains, size, empty, transportRunning, and discoveryRunning can be synchronous when they inspect already available local state.

Local-first behavior

The JavaScript API should preserve Softadastra's local-first model.

A local store operation should not require:

  • remote server
  • connected peer
  • transport
  • discovery
  • cloud access

This should work locally:

js
options.enableTransport = false;
options.enableDiscovery = false;

await client.put("draft/1", "hello");
await client.get("draft/1");

Transport failure should not delete local data.

Discovery failure should not block local store access.

Sync failure should be visible, but local values should remain readable.

Stability rule

This reference should only document public SDK behavior that is implemented and intended to remain stable.

Recommended rule:

txt
public SDK type          -> include here
public SDK method        -> include here
experimental method      -> mention carefully or keep out
internal engine class    -> document in engine section, not here
implementation detail    -> keep out of public API reference

Released under the Apache License 2.0.