e30e75b5d4
Changesets / Create Version PR (push) Has been cancelled
Deploy Shadcn Registry / Deploy Production (push) Has been cancelled
Template Metrics / LOC + Bundle Size (push) Has been cancelled
Code Quality / Oxlint + Oxfmt (push) Has been cancelled
Code Quality / Template Sync (push) Has been cancelled
Code Quality / Build Changed Packages (push) Has been cancelled
Code Quality / Test Changed Packages (push) Has been cancelled
Deploy Expo Example / Deploy Production (push) Has been cancelled
Deploy Ink Example / Deploy Production (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-stream, 3.12) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.10) (push) Has been cancelled
Python Tests / pytest (assistant-ui-sync-server-api, 3.12) (push) Has been cancelled
98 lines
2.2 KiB
Plaintext
98 lines
2.2 KiB
Plaintext
---
|
|
title: Quickstart
|
|
description: Install gorp and wire up your first client/server replica.
|
|
---
|
|
|
|
## Installation
|
|
|
|
```sh
|
|
npm install @assistant-ui/gorp
|
|
```
|
|
|
|
## Define your state and commands
|
|
|
|
A gorp app has two types: the shape of the synced **state**, and the
|
|
**commands** the client can send. Both live in shared code so client and
|
|
server stay in lockstep.
|
|
|
|
```ts
|
|
type State = {
|
|
count: number;
|
|
};
|
|
|
|
type Command =
|
|
| { type: "inc" }
|
|
| { type: "set"; value: number };
|
|
```
|
|
|
|
## Define a mutator
|
|
|
|
The mutator responds to commands by writing to state. Its signature is
|
|
the same for `GorpClient` and `GorpServer`, so a single function can run
|
|
on both sides.
|
|
|
|
```ts
|
|
const initialState: State = { count: 0 };
|
|
|
|
const mutator = (state: State, cmd: Command) => {
|
|
switch (cmd.type) {
|
|
case "inc":
|
|
state.count += 1;
|
|
break;
|
|
case "set":
|
|
state.count = cmd.value;
|
|
break;
|
|
}
|
|
};
|
|
```
|
|
|
|
It must be deterministic in `(state, command, seq)` — `GorpClient` re-runs
|
|
it on every replay to rebuild the optimistic view.
|
|
|
|
## Set up the server
|
|
|
|
`GorpServer` owns the authoritative state. Writes inside the mutator are
|
|
batched per microtask and emitted as deep-patch ops to every subscriber.
|
|
|
|
```ts
|
|
import { GorpServer, GorpSessions } from "@assistant-ui/gorp";
|
|
|
|
const server = new GorpServer<State, Command>({
|
|
initialState,
|
|
mutator,
|
|
});
|
|
const sessions = new GorpSessions(server);
|
|
```
|
|
|
|
## Set up the client
|
|
|
|
`GorpClient` is a replica plus an optimistic view layered on top of the
|
|
last server-confirmed state. The config also takes a `send` callback —
|
|
gorp invokes it whenever a command needs to go over the wire.
|
|
|
|
```ts
|
|
import { GorpClient } from "@assistant-ui/gorp";
|
|
|
|
const transport = createTransport();
|
|
|
|
const client = new GorpClient<State, Command>({
|
|
initialState,
|
|
mutator,
|
|
send: (cmd) => transport.send(cmd),
|
|
});
|
|
|
|
transport.onMessage((msg) => client.apply(msg));
|
|
```
|
|
|
|
## Use it
|
|
|
|
```ts
|
|
client.send({ type: "inc" });
|
|
client.state.count; // 1, immediately (optimistic)
|
|
```
|
|
|
|
The server's first envelope is always a full snapshot, so a fresh client
|
|
catches up without an explicit handshake. After that, only patches flow.
|
|
On reconnect, call `client.resync()` to re-emit pending commands via the
|
|
new connection. `GorpSessions` dedups them against the session high-water.
|