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:
- SDK JS
- Installation
- First App
- Client
- Client Options
- Local Store
- Persistent Store
- Sync
- Transport
- Discovery
- Metadata
- Errors
- Examples
The core rule is:
The JavaScript SDK exposes a stable async API over the Softadastra runtime model.Package
The main package is:
@softadastra/sdkUse the main import:
import { Client, ClientOptions } from "@softadastra/sdk";Other public types can be imported when needed:
import { Client, ClientOptions, Value, Peer } from "@softadastra/sdk";ESM requirement
The JavaScript SDK uses modern JavaScript modules.
Your package.json should include:
{
"type": "module"
}Then you can run files with:
node main.jsMain public types
The JavaScript SDK exposes these main public types:
| Type | Purpose |
|---|---|
Client | Main SDK object |
ClientOptions | Runtime configuration |
Value | Local store value |
Peer | Remote peer description |
NodeInfo | Local node metadata |
Result | Success or failure result |
SoftadastraError | Structured error |
SyncResult | Sync state result value |
TickResult | Sync tick result value |
Minimal client
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
| Method | Purpose |
|---|---|
open() | Open and initialize the local runtime |
close() | Close the runtime and release resources |
Example:
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
| Method | Purpose |
|---|---|
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:
await client.put("settings/theme", "dark");
const value = await client.get("settings/theme");
if (value.isOk()) {
console.log(value.value().toString());
}Sync methods
| Method | Purpose |
|---|---|
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:
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
| Method | Purpose |
|---|---|
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:
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
| Method | Purpose |
|---|---|
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:
const peers = await client.peers();
if (peers.isOk()) {
for (const peer of peers.value()) {
console.log(`${peer.nodeId} ${peer.host}:${peer.port}`);
}
}Metadata methods
| Method | Purpose |
|---|---|
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:
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:
const options = ClientOptions.local("node-a");
const client = new Client(options);Factory helpers
| Helper | Purpose |
|---|---|
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:
const options = ClientOptions.persistent("node-a", "data/node-a.wal");Identity fields
| Field | Purpose |
|---|---|
nodeId | Local node identifier |
displayName | Human-friendly node label |
version | Runtime or application version |
Example:
options.displayName = "Node A";
options.version = "0.1.0";Persistence fields
| Field | Purpose |
|---|---|
enableWal | Enable WAL-backed persistence |
walPath | WAL file path |
autoFlush | Flush WAL writes automatically when configured |
Example:
options.enableWal = true;
options.walPath = "data/node-a.wal";
options.autoFlush = true;Persistent helper:
const options = ClientOptions.persistent("node-a", "data/node-a.wal");
options.autoFlush = true;Transport fields
| Field | Purpose |
|---|---|
enableTransport | Enable transport configuration |
transportHost | Local transport bind host |
transportPort | Local transport bind port |
Example:
options.enableTransport = true;
options.transportHost = "127.0.0.1";
options.transportPort = 4041;Discovery fields
| Field | Purpose |
|---|---|
enableDiscovery | Enable discovery configuration |
discoveryHost | Local discovery bind host |
discoveryPort | Local discovery bind port |
discoveryBroadcastHost | Discovery target host |
discoveryBroadcastPort | Discovery target port |
Example:
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.
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.
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
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
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:
await client.put("message", "hello");You can also create a value explicitly:
const value = Value.fromString("hello");
await client.put("message", value);Read values can be converted back to strings:
const result = await client.get("message");
if (result.isOk()) {
console.log(result.value().toString());
}Peer
Peer describes another node.
const peer = new Peer("node-b", "127.0.0.1", 4042);Common fields:
| Field | Purpose |
|---|---|
nodeId | Remote node identifier |
host | Remote host |
port | Remote transport port |
Use with:
await client.connect(peer);A failed peer connection should not invalidate local state.
NodeInfo
NodeInfo describes the local node.
Common fields:
| Field | Purpose |
|---|---|
nodeId | Local node identifier |
displayName | Human-friendly label |
hostname | Machine hostname |
osName | Operating system |
version | Runtime or app version |
capabilities | Supported runtime capabilities |
uptime_ms() | Runtime uptime in milliseconds |
Example:
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:
Result
-> success: value
-> failure: SoftadastraErrorCommon methods:
| Method | Purpose |
|---|---|
isOk() | Check whether the result is successful |
isErr() | Check whether the result failed |
value() | Access success value |
error() | Access error |
Correct pattern:
const result = await client.get("app/name");
if (result.isErr()) {
console.error(result.error().message);
process.exit(1);
}
console.log(result.value().toString());Avoid:
const value = (await client.get("app/name")).value();That assumes the operation succeeded.
SoftadastraError
SoftadastraError describes what failed.
Common fields and methods:
| Field or method | Purpose |
|---|---|
message | Human-readable error message |
codeString() | Stable error code string, when exposed |
Example:
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:
code : not_found
message : key not foundSyncResult
SyncResult describes current sync state.
Common fields:
| Field | Meaning |
|---|---|
outboxSize | Local operations waiting for synchronization |
queuedCount | Operations ready to be selected for sending |
inFlightCount | Operations prepared or sent, possibly waiting for ACK |
acknowledgedCount | Operations confirmed by the remote side |
failedCount | Operations that exceeded retry policy or hit a sync error |
totalRetries | Total retry attempts |
lastSubmittedVersion | Last local submitted version, if exposed |
lastAppliedRemoteVersion | Last remote applied version, if exposed |
Example:
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:
if (state.isOk() && state.value().hasFailed()) {
console.error("sync has failed work");
}TickResult
TickResult describes the result of one sync tick.
Common fields:
| Field | Meaning |
|---|---|
retriedCount | Operations retried during the tick |
prunedCount | Completed entries removed during the tick |
batchSize | Operations produced in the current batch |
Example:
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
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
const result = await client.get("profile/name");Success returns a Value.
Missing key returns an explicit error.
remove
const result = await client.remove("profile/name");Removes the local value.
If sync is enabled, a delete operation can be tracked for propagation.
contains
const exists = client.contains("profile/name");Returns whether the key exists locally.
size
const count = client.size();Returns the number of local entries.
empty
const isEmpty = client.empty();Returns whether the local store is empty.
Sync reference
syncStateInfo
const state = await client.syncStateInfo();Use it to inspect pending sync work.
sync_state_info
If exposed:
const state = await client.sync_state_info();Snake_case alias for syncStateInfo().
tick
const tick = await client.tick();Runs one sync tick.
tick with pruning
const tick = await client.tick({
prune: true,
});Runs one sync tick and asks the runtime to prune completed work, if supported.
Transport reference
startTransport
const result = await client.startTransport();Starts local transport.
Transport requires:
options.enableTransport = true;
options.transportHost = "127.0.0.1";
options.transportPort = 4041;start_transport
If exposed:
const result = await client.start_transport();Snake_case alias for startTransport().
transportRunning
if (client.transportRunning()) {
console.log("transport is running");
}connect
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
const result = await client.startDiscovery();Starts peer discovery.
Discovery requires:
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:
const result = await client.start_discovery();Snake_case alias for startDiscovery().
discoveryRunning
if (client.discoveryRunning()) {
console.log("discovery is running");
}peers
const result = await client.peers();Returns known peers.
No peers is a valid state.
Metadata reference
refreshNodeInfo
const result = await client.refreshNodeInfo();Refreshes and returns local node metadata.
refresh_node_info
If exposed:
const result = await client.refresh_node_info();Snake_case alias for refreshNodeInfo().
nodeInfo
If exposed:
const result = await client.nodeInfo();Returns cached node metadata without forcing a refresh.
Common flows
Local-only flow
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
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
await client.put("message/1", "hello");
const state = await client.syncStateInfo();
const tick = await client.tick();Transport flow
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
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:
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:
opencloseputgetremovesyncStateInfotickstartTransportconnectstartDiscoverypeersrefreshNodeInfo
Wrong:
const result = client.put("app/name", "Softadastra");Correct:
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:
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:
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