npm package: @rcap/realtime
Description: Real-time multiplayer rooms powered by CRDTs. Connect users, sync state, track presence.
Latest version: 0.1.0

# @rcap/realtime

Real-time multiplayer rooms powered by CRDTs. Add collaborative features to any application with minimal code.

- **Shared State** - Conflict-free data structures that sync automatically across all clients
- **Presence** - Track who's connected and their live state (cursors, selections, etc.)
- **Ephemeral Events** - Fire-and-forget broadcasts for transient actions
- **Yjs Compatible** - Works with the entire Yjs ecosystem (y-codemirror, y-prosemirror, y-quill, y-monaco)
- **Auto Reconnect** - Exponential backoff, pending queue, seamless recovery
- **Persistence** - Optional IndexedDB for offline support

## Quick Start

```bash
npm install @rcap/realtime yjs
```

```typescript
import { createRoom } from '@rcap/realtime';

const room = createRoom({
  apiKey: 'your-api-key',
  roomId: 'my-room',
  user: { id: 'user-1', name: 'Alice' },
});

await room.connect();
```

## Shared State

State is stored in CRDT data structures that automatically merge across clients - no conflict resolution needed.

```typescript
// Key-value map
const settings = room.map('settings');
settings.set('theme', 'dark');
settings.get('theme'); // 'dark'
settings.observe((changes) => {
  console.log('changed keys:', Object.keys(changes.changed));
});

// Ordered list
const items = room.array('items');
items.push({ id: 1, text: 'First item' });
items.observe((changes) => {
  console.log('array updated:', items.toArray());
});

// Text (for editor bindings)
const text = room.doc.getText('content');
```

## Presence

Track connected users and their live state.

```typescript
// Set your state (visible to all other users)
room.presence.set({ cursor: { x: 100, y: 200 }, status: 'editing' });

// Update specific fields
room.presence.update({ cursor: { x: 150, y: 250 } });

// Watch others
room.presence.subscribe((others) => {
  for (const user of others) {
    console.log(user.user.name, user.state.cursor);
  }
});
```

## Ephemeral Events

Broadcast events that are delivered to all connected clients but not persisted.

```typescript
// Send
room.broadcast('reaction', { emoji: 'thumbsup', from: 'Alice' });

// Receive
room.on('reaction', (data, sender) => {
  console.log(`${sender.name} reacted with ${data.emoji}`);
});
```

## Editor Integrations

The underlying Y.Doc is exposed directly, so any Yjs editor binding works out of the box.

```typescript
// CodeMirror 6
import { yCollab } from 'y-codemirror.next';
const ytext = room.doc.getText('editor');
// Use yCollab(ytext, awareness) in your extensions

// ProseMirror
import { ySyncPlugin } from 'y-prosemirror';
const ydoc = room.doc;
// Use ySyncPlugin(ydoc.getXmlFragment('prosemirror'))
```

## API

### `createRoom(options)`

| Option | Type | Description |
|--------|------|-------------|
| `apiKey` | `string` | API key for authentication |
| `roomId` | `string` | Unique room identifier |
| `user` | `{ id, name, color?, avatar? }` | Your user info |
| `baseUrl` | `string` | Server URL (default: `wss://sync.elvenvtt.com`) |
| `persistence` | `boolean` | Enable IndexedDB persistence (default: `true` in browser) |

### Room

| Method | Description |
|--------|-------------|
| `room.connect()` | Connect to the room (returns promise) |
| `room.disconnect()` | Disconnect from the room |
| `room.map(name)` | Get a SharedMap |
| `room.array(name)` | Get a SharedArray |
| `room.doc` | Raw Y.Doc for editor bindings |
| `room.transact(fn)` | Batch mutations in a single update |
| `room.broadcast(event, data)` | Send ephemeral event |
| `room.on(event, callback)` | Listen for ephemeral events |
| `room.presence` | Presence instance |
| `room.onReconnect(fn)` | Reconnection callback |
| `room.onDisconnect(fn)` | Disconnection callback |
| `room.getHealth()` | Connection health info |

### SharedMap

| Method | Description |
|--------|-------------|
| `map.get(key)` | Get value |
| `map.set(key, value)` | Set value |
| `map.delete(key)` | Delete key |
| `map.has(key)` | Check if key exists |
| `map.toJSON()` | Get all entries as object |
| `map.observe(callback)` | Watch for changes |

### SharedArray

| Method | Description |
|--------|-------------|
| `array.push(...items)` | Append items |
| `array.insert(index, ...items)` | Insert at index |
| `array.delete(index, count?)` | Remove items |
| `array.get(index)` | Get item at index |
| `array.toArray()` | Get all items |
| `array.observe(callback)` | Watch for changes |

## License

MIT