Client API

The TopGunClient is the main entry point for interacting with the TopGun cluster. It handles connection management, offline synchronization, and provides access to distributed data structures.

Constructor

new TopGunClient(config)

Parameters

  • serverUrl
    string. WebSocket URL of the TopGun server (e.g., ws://localhost:8765).
  • storage
    IStorageAdapter. Persistence adapter (e.g., IDBAdapter).
  • nodeId?
    string. Optional unique ID for this client. Auto-generated if omitted.

Core Methods

start()

Initializes the storage adapter and starts the synchronization engine.
await client.start();

Non-Blocking Initialization

The IDBAdapter initializes IndexedDB in the background. You can start using the client immediately—write operations are queued in memory and persisted once IndexedDB is ready. This provides true “memory-first” behavior with zero blocking time.

getMap<K, V>(name)

Returns an LWWMap (Last-Writer-Wins Map). Best for simple key-value data where the latest update wins.
const users = client.getMap<string, User>('users');

// Write data
users.set('u1', { name: 'Alice' });

// Read data
const user = users.get('u1');

// Listen for changes
const unsubscribe = users.onChange(() => {
  console.log('Users map changed');
});

getORMap<K, V>(name)

Returns an ORMap (Observed-Remove Map). Best for sets or lists where concurrent additions should be preserved.
const tags = client.getORMap<string, string>('tags');

// Add values (multiple values per key allowed)
tags.add('post:123', 'javascript');
tags.add('post:123', 'typescript');

// Read all values for a key
const postTags = tags.get('post:123');
// Returns: ['javascript', 'typescript']

// Listen for changes
const unsubscribe = tags.onChange(() => {
  console.log('Tags changed');
});

topic(name)

Returns a TopicHandle for real-time Pub/Sub messaging. Messages are ephemeral and not stored.
const chat = client.topic('chat-room');

// Publish a message
chat.publish({ text: 'Hello!' });

// Subscribe to messages (returns unsubscribe function)
const unsubscribe = chat.subscribe((msg) => {
  console.log(msg);
});

// Later: stop listening
unsubscribe();

getLock(name)

Returns a DistributedLock handle. Useful for coordinating tasks across multiple clients.
const lock = client.getLock('resource-A');
if (await lock.lock()) {
  // Critical section
  await lock.unlock();
}

query<T>(mapName, filter)

Creates a live query subscription for a map. Returns a QueryHandle that can be subscribed to for real-time updates.
const handle = client.query<Todo>('todos', {
  where: { completed: false },
  sort: { createdAt: 'desc' },
  limit: 10
});

const unsubscribe = handle.subscribe((results) => {
  console.log('Results:', results);
});

close()

Closes the client, disconnecting from the server and cleaning up resources.
client.close();

Authentication

Use setAuthToken to authenticate the client. The token is sent to the server on connection and reconnection.

client.setAuthToken('jwt-token-here');

For dynamic token refresh (e.g., when tokens expire), use setAuthTokenProvider:

// Provider is called on each reconnection
client.setAuthTokenProvider(async () => {
  const token = await refreshToken();
  return token;
});

Unsubscribing from Changes

All subscription methods return an unsubscribe function. Call it to stop receiving updates:

// Data structure changes
const users = client.getMap<string, User>('users');
const unsubscribeMap = users.onChange(() => {
  console.log('Users changed');
});

// Live query results
const query = client.query<Todo>('todos', { where: { done: false } });
const unsubscribeQuery = query.subscribe((results) => {
  console.log('Query results:', results);
});

// Topic messages
const chat = client.topic('chat-room');
const unsubscribeTopic = chat.subscribe((msg) => {
  console.log('New message:', msg);
});

// Later: clean up all subscriptions
unsubscribeMap();
unsubscribeQuery();
unsubscribeTopic();

Important: The onChange() callback receives no arguments—it only signals that something changed. To see what changed, re-read the data inside the callback. For filtered results with automatic updates, use client.query() instead.