@arealtimetech/adk-js
Version:
JavaScript ADK to interact with the A Realtime Tech Platform.
392 lines (263 loc) • 10.2 kB
Markdown
# JavaScript ADK
*The official JavaScript SDK for the [A Realtime Tech (ART)](https://www.arealtimetech.com) platform.*
[Website](https://www.arealtimetech.com) · [Documentation](https://docs.arealtimetech.com) · [About ART](https://www.arealtimetech.com/about)
## About ART
**A Realtime Tech (ART)** is a realtime application platform that powers low-latency messaging, presence, and collaborative state for modern apps. Build chat, live dashboards, multiplayer experiences, IoT control planes, and CRDT-backed shared documents on a single managed backbone — without standing up your own websocket, presence, or sync infrastructure. Learn more at [arealtimetech.com](https://www.arealtimetech.com).
## About the JavaScript ADK
The **JavaScript ADK (ART Development Kit)** gives your application the tools it needs to talk to ART services. It ships only the runtime needed to connect, subscribe, send, receive, and synchronise state — no separate dev/prod build is required, so you can drop the same package directly into production.
> **Note** — installing the package is not enough on its own. The ADK must also be authenticated using credentials issued by ART (see [Configuration](#configuration) below).
## Installation
```bash
npm install @arealtimetech/adk-js
```
## Configuration
The ADK supports **two ways** to provide credentials. Pick whichever fits your app.
### Option 1 — Load credentials from a JSON file (`AutoLoadCredsFromJSON`)
Set `AutoLoadCredsFromJSON: true` and the ADK will automatically fetch credentials from a JSON file at the configured path. This is the simplest setup and where you can place adk-services.json in the root of the project.
```js
import Adk from '@arealtimetech/adk-js';
const adk = new Adk({
Uri: "", // ART service URL
AutoLoadCredsFromJSON: true, // load credentials from JSON
});
adk.connect();
```
**JSON file shape** (served at the configured config path):
```json
{
"Client-ID": "your-client-id",
"Client-Secret": "your-client-secret",
"Org-Title": "your-org",
"Environment": "your-environment",
"ProjectKey": "your-project-key"
}
```
### Option 2 — Set credentials programmatically (`setCredentials`)
When `AutoLoadCredsFromJSON` is **off** (or omitted), you supply credentials in code via `setCredentials()`. Use this when credentials come from your own auth flow, an active user session, or any other runtime source.
```js
import Adk from '@arealtimetech/adk-js';
const adk = new Adk({
Uri: "", // ART service URL
});
adk.setCredentials({
ClientID: "your-client-id",
ClientSecret: "your-client-secret",
OrgTitle: "your-org",
Environment: "your-environment",
ProjectKey: "your-project-key"
});
adk.connect();
```
| Field | Type | Required | Description |
| -------------- | ------ | -------- | ------------------------------------ |
| `ClientID` | string | yes | Client identifier issued by ART |
| `ClientSecret` | string | yes | Client secret paired with `ClientID` |
| `OrgTitle` | string | yes | Your organisation / tenant title |
| `Environment` | string | yes | Target environment name |
| `ProjectKey` | string | yes | Project key inside the environment |
## Usage
### Establish and manage the connection
```js
import Adk from '@arealtimetech/adk-js';
const adk = new Adk({
Uri: "", // service URL
AuthToken: "", // passcode generated by your server for the user
});
// Open the connection
adk.connect();
// Connection lifecycle
adk.on("open", async (event) => {
console.log("ADK connection opened", event);
});
adk.on("close", () => {
console.log("ADK connection closed");
});
// Tear down
adk.disconnect();
```
### Subscribe to a channel
`subscribe()` joins a specific channel. Once subscribed, the channel is a real-time stream where you can **send**, **receive**, listen to events, and observe **user presence**.
```js
const sub = await adk.subscribe("YOUR_CHANNEL_NAME");
```
### Track presence
`fetchPresence()` returns the list of users currently active in the channel.
```js
sub.fetchPresence((users) => {
console.log("Active users:", users);
});
```
### Listen to all events
`listen()` is a catch-all — every payload published to the channel is delivered to your callback.
```js
sub.listen((data) => {
console.log("Received:", data);
});
```
### Listen to a specific event
`bind()` filters by event name; the callback only fires for matching events.
```js
sub.bind("EVENT", (data) => {
console.log("Event received:", data);
});
```
### Send messages
`push()` publishes an event to the channel. Pass `to` to deliver only to specific users.
```js
const payload = { message: "Hello!" };
sub.push("EVENT", payload, {
to: ["username1", "username2"], // optional — target specific users
});
```
## Threads on an orchestrator-enabled channel
On a channel that has orchestrator enabled, you can spin up isolated **threads** from a subscription. Each thread has its own id and only receives messages tagged with that id.
```js
const sub = await adk.subscribe("YOUR_CHANNEL");
const thread = sub.thread();
console.log(thread.threadId);
```
Pass an existing id to attach to a known thread (for example, one created by the server):
```js
const thread = sub.thread("thread_xxxxx");
```
### Listen to all events on the thread
```js
thread.listen((msg) => {
console.log(msg.event, msg.content);
});
```
### Listen to a specific event
```js
thread.bind("status_update", (content) => {
console.log(content);
});
```
### Send a message
The `thread_id` is attached automatically.
```js
await thread.push("user_input", { message: "hello" }, {
to: ["username"],
});
```
### Stop listening
```js
thread.remove("status_update"); // remove all callbacks for the event
thread.remove("status_update", myFn); // remove a single callback
```
### Close the thread
```js
thread.dispose();
```
## Orchestrator
`adk.orchestrator(id)` connects directly to an orchestrator. The SDK subscribes to the orchestrator, so you can go straight to threads.
```js
const orch = adk.orchestrator("your-orchestrator-id").connect();
const thread = await orch.thread();
thread.listen((msg) => {
console.log(msg.event, msg.content);
});
await thread.push("user_input", { message: "hi" }, {
to: ["username"],
});
```
Reuse an existing thread by id:
```js
const thread = await orch.thread("thread_xxxxx");
```
## Agent
`adk.agent(id)` connects directly to an agent. The SDK subscribes to the agent's.
```js
const agent = adk.agent("your-agent-id").connect();
const thread = agent.thread();
thread.listen((evt) => {
console.log(evt.event, evt.content);
});
// Start a run with user input
const run = await thread.run("Hello agent");
// Await the final output
const output = await run.done();
console.log(output);
```
Reply to a follow-up question from the agent:
```js
await run.sendFeedback("yes, proceed");
```
## Shared Object Channel
A **Shared Object Channel** is a real-time, collaborative data structure backed by a **CRDT**. Multiple clients can update the same JSON tree concurrently and converge to a consistent state.
```js
// Subscribe like any other channel
const sub = await adk.subscribe("YOUR_SO_CHANNEL_NAME");
```
### Reading
#### Listen for live updates
Listen at a **path** inside the shared object — your callback receives plain JSON whenever that subtree changes.
```js
// path examples: "", "user", "user.profile", "todos"
const unsubscribe = await sub.query("user.profile").listen((data) => {
console.log("profile updated:", data);
});
// later
unsubscribe();
```
**Path rules**
- `""` (empty) or `"index"` → whole document
- Use dot paths for objects (e.g. `user.profile.name`)
- For arrays, listen at the array path (e.g. `"todos"`). Item keys are internal; per-item paths are not stable.
#### Fetch once (no subscription)
Retrieve the current value at a path without subscribing for ongoing updates.
```js
const profile = await sub.query("user.profile").execute();
```
### Writing
`sub.state()` returns a live proxy. Mutate it like normal JavaScript — changes are batched and merged using CRDT rules. The proxy:
- Auto-creates missing parent objects/arrays on write
- Deletes safely (deleting a missing key is a no-op)
- Emits ops optimistically (UI updates immediately) and ships a compacted batch to the server
```js
// Get the live state proxy once and reuse it
const state = sub.state();
/* ---------- Objects ---------- */
state.user.profile.name = "Jane Doe";
// Safe delete (no error if the key doesn't exist)
delete state.settings.theme;
/* ---------- Arrays ---------- */
state.todos.push({ text: "one" }, { text: "two" });
// Mutate newly added items immediately
state.todos[0].text = "ONE";
// Replace item at index 1
state.todos.splice(1, 1, { text: "two-ish" });
// Pop last item (returns the removed value)
const last = state.todos.pop();
/* ---------- Flushing ---------- */
// The client batches & compacts ops automatically.
// Call flush() to force-send the current batch now.
await sub.flush();
```
**Array API (on any array path)**
- `push(...items)` / `pop()` / `unshift(...items)`
- `splice(start, deleteCount?, ...insert)`
- `insertAt(index, item)`
- `move(fromIndex, toIndex)`
- Numeric index get/set (`state.todos[0] = {...}`)
- `delete state.todos[i]` (remove at index)
> **Notes**
>
> - You cannot create sparse indices by assignment (e.g. `todos[5] = ...` when length is 1). Use `insertAt` / `splice`.
> - For newly pushed items you can mutate them immediately (optimistic local state).
## Documentation
Full API reference and guides: **[docs.arealtimetech.com](https://docs.arealtimetech.com)**
## Learn more
- **Website:** [arealtimetech.com](https://www.arealtimetech.com)
- **About ART:** [arealtimetech.com/about](https://www.arealtimetech.com/about)
- **Documentation:** [docs.arealtimetech.com](https://docs.arealtimetech.com)
Made with care by [A Realtime Tech](https://www.arealtimetech.com).