npm package: @open-ot/client
Description: The client-side state machine for OpenOT, handling optimistic updates, operation buffering, and server synchronization.
Latest version: 0.3.0

# @open-ot/client

The client-side state machine for OpenOT, handling optimistic updates, operation buffering, and server synchronization.

## Overview

`@open-ot/client` implements the standard OT client state machine, ensuring your UI remains responsive even on slow or unreliable networks. It manages three distinct states to handle the complexity of real-time collaboration:

- **Synchronized**: Client is up-to-date with the server.
- **AwaitingConfirm**: Waiting for the server to acknowledge a sent operation.
- **AwaitingWithBuffer**: User continues editing while waiting for acknowledgment. New edits are buffered and composed.

## Installation

```bash
npm install @open-ot/client @open-ot/core
```

## Quick Start

```typescript
import { OTClient } from '@open-ot/client';
import { TextType } from '@open-ot/core';

const client = new OTClient({
  type: TextType,
  initialSnapshot: "Hello World",
  initialRevision: 0,
});

// User types " Alice"
client.applyLocal([
  { r: 5 },           // Retain "Hello"
  { i: " Alice" },    // Insert " Alice"
  { r: 6 }            // Retain " World"
]);

console.log(client.getSnapshot());
// => "Hello Alice World"
```

## How It Works

### The State Machine

The client operates as a finite state machine to handle the asynchronous nature of network communication:

```
┌─────────────┐
│ Synchronized │ ◄──── Initial state
└──────┬──────┘
       │ User edits
       │ (send to server)
       ▼
┌────────────────┐
│ AwaitingConfirm │ ◄──── Waiting for server ACK
└────────┬───────┘
         │ User continues editing
         │ (buffer locally)
         ▼
┌──────────────────────┐
│ AwaitingWithBuffer   │ ◄──── Composing buffered ops
└──────────────────────┘
```

### Optimistic UI Updates

When a user makes a change, the client:
1. **Immediately applies** the operation to the local snapshot (optimistic update).
2. **Sends** the operation to the server.
3. **Transitions** to `AwaitingConfirm` state.

This ensures the UI never locks up waiting for the server.

### Operation Buffering

If the user continues typing while waiting for the server:
1. New operations are **buffered** locally.
2. The client **composes** consecutive operations into a single efficient operation.
3. Once the server acknowledges the pending operation, the buffered operation is sent.

This minimizes network traffic and reduces serverless invocation costs.

### Handling Remote Operations

When a remote operation arrives from the server:
1. The client **transforms** its pending/buffered operations against the remote operation.
2. The remote operation is **applied** to the local snapshot.
3. The client remains in sync with the server.

## API Reference

### `OTClient<Snapshot, Op>`

#### Constructor

```typescript
new OTClient(options: OTClientOptions<Snapshot, Op>)
```

**Options:**

```typescript
interface OTClientOptions<Snapshot, Op> {
  type: OTType<Snapshot, Op>;
  initialRevision: number;
  initialSnapshot: Snapshot;
  transport?: TransportAdapter;
}
```

- **`type`**: The OT type (e.g., `TextType`, `JsonType`).
- **`initialRevision`**: The starting revision number (usually `0`).
- **`initialSnapshot`**: The initial document state.
- **`transport`** (optional): A transport adapter for automatic server communication.

#### Methods

##### `applyLocal(op: Op): Op | null`

Apply a local operation generated by the user.

**Returns:**
- The operation to send to the server, or `null` if buffering.

**Example:**

```typescript
const opToSend = client.applyLocal([{ i: "Hello" }]);
if (opToSend) {
  // Send to server manually if not using a transport
}
```

##### `serverAck(): Op | null`

Handle a server acknowledgment.

**Returns:**
- The next operation to send (if buffered), or `null`.

**Example:**

```typescript
// When server confirms receipt
const nextOp = client.serverAck();
if (nextOp) {
  // Send the buffered operation
}
```

##### `applyRemote(op: Op): Op`

Apply a remote operation from the server.

**Returns:**
- The transformed operation that was applied to the local snapshot.

**Example:**

```typescript
// When receiving an operation from another user
const transformedOp = client.applyRemote(remoteOp);
// Update UI with transformedOp if needed
```

##### `getSnapshot(): Snapshot`

Get the current document state.

```typescript
const currentState = client.getSnapshot();
```

##### `getRevision(): number`

Get the current revision number.

```typescript
const rev = client.getRevision();
```

## Using with a Transport

The client can automatically handle server communication when provided with a `TransportAdapter`:

```typescript
import { OTClient } from '@open-ot/client';
import { WebSocketTransport } from '@open-ot/transport-websocket';
import { TextType } from '@open-ot/core';

const transport = new WebSocketTransport('ws://localhost:3000');

const client = new OTClient({
  type: TextType,
  initialSnapshot: "",
  initialRevision: 0,
  transport: transport, // Automatic sync!
});

// Just apply local changes, transport handles the rest
client.applyLocal([{ i: "Hello" }]);
```

## Manual Transport Integration

If you're building a custom transport, implement the message protocol:

```typescript
interface MessageProtocol<Op> {
  type: 'op' | 'ack';
  op?: Op;
  revision?: number;
}
```

**Client → Server (operation):**

```json
{
  "type": "op",
  "op": [{ "i": "Hello" }],
  "revision": 5
}
```

**Server → Client (acknowledgment):**

```json
{
  "type": "ack"
}
```

**Server → Client (remote operation):**

```json
{
  "type": "op",
  "op": [{ "i": "World" }]
}
```

## Advanced: Offline-First Applications

The client works seamlessly offline. Operations are queued locally and automatically synced when the connection returns:

```typescript
// User edits while offline
client.applyLocal(op1);
client.applyLocal(op2);
client.applyLocal(op3);

// Operations are composed: op1 ∘ op2 ∘ op3

// When connection returns, transport sends the composed operation
```

## State Transitions

| Current State        | Event              | Next State           | Action                          |
|----------------------|--------------------|----------------------|---------------------------------|
| Synchronized         | User edits         | AwaitingConfirm      | Send operation to server        |
| AwaitingConfirm      | User edits         | AwaitingWithBuffer   | Buffer operation locally        |
| AwaitingWithBuffer   | User edits         | AwaitingWithBuffer   | Compose with buffered operation |
| AwaitingConfirm      | Server ACK         | Synchronized         | Clear pending operation         |
| AwaitingWithBuffer   | Server ACK         | AwaitingConfirm      | Send buffered operation         |
| Any state            | Remote operation   | Same state           | Transform and apply             |

## License

MIT