# API Reference

> *Last updated: 2025-08-09 loro-crdt@1.5.10*

<CopyRawMdxButton />

## Overview

Loro is a powerful Conflict-free Replicated Data Type (CRDT) library that enables real-time collaboration. If CRDTs are new to you, start with [What are CRDTs](/docs/concepts/crdt) for a gentle intro. This API reference provides comprehensive documentation for all classes, methods, and types available in the JavaScript/TypeScript binding.

Note: Under the hood, Loro combines a Fugue-based CRDT core with Eg-walker-inspired techniques that use simple index operations and replay only the divergent history when merging. This yields fast local edits, efficient merges, and low overhead without permanent tombstones. See the primer [Event Graph Walker (Eg-walker)](/docs/advanced/event_graph_walker) and performance notes in the v1.0 blog (import/export speedups, shallow snapshots): https://loro.dev/blog/v1.0

## Pitfalls & Best Practices

**Peer ID Management**

- **Never share PeerIDs** between concurrent sessions (tabs/devices) - causes document divergence
- Use random PeerIDs (default) unless you have strict single-ownership locking
- Don't assign fixed PeerIDs to users or devices

**UTF-16 Text Encoding**

- All text operations use UTF-16 indices by default in JS API
- Slicing in the middle of multi-unit codepoints corrupts them
- Use `insertUtf8()`/`deleteUtf8()` for UTF-8 systems

**Container Creation**

- Concurrent child container creation inside the same LoroMap at same key causes overwrites
- Initialize all child containers for a LoroMap upfront when possible
- Operations on the root containers will not override each other

**Events & Transactions**

- Events emit asynchronously after a microtask in JS API
- Import/export/checkout trigger automatic commits
- Loro transactions are NOT ACID - no rollback/isolation

**Version Control**

- After `checkout()`, document enters read-only "detached" mode, unless `setDetachedEditing(true)` is called
- [Frontiers](/docs/concepts/frontiers) can't determine complete operation sets without history

**Data Structure Choice**

- Use strings in Map for URLs/IDs (LWW), LoroText for collaborative editing

## Common Tasks & Examples

**Getting Started**

- **Create a document**: [`new LoroDoc()`](#LoroDoc.constructor) - Initialize a new collaborative document
- **Add containers**: [`getText`](#LoroDoc.getText), [`getList`](#LoroDoc.getList), [`getMap`](#LoroDoc.getMap), [`getTree`](#LoroDoc.getTree)
- **Listen to changes**: [`subscribe`](#LoroDoc.subscribe) - React to document modifications
- **Export/Import state**: [`export`](#LoroDoc.export) and [`import`](#LoroDoc.import) - Save and load documents

**Real-time Collaboration**

- **Sync between peers**: [`export`](#LoroDoc.export) with `mode: "update"` + [`import`](#LoroDoc.import)/[`importBatch`](#LoroDoc.importBatch) - Exchange incremental updates
- **Stream updates**: [`subscribeLocalUpdates`](#LoroDoc.subscribeLocalUpdates) - Send changes over WebSocket/WebRTC
- **Set unique peer ID**: [`setPeerId`](#LoroDoc.setPeerId) - Ensure each client has a unique identifier
- **Handle conflicts**: Automatic - All Loro data types are CRDTs that merge concurrent edits

**Rich Text Editing**

- **Create rich text**: [`getText`](#LoroDoc.getText) - Initialize a collaborative text container
- **Edit text**: [`insert`](#LoroText.insert), [`delete`](#LoroText.delete), [`applyDelta`](#LoroText.applyDelta)
- **Apply formatting**: [`mark`](#LoroText.mark) - Add bold, italic, links, custom styles
- **Track cursor positions**: [`getCursor`](#LoroText.getCursor) + [`getCursorPos`](#LoroDoc.getCursorPos) - Stable positions across edits
- **Configure styles**: [`configTextStyle`](#LoroDoc.configTextStyle) - Define expand behavior for marks

**Data Structures**

- **Ordered lists**: [`getList`](#LoroDoc.getList) - Arrays with [`push`](#LoroList.push), [`insert`](#LoroList.insert), [`delete`](#LoroList.delete)
- **Key-value maps**: [`getMap`](#LoroDoc.getMap) - Objects with [`set`](#LoroMap.set), [`get`](#LoroMap.get), [`delete`](#LoroMap.delete)
- **Hierarchical trees**: [`getTree`](#LoroDoc.getTree) - File systems, nested comments with [`createNode`](#LoroTree.createNode), [`move`](#LoroTree.move)
- **Reorderable lists**: [`getMovableList`](#LoroDoc.getMovableList) - Drag-and-drop with [`move`](#LoroMovableList.move), [`set`](#LoroMovableList.set)
- **Counters**: [`getCounter`](#LoroDoc.getCounter) - Distributed counters with [`increment`](#LoroCounter.increment)

**Ephemeral State & Presence**

- **User presence**: [`EphemeralStore`](#ephemeralstore) - Share cursor positions, selections, user status (not persisted)
- **Cursor syncing**: Use [`EphemeralStore.set`](#EphemeralStore.set) with cursor data from [`getCursor`](#LoroText.getCursor)
- **Live indicators**: Track who's online, typing indicators, mouse positions
- **Important**: EphemeralStore is a separate CRDT without history - perfect for temporary state that shouldn't persist

**Version Control & History**

- **Undo/redo**: [`UndoManager`](#undomanager) - Local undo of user's own edits
- **Time travel**: [`checkout`](#LoroDoc.checkout) to any [`frontiers`](#LoroDoc.frontiers) - Debug or review history
- **Version tracking**: [`version`](#LoroDoc.version), [`frontiers`](#LoroDoc.frontiers), [`versionVector`](#LoroDoc.versionVector)
- **Fork documents**: [`fork`](#LoroDoc.fork) or [`forkAt`](#LoroDoc.forkAt) - Create branches for experimentation
- **Merge branches**: [`import`](#LoroDoc.import) - Combine changes from forked documents

**Performance & Storage**

- **Incremental updates**: [`export`](#LoroDoc.export) from specific [`version`](#LoroDoc.version) - Send only changes
- **Compact history**: [`export`](#LoroDoc.export) with `mode: "snapshot"` - Full state with compressed history
- **Shallow snapshots**: [`export`](#LoroDoc.export) with `mode: "shallow-snapshot"` - State without partial history (see [Shallow Snapshots](/docs/concepts/shallow_snapshots))

## Basic Usage

```typescript twoslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const text = doc.getText("text");
text.insert(0, "Hello World");

// Subscribe to changes
const unsubscribe = doc.subscribe((event) => {
  console.log("Document changed:", event);
});

// Export updates for synchronization
const updates = doc.export({ mode: "update" });
```

## LoroDoc

The `LoroDoc` class manages containers, sync, versions, and events.

**Constructor**

```typescript no_run
new LoroDoc()
```

Creates a new Loro document with a randomly generated peer ID.

**Example:**
```ts twoslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
```

**Static Methods**

<Method id="LoroDoc.fromSnapshot">
```typescript no_run
static fromSnapshot(snapshot: Uint8Array): LoroDoc
```
</Method>
<Indent>
Creates a new LoroDoc from a snapshot. This is useful for loading a document from a previously exported snapshot.

**Parameters:**
- `snapshot` - Binary snapshot data

**Returns:** A new LoroDoc instance

**Example:**
```ts no_run twoslash
import { LoroDoc } from "loro-crdt";

// Assume we have a snapshot from a previous export
const prevDoc = new LoroDoc();
prevDoc.getText("text").insert(0, "Hello");
const snapshot: Uint8Array = prevDoc.export({ mode: "snapshot" });

const doc = LoroDoc.fromSnapshot(snapshot);
```
</Indent>

**Properties**

<Method id="LoroDoc.peerId">
```typescript no_run
readonly peerId: bigint
```
</Method>
<Indent>
Gets the peer ID of the current writer as a bigint.

**See Also:** [PeerID Management](/docs/concepts/peerid_management)

**Example:**
```ts twoslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const id = doc.peerId;
```
</Indent>

<Method id="LoroDoc.peerIdStr">
```typescript no_run
readonly peerIdStr: `${number}`
```
</Method>
<Indent>
Gets the peer ID as a decimal string.

**Example:**
```ts twoslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const idStr = doc.peerIdStr;
```
</Indent>

### Configuration Methods

<Method id="LoroDoc.setPeerId">
```typescript no_run
setPeerId(peer: number | bigint | `${number}`): void
```
</Method>
<Indent>
Sets the peer ID for this document. It must be a number, a BigInt, or a decimal string that fits into an unsigned 64-bit integer.
See [PeerID Management](/docs/concepts/peerid_management) for why uniqueness matters in distributed systems.

**Parameters:**
- `peer` - Peer ID as number, bigint, or decimal string

**Example:**
```ts twoslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.setPeerId("42");
```

**â ï¸ Critical Pitfall:** Never let two parallel peers (e.g., multiple tabs/devices) share the same PeerID â it creates duplicate op IDs and causes document divergence. Common mistakes:
- Don't assign a fixed PeerId to a user (users have multiple devices)
- Don't assign a fixed PeerId to a device (multiple tabs can open the same document)
- If you must reuse PeerIDs, enforce single ownership with strict locking mechanisms
- Best practice: Use random IDs (default behavior) unless you have a strong reason not to

See [PeerID reuse](/docs/tutorial/tips) for safe reuse patterns.
</Indent>

<Method id="LoroDoc.setRecordTimestamp">
```typescript no_run
setRecordTimestamp(auto_record: boolean): void
```
</Method>
<Indent>
Configures whether to automatically record timestamps for changes. Timestamps use Unix time (seconds since epoch). Learn more about storing timestamps and typical use cases in [Storing Timestamps](/docs/advanced/timestamp).

**Parameters:**
- `auto_record` - Whether to automatically record timestamps

**â ï¸ Important:** This setting doesn't persist in exported Updates or Snapshots. You must reapply this configuration each time you initialize a document.

**Example:**
```ts twoslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.setRecordTimestamp(true);
```
</Indent>

<Method id="LoroDoc.setChangeMergeInterval">
```typescript no_run
setChangeMergeInterval(interval: number): void
```
</Method>
<Indent>
Sets the interval in milliseconds for merging continuous local changes into a single change record. In Loro, multiple low-level operations are grouped into higher-level Changes for readability and syncing. See [Operations and Changes](/docs/concepts/operations_changes).

**Parameters:**
- `interval` - Merge interval in milliseconds

**Example:**
```ts twoslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.setChangeMergeInterval(1000); // Merge changes within 1 second
```
</Indent>

<Method id="LoroDoc.configTextStyle">
```typescript no_run
configTextStyle(styles: StyleConfig): void
```
</Method>
<Indent>
Configures the behavior of text styles (marks) in rich text containers. Marks can expand when edits happen at their edges (before/after/both/none). For a primer on rich text and marks in Loro, see [Text](/docs/tutorial/text).

**Parameters:**
- `styles` - Configuration object mapping style names to their config

**StyleConfig Type:**
```typescript no_run
type StyleConfig = Record<string, {
  expand?: "after" | "before" | "both" | "none"
}>
```

**Example:**
```ts twoslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.configTextStyle({
  bold: { expand: "after" },
  italic: { expand: "none" },
  link: { expand: "none" }
});
```
</Indent>

<Method id="LoroDoc.configDefaultTextStyle">
```typescript no_run
configDefaultTextStyle(style?: { expand: "after" | "before" | "both" | "none" }): void
```
</Method>
<Indent>
Configures the default text style for the document when using LoroText. If undefined is provided, the default style is reset.

**Parameters:**
- `style` - Default style configuration (optional)

**Example:**
```ts twoslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.configDefaultTextStyle({ expand: "after" });
```
</Indent>

### Container Access Methods

**ð Note:** Creating root containers (e.g., `doc.getText("...")`) does not record operations; nested container creation (e.g., `map.setContainer(...)`) does.

**â ï¸ Pitfall:** Avoid concurrent creation of child containers with the same key in LoroMaps. Instead of:
```ts no_run
// Dangerous - can cause overwrites
doc.getMap("user").getOrCreateContainer(userId, new LoroMap())
```
Use:
```ts no_run
// Safe - unique root container per user
doc.getMap("user." + userId)
```

<Method id="LoroDoc.getText">
```typescript no_run
getText(name: string): LoroText
```
</Method>
<Indent>

Gets or creates a text container with the given name. New to LoroText and marks? See [Text](/docs/tutorial/text).

**Parameters:**
- `name` - The container name

**Returns:** A `LoroText` instance

**Example:**
```ts twoslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const text = doc.getText("content");
text.insert(0, "Hello");
```
</Indent>

<Method id="LoroDoc.getList">
```typescript no_run
getList(name: string): LoroList
```
</Method>
<Indent>

Gets or creates a list container with the given name. Unsure whether to use List or MovableList? See [List and Movable List](/docs/tutorial/list) and the type selection guide [Choosing CRDT Types](/docs/concepts/choose_crdt_type).

**Parameters:**
- `name` - The container name

**Returns:** A `LoroList` instance

**Example:**
```ts twoslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const list = doc.getList("items");
list.push("Item 1");
```
</Indent>

<Method id="LoroDoc.getMap">
```typescript no_run
getMap(name: string): LoroMap
```
</Method>
<Indent>
Gets or creates a map container with the given name. See [Map](/docs/tutorial/map) for basics and patterns.

**Parameters:**
- `name` - The container name

**Returns:** A `LoroMap` instance

**Example:**
```ts twoslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const map = doc.getMap("settings");
map.set("theme", "dark");
```
</Indent>

<Method id="LoroDoc.getTree">
```typescript no_run
getTree(name: string): LoroTree
```
</Method>
<Indent>
Gets or creates a tree container with the given name. Learn about hierarchical editing and moves in [Tree](/docs/tutorial/tree).

**Parameters:**
- `name` - The container name

**Returns:** A `LoroTree` instance

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const tree = doc.getTree("fileSystem");
const root = tree.createNode();
```
</Indent>

<Method id="LoroDoc.getCounter">
```typescript no_run
getCounter(name: string): LoroCounter
```
</Method>
<Indent>
Gets or creates a counter container with the given name. Counters are special CRDTs that sum concurrent increments; see [Counter](/docs/tutorial/counter).

**Parameters:**
- `name` - The container name

**Returns:** A `LoroCounter` instance

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const counter = doc.getCounter("likes");
counter.increment(1);
```
</Indent>

<Method id="LoroDoc.getMovableList">
```typescript no_run
getMovableList(name: string): LoroMovableList
```
</Method>
<Indent>
Gets or creates a movable list container with the given name. MovableList is designed for reordering with concurrent moves. See [List and Movable List](/docs/tutorial/list) and [Choosing CRDT Types](/docs/concepts/choose_crdt_type).

**Parameters:**
- `name` - The container name

**Returns:** A `LoroMovableList` instance

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const movableList = doc.getMovableList("tasks");
movableList.push("Task 1");
movableList.push("Task 2");
movableList.push("Task 3");
movableList.move(0, 2); // Move first item to third position
```
</Indent>

<Method id="LoroDoc.getContainerById">
```typescript no_run
getContainerById(id: ContainerID): Container | undefined
```
</Method>
<Indent>
Gets a container by its unique ID. Container IDs (CID) uniquely reference containers across updates; see [Container ID](/docs/advanced/cid) and [Container](/docs/concepts/container).

**Parameters:**
- `id` - The container ID

**Returns:** The container instance or undefined if not found

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const text = doc.getText("text");
const textId = text.id;
const sameText = doc.getContainerById(textId);
```
</Indent>

### Import/Export Methods

<Method id="LoroDoc.export">
```typescript no_run
export(mode?: ExportMode): Uint8Array
```
</Method>
<Indent>
Exports the document in various formats for synchronization or persistence. For a walkthrough of export modesâsnapshot, update, shallow-snapshot, and updates-in-rangeâsee [Export Mode](/docs/tutorial/encoding). Shallow snapshots remove history while keeping current state; see [Shallow Snapshots](/docs/concepts/shallow_snapshots). VersionVector and Frontiers are two ways to represent versions; see [Version Vector](/docs/concepts/version_vector) and [Frontiers](/docs/concepts/frontiers).

**Parameters:**
- `mode` - Export configuration (optional)

**ExportMode Options:**
```typescript no_run
type ExportMode =
  | { mode: "snapshot" }
  | { mode: "update", from?: VersionVector }
  | { mode: "shallow-snapshot", frontiers: Frontiers }
  | { mode: "updates-in-range", spans: { id: OpId; len: number }[] }
```

**Returns:** Encoded binary data

**â ï¸ Important Notes:**
- **Shallow snapshots**: Cannot import updates from before the shallow start point. Peers can only sync if they have versions after this point.
- **Auto-commit**: The document automatically commits pending operations before export.
- **Performance**: Export new snapshots periodically to reduce import times for new peers.

**Examples:**
```typescript no_run
import { LoroDoc, VersionVector } from "loro-crdt";

const doc = new LoroDoc();
// ... make some changes to the document ...

// Export full snapshot
const snapshot = doc.export({ mode: "snapshot" });

// Export updates from a specific version
const lastSyncVersion = doc.version(); // Get current version
// ... make more changes ...
const updates = doc.export({
  mode: "update",
  from: lastSyncVersion
});

// Export shallow snapshot at current version
const shallowSnapshot = doc.export({
  mode: "shallow-snapshot",
  frontiers: doc.frontiers()
});
```
</Indent>

<Method id="LoroDoc.import">
```typescript no_run
import(data: Uint8Array): ImportStatus
```
</Method>
<Indent>
Imports updates or snapshots into the document. Returns an `ImportStatus` describing which peer ranges were applied or are pending. See [Sync](/docs/tutorial/sync) and [Import Status](/docs/concepts/import_status) for how Loro handles out-of-order and partial updates.

**Parameters:**
- `data` - Binary data or another LoroDoc to import from

**â ï¸ Important:** LoroDoc will automatically commits pending operations before import. If the doc is in detached mode, the imported operations are recorded into OpLog but not applied to DocState until you call `attach()`, see [Attached vs Detached States](/docs/concepts/attached_detached) adn [OpLog and DocState](/docs/concepts/oplog_docstate).

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
// Receive updates from another peer (e.g., via network)
const otherDoc = new LoroDoc();
otherDoc.getText("text").insert(0, "Hello");
const updates: Uint8Array = otherDoc.export({ mode: "update" });

// Import binary updates
const status = doc.import(updates);
console.log(status.success);
```
</Indent>

<Method id="LoroDoc.importBatch">
```typescript no_run
importBatch(data: Uint8Array[]): ImportStatus
```
</Method>
<Indent>
Efficiently imports multiple updates in a single batch operation. See [Batch Import](/docs/advanced/import_batch) for performance considerations and usage.

**Parameters:**
- `data` - Array of binary updates

**Example:**
```typescript no_run
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
declare const update1: Uint8Array;
declare const update2: Uint8Array;
declare const update3: Uint8Array;

// Usage example:
const updates = [update1, update2, update3];
const status = doc.importBatch(updates);
```
</Indent>

<Method id="LoroDoc.exportJsonUpdates">
```typescript no_run
exportJsonUpdates(start?: VersionVector, end?: VersionVector, withPeerCompression?: boolean): JsonSchema
```
</Method>
<Indent>
Exports updates in JSON format for debugging or alternative storage. See [Export Mode](/docs/tutorial/encoding) for format details and trade-offs.

**Parameters:**
- `start` - Starting version (optional)
- `end` - Ending version (optional)

**Returns:** JSON representation of updates

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const jsonUpdates = doc.exportJsonUpdates();
console.log(JSON.stringify(jsonUpdates, null, 2));
```
</Indent>

<Method id="LoroDoc.importJsonUpdates">
```typescript no_run
importJsonUpdates(json: string | JsonSchema): void
```
</Method>
<Indent>
Imports updates from JSON format. Useful for debugging, migration, or custom storage layers; see [Export Mode](/docs/tutorial/encoding).

**Parameters:**
- `json` - JSON string or object containing updates

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const otherDoc = new LoroDoc();
otherDoc.getText("text").insert(0, "Hello");
const jsonStr = otherDoc.exportJsonUpdates();
doc.importJsonUpdates(jsonStr);
```
</Indent>

## Versioning

Work with the history DAG using frontiers (heads) and version vectors. Switch, branch, and merge versions safely without manual conflict resolution. See [Versioning Deep Dive](/docs/advanced/version_deep_dive) and [Attached vs Detached States](/docs/concepts/attached_detached).

### Version Control Methods

<Method id="LoroDoc.checkout">
```typescript no_run
checkout(frontiers: Frontiers): void
```
</Method>
<Indent>
Checks out the document to a specific version, making it read-only at that point in history. This is the core of time travel; see [Time Travel](/docs/tutorial/time_travel) and [Version](/docs/tutorial/version).

**Parameters:**
- `frontiers` - Array of OpIds representing the target version

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const frontiers = doc.frontiers();
// Make some changes...
doc.checkout(frontiers); // Go back to previous version
```
</Indent>

<Indent>
**â ï¸ Important:** In Loro 1.0, `version()`/`frontiers()` include pending (uncommitted) local operations.

**ð Note:** After `checkout()`, the document enters "detached" mode and becomes read-only by default. Use `attach()` or `checkoutToLatest()` to return to editing mode. See [Version Deep Dive](/docs/advanced/version_deep_dive) and [Attached vs Detached States](/docs/concepts/attached_detached).
</Indent>

<Method id="LoroDoc.checkoutToLatest">
```typescript no_run
checkoutToLatest(): void
```
</Method>
<Indent>
Returns the document to the latest version after a checkout. Related concepts: [Frontiers](/docs/concepts/frontiers) and [Version Vector](/docs/concepts/version_vector).

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.checkoutToLatest();
```
</Indent>

<Method id="LoroDoc.attach">
```typescript no_run
attach(): void
```
</Method>
<Indent>
Attaches the document to track latest changes after being detached. See [Attached vs Detached States](/docs/concepts/attached_detached) for how Loro separates current state from history.

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.attach();
```
</Indent>

<Method id="LoroDoc.detach">
```typescript no_run
detach(): void
```
</Method>
<Indent>
Detaches the document from tracking latest changes, freezing it at current version. See [Attached vs Detached States](/docs/concepts/attached_detached).

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.detach();
```
</Indent>

<Method id="LoroDoc.fork">
```typescript no_run
fork(): LoroDoc
```
</Method>
<Indent>
Creates a new document that is a fork of the current one with a new peer ID. Forking is useful for branching workflows; see [Version](/docs/tutorial/version).

**Returns:** A new LoroDoc instance

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const forkedDoc = doc.fork();
```
</Indent>

<Method id="LoroDoc.forkAt">
```typescript no_run
forkAt(frontiers: Frontiers): LoroDoc
```
</Method>
<Indent>
Creates a fork at a specific version in history. Learn more about versions, DAG history, and heads in [Version Deep Dive](/docs/advanced/version_deep_dive).

**Parameters:**
- `frontiers` - The version to fork from

**Returns:** A new LoroDoc instance

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const frontiers = doc.frontiers();
const forkedDoc = doc.forkAt(frontiers);
```
</Indent>

## Events & Transactions

React to changes and group local operations into transactions. Events are delivered asynchronously after a microtask. See [Event Handling](/docs/tutorial/event) and [Transaction Model](/docs/concepts/transaction_model).

### Subscription Methods

<Method id="LoroDoc.subscribe">
```typescript no_run
subscribe(listener: (event: LoroEventBatch) => void): () => void
```
</Method>
<Indent>
Subscribes to all document changes. See [Event Handling](/docs/tutorial/event) for the event model and best practices.

**Parameters:**
- `listener` - Callback function that receives change events

**Returns:** Unsubscribe function

**Event Structure:**
```typescript no_run
interface LoroEventBatch {
  by: "local" | "import" | "checkout"
  origin?: string
  currentTarget?: ContainerID
  events: LoroEvent[]
  from: Frontiers
  to: Frontiers
}
```

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const unsubscribe = doc.subscribe((event) => {
  console.log("Change type:", event.by);
  event.events.forEach(e => {
    console.log("Container changed:", e.target);
    console.log("Diff:", e.diff);
  });
});

// Later: unsubscribe();
```

**â ï¸ Important:** Events are emitted asynchronously after a microtask.
```ts no_run
doc.commit();
await Promise.resolve();
// Now events have been emitted
```

**ð Note:** Multiple operations before a commit are batched into a single event. See [Event Handling](/docs/tutorial/event).
</Indent>

<Method id="LoroDoc.subscribeLocalUpdates">
```typescript no_run
subscribeLocalUpdates(f: (bytes: Uint8Array) => void): () => void
```
</Method>
<Indent>
Subscribes only to local changes, useful for syncing with remote peers. This is typically wired to your transport layer; see [Sync](/docs/tutorial/sync).

**Parameters:**
- `f` - Callback that receives binary updates

**Returns:** Unsubscribe function

**Example:**
```ts no_run threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
declare const websocket: { send: (data: Uint8Array) => void };

// Usage example:
const unsubscribe = doc.subscribeLocalUpdates((updates) => {
  // Send updates to remote peers
  websocket.send(updates);
});
```
</Indent>

<Method id="LoroDoc.subscribeFirstCommitFromPeer">
```typescript no_run
subscribeFirstCommitFromPeer(f: (e: { peer: PeerID }) => void): () => void
```
</Method>
<Indent>
Subscribes to the first commit from each peer, useful for tracking peer metadata.

**Parameters:**
- `f` - Callback that receives peer information

**Returns:** Unsubscribe function

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.subscribeFirstCommitFromPeer(({ peer }) => {
  // Store peer metadata
  doc.getMap("peers").set(peer, {
    joinedAt: Date.now(),
    name: `User ${peer}`
  });
});
```
</Indent>



### Transaction Methods

<Method id="LoroDoc.commit">
```typescript no_run
commit(options?: { origin?: string, message?: string, timestamp?: number }): void
```
</Method>
<Indent>
Commits pending changes as a single transaction. A transaction groups operations into a Change; see [Operations and Changes](/docs/concepts/operations_changes).

**â ï¸ Critical Distinction:** Loro transactions are NOT ACID database transactions:
- No rollback capability
- No isolation guarantees
- Purpose: Bundle local operations for event batching and history grouping
- Many operations (import/export/checkout) trigger implicit commits

See [Transaction Model](/docs/concepts/transaction_model).

**Parameters:**
- `options` - Optional commit configuration
  - `message` - Commit message (persisted in the document like a git commit message, visible to all peers after sync)
  - `origin` - Origin identifier (local only - used for marking local events, remote peers won't see this)
  - `timestamp` - Unix timestamp in seconds (see [Storing Timestamps](/docs/advanced/timestamp))

**Important distinction:**
- `message` is persisted in the document's history and will be synchronized to all peers, similar to git commit messages
- `origin` is only used locally for filtering events (e.g., excluding certain origins from undo) and is NOT synchronized to remote peers

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.commit({
  message: "Updated document title", // Persisted & synced to all peers
  origin: "user-action",             // Local only, for event filtering
  timestamp: Math.floor(Date.now() / 1000)
});
```
</Indent>

### Query Methods

<Method id="LoroDoc.toJSON">
```typescript no_run
toJSON(): Value
```
</Method>
<Indent>
Converts the entire document to a JSON-compatible value. If you prefer a structure where sub-containers are referenced by ID (for privacy or streaming), use getShallowValue(); see [Shallow Snapshots](/docs/concepts/shallow_snapshots).

**Returns:** JSON representation of the document

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const json = doc.toJSON();
console.log(JSON.stringify(json, null, 2));
```
</Indent>

<Method id="LoroDoc.getShallowValue">
```typescript no_run
getShallowValue(): Record<string, ContainerID>
```
</Method>
<Indent>
Gets a shallow representation where sub-containers are represented by their IDs. This is helpful when you want to share structure without history; see [Shallow Snapshots](/docs/concepts/shallow_snapshots).

**Returns:** Shallow JSON value

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const shallow = doc.getShallowValue();
// Sub-containers appear as: "cid:..."
```
</Indent>

<Method id="LoroDoc.getDeepValueWithID">
```typescript no_run
getDeepValueWithID(): any
```
</Method>
<Indent>
Gets the deep value of the document with container IDs preserved. This is useful when you need to traverse the document structure while maintaining references to container IDs.

**Returns:** Document value with container IDs

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const deepValue = doc.getDeepValueWithID();
```
</Indent>

<Method id="LoroDoc.version">
```typescript no_run
version(): VersionVector
```
</Method>
<Indent>
Gets the current version vector of the document. Version vectors track how much data from each peer youâve seen; see [Version Vector](/docs/concepts/version_vector).

**Returns:** Map from PeerID to counter

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const vv = doc.version();
console.log(vv.toJSON());
```
</Indent>

<Method id="LoroDoc.frontiers">
```typescript no_run
frontiers(): Frontiers
```
</Method>
<Indent>
Gets the current frontiers (heads) of the document. Frontiers are a compact representation of a version; see [Frontiers](/docs/concepts/frontiers) for when to use them instead of version vectors.

**ð Note:** Frontiers are a compact version representation.

**â ï¸ Limitation:** When you have a Frontier pointing to operations you don't know about, you cannot determine the complete set of operation IDs included in that version. Version Vectors don't have this limitation but are more verbose. See [Frontiers](/docs/concepts/frontiers) for trade-offs.

**Returns:** Array of OpIds

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const frontiers = doc.frontiers();
// Can be used for checkouts or shallow snapshots
```
</Indent>

<Method id="LoroDoc.diff">
```typescript no_run
diff(from: Frontiers, to: Frontiers, for_json?: boolean): [ContainerID, Diff | JsonDiff][]
```
</Method>
<Indent>
Calculates differences between two versions. Understanding how Loro computes diffs benefits from the history DAG model; see [Version Deep Dive](/docs/advanced/version_deep_dive).

**Parameters:**
- `from` - Starting frontiers
- `to` - Ending frontiers
- `for_json` - If true, returns JsonDiff format (default: true)

**Returns:** Array of container IDs and their diffs

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const fromFrontiers = doc.frontiers();
// Make changes...
const toFrontiers = doc.frontiers();

const diffs = doc.diff(fromFrontiers, toFrontiers);
diffs.forEach(([containerId, diff]) => {
  console.log(`Container ${containerId} changed:`, diff);
});
```
</Indent>

### Pre-Commit Hook

<Method id="LoroDoc.subscribePreCommit">
```typescript no_run
subscribePreCommit(f: (e: { changeMeta: Change, origin: string, modifier: ChangeModifier }) => void): () => void
```
</Method>
<Indent>
Subscribe to the pre-commit event. You can modify the message and timestamp of the next change. This hook runs right before a Change is recorded; see [Transaction Model](/docs/concepts/transaction_model) and [Operations and Changes](/docs/concepts/operations_changes).

Pitfall: `commit()` can be triggered implicitly by `import`, `export`, and `checkout`. Use this hook to attach metadata even for those implicit commits.

```typescript no_run
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const unsubscribe = doc.subscribePreCommit(({ modifier }) => {
  modifier
    .setMessage("Tagged by pre-commit")
    .setTimestamp(Math.floor(Date.now() / 1000));
});
doc.getText("text").insert(0, "Hello");
doc.commit();
unsubscribe();
```
</Indent>

### Cursor Utilities

<Method id="LoroDoc.getCursorPos">
```typescript no_run
getCursorPos(cursor: Cursor): { update?: Cursor, offset: number, side: Side }
```
</Method>
<Indent>
Resolve a stable `Cursor` to an absolute position. Cursors remain valid across concurrent edits; see [Cursor and Stable Positions](/docs/concepts/cursor_stable_positions) and [Cursor tutorial](/docs/tutorial/cursor). The side controls affinity when the cursor sits at an insertion boundary.

```typescript no_run
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const text = doc.getText("text");
text.insert(0, "abc");

// Get cursor at position 1
const c0 = text.getCursor(1);
const pos = doc.getCursorPos(c0!);
console.log(pos.offset); // 1
```
</Indent>

### Pending Operations

<Method id="LoroDoc.getUncommittedOpsAsJson">
```typescript no_run
getUncommittedOpsAsJson(): JsonSchema | undefined
```
</Method>
<Indent>
Get pending operations from the current transaction in JSON format. Useful for debugging what will be included in the next Change; see [Transaction Model](/docs/concepts/transaction_model).

```typescript no_run
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");

text.insert(0, "Hello");
const pending = doc.getUncommittedOpsAsJson();
doc.commit();
const none = doc.getUncommittedOpsAsJson(); // undefined after commit
```
</Indent>

### Change Graph & History
These APIs traverse the history DAG of changes (ancestors/descendants, spans). If this sounds unfamiliar, start with Loro's [Versioning Deep Dive](/docs/advanced/version_deep_dive) and the [Event Graph Walker](/docs/concepts/event_graph_walker).

<Method id="LoroDoc.travelChangeAncestors">
```typescript no_run
travelChangeAncestors(ids: OpId[], f: (change: Change) => boolean): void
```
</Method>
<Indent>
Visit ancestors of the given changes in causal order.

```typescript no_run
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.getText("text").insert(0, "Hello");
doc.commit();
const head = doc.frontiers();
doc.travelChangeAncestors(head, (change) => {
  console.log(change.peer, change.counter);
  return true; // continue
});
```
</Indent>

<Method id="LoroDoc.findIdSpansBetween">
```typescript no_run
findIdSpansBetween(from: Frontiers, to: Frontiers): VersionVectorDiff
```
</Method>
<Indent>
Find the op id spans that lie between two versions.
</Indent>

<Method id="LoroDoc.exportJsonInIdSpan">
```typescript no_run
exportJsonInIdSpan(idSpan: { peer: PeerID, counter: number, length: number }): JsonChange[]
```
</Method>
<Indent>
```typescript no_run
import { LoroDoc } from "loro-crdt";
const a = new LoroDoc();
const b = new LoroDoc();

// Usage example:
a.getText("text").update("Hello");
a.commit();
const snapshot = a.export({ mode: "snapshot" });
let printed: any;
b.subscribe((e) => {
  const spans = b.findIdSpansBetween(e.from, e.to);
  const changes = b.exportJsonInIdSpan(spans.forward[0]);
  printed = changes;
});
b.import(snapshot);
```
</Indent>

<Method id="LoroDoc.getChangedContainersIn">
```typescript no_run
getChangedContainersIn(id: OpId, len: number): ContainerID[]
```
</Method>
<Indent>
Get container IDs modified in the given ID range.

```typescript no_run
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.getList("list").insert(0, 1);
doc.commit();
const head = doc.frontiers()[0];
const containers = doc.getChangedContainersIn(head, 1);
```
</Indent>

### Revert & Apply Diff

<Method id="LoroDoc.revertTo">
```typescript no_run
revertTo(frontiers: Frontiers): void
```
</Method>
<Indent>
Revert the document to a given version by generating inverse operations.

```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.setPeerId("1");
const t = doc.getText("text");
t.update("Hello");
doc.commit();
doc.revertTo([{ peer: "1", counter: 1 }]);
```
</Indent>

<Method id="LoroDoc.applyDiff">
```typescript no_run
applyDiff(diff: [ContainerID, Diff | JsonDiff][]): void
```
</Method>
<Indent>
Apply a batch of diffs to the document.

```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc1 = new LoroDoc();
const doc2 = new LoroDoc();

// Usage example:
doc1.getText("text").insert(0, "Hello");
const diff = doc1.diff([], doc1.frontiers());
doc2.applyDiff(diff);
```
</Indent>

### Detached Editing

<Method id="LoroDoc.setDetachedEditing">
```typescript no_run
setDetachedEditing(enable: boolean): void
```
</Method>
<Indent>
Enables or disables detached editing mode. Detached editing lets you stage edits separate from the latest head; see [Attached vs Detached States](/docs/concepts/attached_detached).

**Parameters:**
- `enable` - Whether to enable detached editing

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.setDetachedEditing(true);
```
</Indent>

<Method id="LoroDoc.isDetachedEditingEnabled">
```typescript no_run
isDetachedEditingEnabled(): boolean
```
</Method>
<Indent>
Checks if detached editing mode is enabled.

**Returns:** True if detached editing is enabled

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const enabled = doc.isDetachedEditingEnabled();
```
</Indent>

<Method id="LoroDoc.isDetached">
```typescript no_run
isDetached(): boolean
```
</Method>
<Indent>
Checks if the document is currently detached.

**Returns:** True if document is detached

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
console.log(doc.isDetached());
```
</Indent>

### Commit Options Helpers

<Method id="LoroDoc.setNextCommitMessage">
```typescript no_run
setNextCommitMessage(msg: string): void
```
</Method>
<Indent>
Sets the message for the next commit.

**Parameters:**
- `msg` - Commit message

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.setNextCommitMessage("User action");
```
</Indent>

<Method id="LoroDoc.setNextCommitOrigin">
```typescript no_run
setNextCommitOrigin(origin: string): void
```
</Method>
<Indent>
Sets the origin for the next commit.

**Parameters:**
- `origin` - Origin identifier

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.setNextCommitOrigin("ui");
```
</Indent>

<Method id="LoroDoc.setNextCommitTimestamp">
```typescript no_run
setNextCommitTimestamp(timestamp: number): void
```
</Method>
<Indent>
Sets the timestamp for the next commit.

**Parameters:**
- `timestamp` - Unix timestamp in seconds

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.setNextCommitTimestamp(Math.floor(Date.now() / 1000));
```
</Indent>

<Method id="LoroDoc.setNextCommitOptions">
```typescript no_run
setNextCommitOptions(options: { origin?: string, timestamp?: number, message?: string }): void
```
</Method>
<Indent>
Sets multiple options for the next commit.

**Parameters:**
- `options` - Commit options object

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.setNextCommitOptions({ origin: "ui", message: "batch" });
doc.getText("text").insert(0, "Hi");
doc.commit();
```
</Indent>

<Method id="LoroDoc.clearNextCommitOptions">
```typescript no_run
clearNextCommitOptions(): void
```
</Method>
<Indent>
Clears all pending commit options.

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.clearNextCommitOptions();
```
</Indent>

### Version & Frontier Utilities

<Method id="LoroDoc.frontiersToVV">
```typescript no_run
frontiersToVV(frontiers: Frontiers): VersionVector
```
</Method>
<Indent>
Converts frontiers to a version vector.

**Parameters:**
- `frontiers` - Frontiers to convert

**Returns:** Version vector representation

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const frontiers = doc.frontiers();
const vv = doc.frontiersToVV(frontiers);
```
</Indent>

<Method id="LoroDoc.vvToFrontiers">
```typescript no_run
vvToFrontiers(vv: VersionVector): Frontiers
```
</Method>
<Indent>
Converts a version vector to frontiers.

**Parameters:**
- `vv` - Version vector to convert

**Returns:** Frontiers representation

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const vv = doc.version();
const frontiers = doc.vvToFrontiers(vv);
```
</Indent>

<Method id="LoroDoc.oplogVersion">
```typescript no_run
oplogVersion(): VersionVector
```
</Method>
<Indent>
Gets the oplog version vector.

**Returns:** Oplog version vector

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const vv = doc.oplogVersion();
```
</Indent>

<Method id="LoroDoc.oplogFrontiers">
```typescript no_run
oplogFrontiers(): Frontiers
```
</Method>
<Indent>
Gets the oplog frontiers.

**Returns:** Oplog frontiers

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const frontiers = doc.oplogFrontiers();
```
</Indent>

<Method id="LoroDoc.cmpWithFrontiers">
```typescript no_run
cmpWithFrontiers(frontiers: Frontiers): -1 | 0 | 1
```
</Method>
<Indent>
Compares current document state with given frontiers.

**Parameters:**
- `frontiers` - Frontiers to compare with

**Returns:** -1 if behind, 0 if equal, 1 if ahead

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const frontiers = doc.frontiers();
const cmp = doc.cmpWithFrontiers(frontiers);
```
</Indent>

<Method id="LoroDoc.cmpFrontiers">
```typescript no_run
cmpFrontiers(a: Frontiers, b: Frontiers): -1 | 0 | 1 | undefined
```
</Method>
<Indent>
Compares two frontiers.

**Parameters:**
- `a` - First frontiers
- `b` - Second frontiers

**Returns:** -1 if a < b, 0 if equal, 1 if a > b, undefined if incomparable

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const f1 = doc.frontiers();
const f2 = doc.frontiers();
const cmp = doc.cmpFrontiers(f1, f2);
```
</Indent>

### JSONPath & Path Queries
Use simple path strings and JSONPath to fetch nested values and containers. Paths are formed from root container names and keys (e.g., map/key or list/0). For container IDs, see [Container ID](/docs/advanced/cid).

<Method id="LoroDoc.getByPath">
```typescript no_run
getByPath(path: string): Value | Container | undefined
```
</Method>
<Indent>
Gets a value or container by its path.

**Parameters:**
- `path` - Path string (e.g., "map/key")

**Returns:** Value or container at the path, or undefined

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");
map.set("key", 1);

// Usage example:
const value = doc.getByPath("map/key");
```
</Indent>

<Method id="LoroDoc.getPathToContainer">
```typescript no_run
getPathToContainer(id: ContainerID): (string | number)[] | undefined
```
</Method>
<Indent>
Gets the path to a container by its ID.

**Parameters:**
- `id` - Container ID

**Returns:** Array representing the path, or undefined

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");

const path = doc.getPathToContainer(map.id);
```
</Indent>

<Method id="LoroDoc.JSONPath">
```typescript no_run
JSONPath(jsonpath: string): any[]
```
</Method>
<Indent>
Queries the document using JSONPath syntax.

**Parameters:**
- `jsonpath` - JSONPath query string

**Returns:** Array of matching values

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");
map.set("key", 1);

// Usage example:
const results = doc.JSONPath("$.map");
```
</Indent>

### Shallow Doc Utilities
These helpers relate to shallow snapshots and redaction. If you need a refresher on what âshallowâ means, see [Shallow Snapshots](/docs/concepts/shallow_snapshots).

<Method id="LoroDoc.shallowSinceVV">
```typescript no_run
shallowSinceVV(): VersionVector
```
</Method>
<Indent>
Gets the version vector since which the document is shallow.

**Returns:** Version vector

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const vv = doc.shallowSinceVV();
```
</Indent>

<Method id="LoroDoc.shallowSinceFrontiers">
```typescript no_run
shallowSinceFrontiers(): Frontiers
```
</Method>
<Indent>
Gets the frontiers since which the document is shallow.

**Returns:** Frontiers

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const frontiers = doc.shallowSinceFrontiers();
```
</Indent>

<Method id="LoroDoc.isShallow">
```typescript no_run
isShallow(): boolean
```
</Method>
<Indent>
Checks if the document is shallow.

**Returns:** True if document is shallow

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const shallow = doc.isShallow();
```
</Indent>

<Method id="LoroDoc.setHideEmptyRootContainers">
```typescript no_run
setHideEmptyRootContainers(hide: boolean): void
```
</Method>
<Indent>
Controls whether empty root containers are hidden in JSON output.

**Parameters:**
- `hide` - Whether to hide empty root containers

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.setHideEmptyRootContainers(true);
// Now empty roots are hidden in toJSON()
```
</Indent>

<Method id="LoroDoc.deleteRootContainer">
```typescript no_run
deleteRootContainer(cid: ContainerID): void
```
</Method>
<Indent>
Deletes a root container.

**Parameters:**
- `cid` - Container ID to delete

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");

// Usage example:
doc.deleteRootContainer(map.id);
```
</Indent>

<Method id="LoroDoc.hasContainer">
```typescript no_run
hasContainer(id: ContainerID): boolean
```
</Method>
<Indent>
Checks if a container exists in the document.

**Parameters:**
- `id` - Container ID to check

**Returns:** True if container exists

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");

const exists = doc.hasContainer(map.id);
```
</Indent>

### JSON Serialization with Replacer

<Method id="LoroDoc.toJsonWithReplacer">
```typescript no_run
toJsonWithReplacer(replacer: (key: string | number, value: Value | Container) => Value | Container | undefined): Value
```
</Method>
<Indent>
Customize JSON serialization of containers and values.

**Parameters:**
- `replacer` - Function to transform values during serialization

**Returns:** Customized JSON value

**Example:**
```ts threeslash
import { LoroDoc, LoroText } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");
text.insert(0, "Hello");

// Usage example:
const json = doc.toJsonWithReplacer((key, value) => {
  if (value instanceof LoroText) {
    return value.toDelta();
  }
  return value;
});
```
</Indent>

### Stats & Introspection

<Method id="LoroDoc.debugHistory">
```typescript no_run
debugHistory(): void
```
</Method>
<Indent>
Prints debug information about the document history.

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.debugHistory();
```
</Indent>

<Method id="LoroDoc.changeCount">
```typescript no_run
changeCount(): number
```
</Method>
<Indent>
Gets the total number of changes in the document.

**Returns:** Number of changes

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const changes = doc.changeCount();
```
</Indent>

<Method id="LoroDoc.opCount">
```typescript no_run
opCount(): number
```
</Method>
<Indent>
Gets the total number of operations in the document.

**Returns:** Number of operations

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const ops = doc.opCount();
```
</Indent>

<Method id="LoroDoc.getAllChanges">
```typescript no_run
getAllChanges(): Map<PeerID, Change[]>
```
</Method>
<Indent>
Gets all changes grouped by peer ID.

**Returns:** Map of peer ID to changes

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const changes = doc.getAllChanges();
```
</Indent>

<Method id="LoroDoc.getChangeAt">
```typescript no_run
getChangeAt(id: OpId): Change
```
</Method>
<Indent>
Gets a specific change by operation ID.

**Parameters:**
- `id` - Operation ID

**Returns:** Change object

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.getText("text").insert(0, "hello");
doc.commit()
const changes = doc.getAllChanges();
const change = changes.get(doc.peerIdStr)?.[0];
```
</Indent>

<Method id="LoroDoc.getChangeAtLamport">
```typescript no_run
getChangeAtLamport(peer_id: string, lamport: number): Change | undefined
```
</Method>
<Indent>
Gets a change by peer ID and Lamport timestamp.

**Parameters:**
- `peer_id` - Peer ID
- `lamport` - Lamport timestamp

**Returns:** Change object or undefined

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.getText("text").insert(0, "hello");
const change = doc.getChangeAtLamport(doc.peerIdStr, 1);
```
</Indent>

<Method id="LoroDoc.getOpsInChange">
```typescript no_run
getOpsInChange(id: OpId): any[]
```
</Method>
<Indent>
Gets all operations in a specific change.

**Parameters:**
- `id` - Operation ID

**Returns:** Array of operations

**Example:**
```typescript no_run
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.getText("text").insert(0, "hello");
const changes = doc.getAllChanges();
const ops = doc.getOpsInChange(changes[0].id);
```
</Indent>

<Method id="LoroDoc.getPendingTxnLength">
```typescript no_run
getPendingTxnLength(): number
```
</Method>
<Indent>
Gets the number of pending operations in the current transaction.

**Returns:** Number of pending operations

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
doc.getText("text").insert(0, "x");
console.log(doc.getPendingTxnLength());
doc.commit();
```
</Indent>

### Import/Export Utilities

<Method id="decodeImportBlobMeta">
```typescript no_run
decodeImportBlobMeta(blob: Uint8Array, check_checksum: boolean): ImportBlobMetadata
```
</Method>
<Indent>
Decodes metadata from an import blob.

**Parameters:**
- `blob` - Binary data to decode
- `check_checksum` - Whether to verify checksum

**Returns:** Import blob metadata

**Example:**
```ts threeslash
import { LoroDoc, decodeImportBlobMeta } from "loro-crdt";

const doc = new LoroDoc();
const updates = doc.export({ mode: "update" });
const meta = decodeImportBlobMeta(updates, true);
```
</Indent>

<Method id="redactJsonUpdates">
```typescript no_run
redactJsonUpdates(json: string | JsonSchema, version_range: any): JsonSchema
```
</Method>
<Indent>
Redacts JSON updates within a specified version range.

Use this to safely remove accidentally leaked sensitive content from history while preserving structure. See [Tips: Redaction](/docs/tutorial/tips).

**Parameters:**
- `json` - JSON updates to redact
- `version_range` - Version range for redaction

**Returns:** Redacted JSON schema

**Example:**
```ts threeslash
import { LoroDoc, redactJsonUpdates } from "loro-crdt";

const doc = new LoroDoc();
const json = doc.exportJsonUpdates();
const redacted = redactJsonUpdates(json, { [doc.peerIdStr]: [0, 999999] });
```
</Indent>

---

## Container Types

Common CRDT containers for modeling JSON-like structures. See [Choosing CRDT Types](/docs/concepts/choose_crdt_type) and [Composing CRDTs](/docs/tutorial/composition) for when to use each and how to nest them.

### LoroText

A rich text container supporting collaborative text editing with formatting. Supports overlapping marks (bold, italic, links) and
stable cursors. The merge semantics avoid interleaving artifacts under concurrency (Fugue + Eg-walker ideas); you use simple index
APIs and Loro handles index transformation.
See [Text](/docs/tutorial/text), [Eg-walker](/docs/advanced/event_graph_walker), and the rich text blog: https://loro.dev/blog/loro-richtext

**â ï¸ Critical: UTF-16 String Encoding**

LoroText uses **UTF-16** encoding, matching JavaScript's native string encoding:
- All standard methods (`insert()`, `delete()`, `mark()`, `slice()`, `charAt()`) use UTF-16 code unit indices
- `length` returns UTF-16 code units (same as JavaScript `string.length`)
- Use `insertUtf8()` and `deleteUtf8()` for UTF-8 byte-based operations when integrating with UTF-8 systems

**â ï¸ Common Pitfalls:**
1. **Index Misalignment**: UTF-16 indices differ from visual character count
2. **Performance**: Cursor queries on deleted positions require history traversal - in that case, it will return a refreshed Cursor object that does not point to the deleted text

**Example with emoji:**
```typescript no_run
const text = doc.getText("text");
text.insert(0, "Hello ð World");
console.log(text.length);        // 13 (emoji counts as 2)
console.log(text.toString()[6]); // â ï¸ Invalid - splits the emoji
text.delete(6, 2);               // â Correct - deletes entire emoji
text.delete(6, 1);               // â Wrong - corrupts the emoji

// Safe iteration
text.iter((char) => {
  console.log(char); // Each character handled correctly
  return true;
});
```

**ð Text vs String in Maps:**
- Use `LoroText` for collaborative text editing where all concurrent edits must be preserved
- Use regular strings in `LoroMap` for atomic values (URLs, IDs, hashes) where Last-Write-Wins is preferred
- Example: URLs should be strings in maps, not LoroText. Otherwise, the automatically merged result may be an invalid URL


<Method id="LoroText.insert">
```typescript no_run
insert(index: number, text: string): void
```
</Method>

<Indent>
Inserts text at the specified position using UTF-16 code unit indices (same as JavaScript string indices).

**Parameters:**
- `index` - UTF-16 code unit position to insert at (0-based, same as JavaScript string index)
- `text` - Text to insert

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");

// Usage example:
text.insert(0, "Hello ");
text.insert(6, "World");
```
</Indent>

<Method id="LoroText.delete">
```typescript no_run
delete(index: number, len: number): void
```
</Method>

<Indent>
Deletes text from the specified position using UTF-16 code units.

**Parameters:**
- `index` - Starting UTF-16 code unit position (same as JavaScript string index)
- `len` - Number of UTF-16 code units to delete

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");
text.insert(0, "Hello ð World");
text.delete(6, 2); // Delete emoji (2 UTF-16 units)
text.delete(5, 1); // Delete space before World
```
</Indent>

<Method id="LoroText.mark">
```typescript no_run
mark(range: { start: number, end: number }, key: string, value: Value): void
```
</Method>

<Indent>
Applies formatting to a text range. Marks can be configured to expand/stop at edges via configTextStyle(); see [Text](/docs/tutorial/text) for mark behavior.

**Parameters:**
- `range` - The range to format
- `key` - Style attribute name
- `value` - Style value

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");
text.insert(0, "Hello World");
doc.configTextStyle({ bold: { expand: "after" } });
text.mark({ start: 0, end: 5 }, "bold", true);
```
</Indent>

<Method id="LoroText.unmark">
```typescript no_run
unmark(range: { start: number, end: number }, key: string): void
```
</Method>
<Indent>
Removes formatting from a text range. For how conflicting edits on marks resolve, see [Text](/docs/tutorial/text).

**Parameters:**
- `range` - The range to unformat
- `key` - Style attribute to remove

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");
text.insert(0, "Hello World");
text.mark({ start: 0, end: 5 }, "bold", true);
text.unmark({ start: 0, end: 5 }, "bold");
```
</Indent>

<Method id="LoroText.toDelta">
```typescript no_run
toDelta(): Delta<string>[]
```
</Method>
<Indent>
Converts text to Delta format (Quill-compatible).

**Returns:** Array of Delta operations

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");
text.insert(0, "Hello World");
text.mark({ start: 0, end: 5 }, "bold", true);
const delta = text.toDelta();
// [{ insert: "Hello", attributes: { bold: true } }, { insert: " World" }]
```
</Indent>

<Method id="LoroText.applyDelta">
```typescript no_run
applyDelta(delta: Delta<string>[]): void
```
</Method>
<Indent>
Applies Delta operations to the text.

**Parameters:**
- `delta` - Array of Delta operations

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");
text.applyDelta([
  { insert: "Hello", attributes: { bold: true } },
  { insert: " World" }
]);
```
</Indent>

<Method id="LoroText.update">
```typescript no_run
update(text: string, options?: { timeoutMs?: number; useRefinedDiff?: boolean }): void
```
</Method>
<Indent>
Updates the current text to the target text using Myers' diff algorithm.

**Parameters:**
- `text` - New text content
- `options` - Update options
  - `timeoutMs` - Optional timeout for the diff computation
  - `useRefinedDiff` - Use refined diff for better quality on long texts

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");

text.insert(0, "Hello");
text.update("Hello World", { timeoutMs: 100 });
```
</Indent>

<Method id="LoroText.updateByLine">
```typescript no_run
updateByLine(text: string, options?: { timeoutMs?: number; useRefinedDiff?: boolean }): void
```
</Method>
<Indent>
Line-based update that's faster for large texts (less precise than `update`).

```typescript no_run
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");

text.insert(0, "Line A\nLine C");
text.updateByLine("Line A\nLine B\nLine C");
```
</Indent>

<Method id="LoroText.getCursor">
```typescript no_run
getCursor(pos: number, side?: Side): Cursor | undefined
```
</Method>
<Indent>
Gets a stable cursor position that survives edits.

**Parameters:**
- `pos` - Position in the text
- `side` - Cursor affinity (-1, 0, or 1)

**Returns:** Cursor object or undefined

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");
text.insert(0, "Hello World");

const cursor = text.getCursor(5);
// Cursor remains valid even after edits
```
</Indent>

<Method id="LoroText.toString">
```typescript no_run
toString(): string
```
</Method>
<Indent>
Converts to plain text string.

**Returns:** Plain text content

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");
text.insert(0, "Hello World");
const plainText = text.toString();
```
</Indent>

<Method id="LoroText.charAt">
```typescript no_run
charAt(pos: number): string
```
</Method>
<Indent>
Gets the character at a specific UTF-16 code unit position.

**Parameters:**
- `pos` - UTF-16 code unit position

**Returns:** Character at position

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");
text.insert(0, "Hello");
const char = text.charAt(1); // "e"
```
</Indent>

<Method id="LoroText.slice">
```typescript no_run
slice(start: number, end: number): string
```
</Method>
<Indent>
Extracts a section of the text using UTF-16 code unit positions.

**Parameters:**
- `start` - Start UTF-16 code unit index
- `end` - End UTF-16 code unit index (exclusive)

**Returns:** Sliced text

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");
text.insert(0, "Hello ð World");

const slice1 = text.slice(0, 5);  // "Hello"
const slice2 = text.slice(6, 8);  // "ð" (emoji spans 6-8)
const slice3 = text.slice(9, 14); // "World"
```
</Indent>

<Method id="LoroText.splice">
```typescript no_run
splice(pos: number, len: number, s: string): string
```
</Method>
<Indent>
Replaces text at a position with new content.

**Parameters:**
- `pos` - Start position
- `len` - Length to delete
- `s` - String to insert

**Returns:** Deleted text

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");
text.insert(0, "Hello World");

// Usage example:
const deleted = text.splice(6, 5, "Loro"); // returns "World"
```
</Indent>

<Method id="LoroText.push">
```typescript no_run
push(s: string): void
```
</Method>
<Indent>
Appends text to the end of the document.

**Parameters:**
- `s` - String to append

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");

text.push("Hello");
text.push(" World");
```
</Indent>

<Method id="LoroText.iter">
```typescript no_run
iter(callback: (char: string) => boolean): void
```
</Method>
<Indent>
Iterates over each character in the text.

**Parameters:**
- `callback` - Function called for each character. Return false to stop iteration.

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");
text.insert(0, "Hello");

// Usage example:
text.iter((char) => {
  console.log(char);
  return true; // continue iteration
});
```
</Indent>

<Method id="LoroText.insertUtf8">
```typescript no_run
insertUtf8(index: number, content: string): void
```
</Method>
<Indent>
Inserts text at a UTF-8 byte index position.

**Parameters:**
- `index` - UTF-8 byte index
- `content` - Text to insert

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");

text.insertUtf8(0, "Hello");
```
</Indent>

<Method id="LoroText.deleteUtf8">
```typescript no_run
deleteUtf8(index: number, len: number): void
```
</Method>
<Indent>
Deletes text at a UTF-8 byte index position.

**Parameters:**
- `index` - UTF-8 byte index
- `len` - Number of UTF-8 bytes to delete

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");
text.insert(0, "Hello World");

// Usage example:
text.deleteUtf8(6, 5); // Delete "World"
```
</Indent>

<Method id="LoroText.getEditorOf">
```typescript no_run
getEditorOf(pos: number): PeerID | undefined
```
</Method>
<Indent>
Gets the peer ID of who last edited the character at a position.

**Parameters:**
- `pos` - Character position

**Returns:** PeerID of last editor or undefined

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");
text.insert(0, "Hello");

// Usage example:
const editor = text.getEditorOf(0);
```
</Indent>

<Method id="LoroText.kind">
```typescript no_run
kind(): "Text"
```
</Method>
<Indent>
Returns the container type.

**Returns:** "Text"

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");

const type = text.kind(); // "Text"
```
</Indent>

<Method id="LoroText.parent">
```typescript no_run
parent(): Container | undefined
```
</Method>
<Indent>
Gets the parent container if this text is nested.

**Returns:** Parent container or undefined

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const list = doc.getList("list");
const text = list.insertContainer(0, doc.getText("nested"));

// Usage example:
const parent = text.parent(); // Returns the list
```
</Indent>

<Method id="LoroText.isAttached">
```typescript no_run
isAttached(): boolean
```
</Method>
<Indent>
Checks if the container is attached to a document.

**Returns:** True if attached

**Example:**
```ts threeslash
import { LoroDoc, LoroText } from "loro-crdt";
const doc = new LoroDoc();
const text = new LoroText();

const attached = text.isAttached(); // false until attached to doc
```
</Indent>

<Method id="LoroText.getAttached">
```typescript no_run
getAttached(): LoroText | undefined
```
</Method>
<Indent>
Gets the attached version of this container.

**Returns:** Attached container or undefined

**Example:**
```ts threeslash
import { LoroDoc, LoroText } from "loro-crdt";
const doc = new LoroDoc();
const text = new LoroText();

const attached = text.getAttached();
```
</Indent>

<Method id="LoroText.isDeleted">
```typescript no_run
isDeleted(): boolean
```
</Method>
<Indent>
Checks if the container has been deleted.

**Returns:** True if deleted

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");

const deleted = text.isDeleted();
```
</Indent>

<Method id="LoroText.getShallowValue">
```typescript no_run
getShallowValue(): string
```
</Method>
<Indent>
Gets the text content without marks.

**Returns:** Plain text string

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");
text.insert(0, "Hello");

// Usage example:
const value = text.getShallowValue(); // "Hello"
```
</Indent>

<Method id="LoroText.toJSON">
```typescript no_run
toJSON(): any
```
</Method>
<Indent>
Converts the text to JSON representation.

**Returns:** JSON value

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");
text.insert(0, "Hello");

// Usage example:
const json = text.toJSON(); // "Hello"
```
</Indent>

<Method id="LoroText.id">
```typescript no_run
readonly id: ContainerID
```
</Method>
<Indent>
Gets the unique container ID.

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");

const containerId = text.id;
```
</Indent>

<Method id="LoroText.length">
```typescript no_run
readonly length: number
```
</Method>
<Indent>
Gets the length of the text in UTF-16 code units (same as JavaScript's `string.length`).

**â ï¸ Important:** Emoji and other characters outside the Basic Multilingual Plane count as 2 UTF-16 units. This affects all index-based operations:
```typescript no_run
text.insert(0, "ð¨âð©âð§âð¦"); // Family emoji
console.log(text.length); // 11 (not 1!) - complex emoji with ZWJ sequences
```

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");
text.insert(0, "Hello");
console.log(text.length); // 5

text.insert(5, " ð");
console.log(text.length); // 8 (space + emoji which counts as 2)
```
</Indent>

### LoroList

An ordered list container for collaborative arrays. Uses index-based APIs; under concurrency, Loro transforms indices
by replaying only the necessary portion of history (Eg-walker-inspired). See [List and Movable List](/docs/tutorial/list),
[Choosing CRDT Types](/docs/concepts/choose_crdt_type), and [Eg-walker](/docs/advanced/event_graph_walker).

**â ï¸ Important: List vs Map for Coordinates**
```typescript no_run
// â WRONG - Don't use List for coordinates
const coord = doc.getList("coord");
coord.push(10); // x
coord.push(20); // y
// Concurrent updates can create [10, 20a, 20b] instead of [10, 20]

// â CORRECT - Use Map for coordinates
const coord = doc.getMap("coord");
coord.set("x", 10);
coord.set("y", 20);
// Concurrent updates properly merge to {x: 10, y: 20}
```

<Method id="LoroList.insert">
```typescript no_run
insert(pos: number, value: Value | Container): void
```
</Method>
<Indent>
Inserts a value at the specified position.

**Parameters:**
- `pos` - Insert position
- `value` - Value to insert

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const list = doc.getList("list");
list.insert(0, "First");
list.insert(1, { type: "object" });
```
</Indent>

<Method id="LoroList.insertContainer">
```typescript no_run
insertContainer<T extends Container>(pos: number, container: T): T
```
</Method>
<Indent>
Inserts a new container at the position.

**Parameters:**
- `pos` - Insert position
- `container` - Container instance

**Returns:** The inserted container

**Example:**
```ts threeslash
import { LoroDoc, LoroText } from "loro-crdt";
const doc = new LoroDoc();
const list = doc.getList("list");
const subText = list.insertContainer(0, new LoroText());
subText.insert(0, "Nested text");
```
</Indent>

<Method id="LoroList.delete">
```typescript no_run
delete(pos: number, len: number): void
```
</Method>
<Indent>
Deletes elements from the list.

**Parameters:**
- `pos` - Starting position
- `len` - Number of elements to delete

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const list = doc.getList("list");
list.push("a");
list.push("b");
list.push("c");
list.push("d");

list.delete(1, 2); // Delete 2 elements starting at index 1
```
</Indent>

<Method id="LoroList.push">
```typescript no_run
push(value: Value | Container): void
```
</Method>
<Indent>
Appends a value to the end of the list.

**Parameters:**
- `value` - Value to append

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const list = doc.getList("list");
list.push("Last item");
```
</Indent>

<Method id="LoroList.getIdAt">
```typescript no_run
getIdAt(pos: number): { peer: PeerID, counter: number } | undefined
```
</Method>
<Indent>
```typescript no_run
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const list = doc.getList("list");

list.insert(0, 1);
const id0 = list.getIdAt(0);
```
</Indent>

<Method id="LoroList.pushContainer">
```typescript no_run
pushContainer<T extends Container>(container: T): T
```
</Method>
<Indent>
Appends a container to the end of the list.

**Parameters:**
- `container` - Container to append

**Returns:** The appended container

**Example:**
```ts threeslash
import { LoroDoc, LoroMap } from "loro-crdt";
const doc = new LoroDoc();
const list = doc.getList("list");
const map = list.pushContainer(new LoroMap());
map.set("key", "value");
```
</Indent>

<Method id="LoroList.pop">
```typescript no_run
pop(): Value | Container | undefined
```
</Method>
<Indent>
Removes and returns the last element.

**Returns:** The removed element or undefined

**Example:**
```ts no_run threeslash
import { LoroList } from "loro-crdt";
declare const list: LoroList;
// ---cut---
const lastItem = list.pop();
```
</Indent>

<Method id="LoroList.get">
```typescript no_run
get(index: number): Value | Container | undefined
```
</Method>
<Indent>
Gets the value at the specified index.

**Parameters:**
- `index` - Element index

**Returns:** The value or undefined

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const list = doc.getList("items");
list.push("first", "second");

const item = list.get(0); // "first"
```
</Indent>

<Method id="LoroList.getCursor">
```typescript no_run
getCursor(pos: number, side?: Side): Cursor | undefined
```
</Method>
<Indent>
Gets a stable cursor for the position.

**Parameters:**
- `pos` - Position in the list
- `side` - Cursor affinity

**Returns:** Cursor object or undefined

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
const list = doc.getList("list");
list.push("a", "b", "c");

const cursor = list.getCursor(2);
```
</Indent>

<Method id="LoroList.toArray">
```typescript no_run
toArray(): (Value | Container)[]
```
</Method>
<Indent>
Converts the list to a JavaScript array.

**Returns:** Array of values

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const list = doc.getList("list");
list.push("a", "b", "c");
const array = list.toArray();
```
</Indent>

<Method id="LoroList.clear">
```typescript no_run
clear(): void
```
</Method>
<Indent>
Removes all elements from the list.

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const list = doc.getList("list");
list.push("a", "b", "c");
list.clear();
```
</Indent>

<Method id="LoroList.length">
```typescript no_run
length: number
```
</Method>
<Indent>
Gets the number of elements in the list.

**Returns:** List length

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const list = doc.getList("list");
list.push("a");
list.push("b");
list.push("c");
console.log(`List has ${list.length} items`);
```
</Indent>

<Method id="LoroList.kind">
```typescript no_run
kind(): "List"
```
</Method>
<Indent>
Returns the container type.

**Returns:** "List"

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const list = doc.getList("list");

const type = list.kind(); // "List"
```
</Indent>

<Method id="LoroList.toJSON">
```typescript no_run
toJSON(): any
```
</Method>
<Indent>
Converts the list to JSON representation.

**Returns:** JSON array

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const list = doc.getList("list");
list.push(1, 2, 3);

// Usage example:
const json = list.toJSON(); // [1, 2, 3]
```
</Indent>

<Method id="LoroList.parent">
```typescript no_run
parent(): Container | undefined
```
</Method>
<Indent>
Gets the parent container if this list is nested.

**Returns:** Parent container or undefined

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");
const list = map.setContainer("nested", doc.getList("list"));

// Usage example:
const parent = list.parent(); // Returns the map
```
</Indent>

<Method id="LoroList.isAttached">
```typescript no_run
isAttached(): boolean
```
</Method>
<Indent>
Checks if the container is attached to a document.

**Returns:** True if attached

**Example:**
```ts threeslash
import { LoroDoc, LoroList } from "loro-crdt";
const doc = new LoroDoc();
const list = new LoroList();

const attached = list.isAttached(); // false until attached to doc
```
</Indent>

<Method id="LoroList.getAttached">
```typescript no_run
getAttached(): LoroList | undefined
```
</Method>
<Indent>
Gets the attached version of this container.

**Returns:** Attached container or undefined

**Example:**
```ts threeslash
import { LoroDoc, LoroList } from "loro-crdt";
const doc = new LoroDoc();
const list = new LoroList();

const attached = list.getAttached();
```
</Indent>

<Method id="LoroList.isDeleted">
```typescript no_run
isDeleted(): boolean
```
</Method>
<Indent>
Checks if the container has been deleted.

**Returns:** True if deleted

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const list = doc.getList("list");

const deleted = list.isDeleted();
```
</Indent>

<Method id="LoroList.getShallowValue">
```typescript no_run
getShallowValue(): Value[]
```
</Method>
<Indent>
Gets the list values with sub-containers as IDs.

**Returns:** Array of values

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const list = doc.getList("list");
list.push(1, 2);

// Usage example:
const values = list.getShallowValue(); // [1, 2]
```
</Indent>

<Method id="LoroList.id">
```typescript no_run
readonly id: ContainerID
```
</Method>
<Indent>
Gets the unique container ID.

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const list = doc.getList("list");

const containerId = list.id;
```
</Indent>

### LoroMap

A key-value map container for collaborative objects. See [Map](/docs/tutorial/map).

<Method id="LoroMap.set">
```typescript no_run
set(key: string, value: Value | Container): void
```
</Method>
<Indent>
Sets a key-value pair.

Note: Setting a key to the same value is a no-op (no operation recorded). See [Map basics](/docs/tutorial/map).

**Parameters:**
- `key` - The key
- `value` - The value

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");

map.set("name", "Alice");
map.set("age", 30);
```
</Indent>

<Method id="LoroMap.setContainer">
```typescript no_run
setContainer<T extends Container>(key: string, container: T): T
```
</Method>
<Indent>
Sets a container as the value for a key.

**â ï¸ Pitfall:** Concurrent child container creation at the same key on a LoroMap from multiple peers causes overwrites (different container IDs cannot auto-merge).  See [Container initialization](/docs/tutorial/tips).

**Parameters:**
- `key` - The key
- `container` - Container instance

**Returns:** The set container

**Example:**
```ts threeslash
import { LoroDoc, LoroList } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");

const list = map.setContainer("items", new LoroList());
list.push("item1");
```
</Indent>

<Method id="LoroMap.get">
```typescript no_run
get(key: string): Value | Container | undefined
```
</Method>
<Indent>
Gets the value for a key.

**Parameters:**
- `key` - The key

**Returns:** The value or undefined

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");

const name = map.get("name");
```
</Indent>

<Method id="LoroMap.getOrCreateContainer">
```typescript no_run
getOrCreateContainer<T extends Container>(key: string, container: T): T
```
</Method>
<Indent>
Gets an existing container or creates a new one.

**â ï¸ Pitfall:** Parallel container creation for the same key across peers causes overwrites. See [Container initialization](/docs/tutorial/tips).

**Parameters:**
- `key` - The key
- `container` - Container to create if not exists

**Returns:** The container

**Example:**
```ts threeslash
import { LoroDoc, LoroText } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");

const text = map.getOrCreateContainer("description", new LoroText());
```
</Indent>

<Method id="LoroMap.delete">
```typescript no_run
delete(key: string): void
```
</Method>
<Indent>
Removes a key-value pair.

**Parameters:**
- `key` - The key to remove

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");

map.delete("obsoleteKey");
```
</Indent>

<Method id="LoroMap.clear">
```typescript no_run
clear(): void
```
</Method>
<Indent>
Removes all key-value pairs.

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");

map.clear();
```
</Indent>

<Method id="LoroMap.keys">
```typescript no_run
keys(): string[]
```
</Method>
<Indent>
Gets all keys in the map.

**Returns:** Array of keys

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");

const allKeys = map.keys();
```
</Indent>

<Method id="LoroMap.values">
```typescript no_run
values(): (Value | Container)[]
```
</Method>
<Indent>
Gets all values in the map.

**Returns:** Array of values

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");

const allValues = map.values();
```
</Indent>

<Method id="LoroMap.entries">
```typescript no_run
entries(): [string, Value | Container][]
```
</Method>
<Indent>
Gets all key-value pairs.

**Returns:** Array of entries

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");

for (const [key, value] of map.entries()) {
  console.log(`${key}: ${value}`);
}
```
</Indent>

<Method id="LoroMap.getLastEditor">
```typescript no_run
getLastEditor(key: string): PeerID | undefined
```
</Method>
<Indent>
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");

map.set("k", 1);
doc.commit();
const who = map.getLastEditor("k");
// who = doc.peerIdStr
```
</Indent>

<Method id="LoroMap.size">
```typescript no_run
size: number
```
</Method>
<Indent>
Gets the number of key-value pairs.

**Returns:** Map size

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");

console.log(`Map has ${map.size} entries`);
```
</Indent>

<Method id="LoroMap.kind">
```typescript no_run
kind(): "Map"
```
</Method>
<Indent>
Returns the container type.

**Returns:** "Map"

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");

const type = map.kind(); // "Map"
```
</Indent>

<Method id="LoroMap.toJSON">
```typescript no_run
toJSON(): any
```
</Method>
<Indent>
Converts the map to JSON representation.

**Returns:** JSON object

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");
map.set("name", "Alice");

// Usage example:
const json = map.toJSON(); // { name: "Alice" }
```
</Indent>

<Method id="LoroMap.parent">
```typescript no_run
parent(): Container | undefined
```
</Method>
<Indent>
Gets the parent container if this map is nested.

**Returns:** Parent container or undefined

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const list = doc.getList("list");
const map = list.insertContainer(0, doc.getMap("nested"));

// Usage example:
const parent = map.parent(); // Returns the list
```
</Indent>

<Method id="LoroMap.isAttached">
```typescript no_run
isAttached(): boolean
```
</Method>
<Indent>
Checks if the container is attached to a document.

**Returns:** True if attached

**Example:**
```ts threeslash
import { LoroDoc, LoroMap } from "loro-crdt";
const doc = new LoroDoc();
const map = new LoroMap();

const attached = map.isAttached(); // false until attached to doc
```
</Indent>

<Method id="LoroMap.getAttached">
```typescript no_run
getAttached(): LoroMap | undefined
```
</Method>
<Indent>
Gets the attached version of this container.

**Returns:** Attached container or undefined

**Example:**
```ts threeslash
import { LoroDoc, LoroMap } from "loro-crdt";
const doc = new LoroDoc();
const map = new LoroMap();

const attached = map.getAttached();
```
</Indent>

<Method id="LoroMap.isDeleted">
```typescript no_run
isDeleted(): boolean
```
</Method>
<Indent>
Checks if the container has been deleted.

**Returns:** True if deleted

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");

const deleted = map.isDeleted();
```
</Indent>

<Method id="LoroMap.getShallowValue">
```typescript no_run
getShallowValue(): Record<string, Value>
```
</Method>
<Indent>
Gets the map values with sub-containers as IDs.

**Returns:** Object with values

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");
map.set("key", "value");

// Usage example:
const values = map.getShallowValue(); // { key: "value" }
```
</Indent>

<Method id="LoroMap.id">
```typescript no_run
readonly id: ContainerID
```
</Method>
<Indent>
Gets the unique container ID.

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");

const containerId = map.id;
```
</Indent>

### LoroTree

A hierarchical tree container for nested structures. Supports moving subtrees while handling concurrent edits. See [Tree](/docs/tutorial/tree).

**â ï¸ Important Tree Operation Notes:**
- **Concurrent moves can create cycles**: Loro detects and prevents these automatically
- **Fractional indexing**: Has interleaving issues but maintains relative ordering
- **Don't disable fractional index** if you need siblings to be sorted. See [Tree](/docs/tutorial/tree).

<Method id="LoroTree.createNode">
```typescript no_run
createNode(parent?: TreeID, index?: number): LoroTreeNode
```
</Method>
<Indent>
Creates a new tree node.

**Parameters:**
- `parent` - Parent node ID (optional, creates root if omitted)
- `index` - Position among siblings (optional)

**Returns:** The new node's handler

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");

const root = tree.createNode();
const child = root.createNode(0);
```
</Indent>

<Method id="LoroTree.move">
```typescript no_run
move(target: TreeID, parent?: TreeID, index?: number): void
```
</Method>
<Indent>
Moves a node to a new position.

**Parameters:**
- `target` - Node to move
- `parent` - New parent (undefined for root)
- `index` - Position among siblings

**Example:**
```typescript no_run threeslash
import { LoroDoc, TreeID } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");
declare const nodeId: TreeID;
declare const newParentId: TreeID;

// Usage example:
tree.move(nodeId, newParentId, 0);
```
</Indent>

<Method id="LoroTree.delete">
```typescript no_run
delete(target: TreeID): void
```
</Method>
<Indent>
Deletes a node and its descendants.

**Parameters:**
- `target` - Node to delete

**Example:**
```ts threeslash
import { LoroDoc, TreeID } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");
const node = tree.createNode();
const nodeId = node.id;

// Usage example:
tree.delete(nodeId);
```
</Indent>

<Method id="LoroTree.getNodeByID">
```typescript no_run
getNodeByID(id: TreeID): LoroTreeNode | undefined
```
</Method>
<Indent>
Gets a node handler by its ID.

**Parameters:**
- `id` - Node ID

**Returns:** Node handler or undefined

**Example:**
```ts threeslash
import { LoroDoc, TreeID } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");
const _node = tree.createNode();
const nodeId = _node.id;

// Usage example:
const node = tree.getNodeByID(nodeId);
if (node) {
  node.data.set("label", "New Label");
}
```
</Indent>

<Method id="LoroTree.nodes">
```typescript no_run
nodes(): LoroTreeNode[]
```
</Method>
<Indent>
Gets all nodes in the tree.

**Returns:** Array of all nodes

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");

const allNodes = tree.nodes();
```
</Indent>

<Method id="LoroTree.roots">
```typescript no_run
roots(): LoroTreeNode[]
```
</Method>
<Indent>
Gets all root nodes.

**Returns:** Array of root nodes

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");

const rootNodes = tree.roots();
```
</Indent>

<Method id="LoroTree.has">
```typescript no_run
has(target: TreeID): boolean
```
</Method>
<Indent>
Checks if a node exists.

**Parameters:**
- `target` - Node ID to check

**Returns:** Boolean indicating existence

**Example:**
```typescript no_run threeslash
import { LoroDoc, TreeID } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");
declare const nodeId: TreeID;

// Usage example:
if (tree.has(nodeId)) {
  console.log("Node exists");
}
```
</Indent>

<Method id="LoroTree.isNodeDeleted">
```typescript no_run
isNodeDeleted(target: TreeID): boolean
```
</Method>
<Indent>
Checks if a node has been deleted.

**Parameters:**
- `target` - Node ID to check

**Returns:** Boolean indicating deletion status

**Example:**
```typescript no_run threeslash
import { LoroDoc, TreeID } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");
declare const nodeId: TreeID;

// Usage example:
if (tree.isNodeDeleted(nodeId)) {
  console.log("Node was deleted");
}
```
</Indent>
<Method id="LoroTree.enableFractionalIndex">
```typescript no_run
enableFractionalIndex(jitter: number): void
```
</Method>
<Indent>
Enables fractional indexing for better concurrent move performance.

**Parameters:**
- `jitter` - Jitter amount for fractional indices

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");

tree.enableFractionalIndex(0.001);
```
</Indent>

<Method id="LoroTree.disableFractionalIndex">
```typescript no_run
disableFractionalIndex(): void
```
</Method>
<Indent>
Disables fractional indexing.

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");

tree.disableFractionalIndex();
```
</Indent>

<Method id="LoroTree.isFractionalIndexEnabled">
```typescript no_run
isFractionalIndexEnabled(): boolean
```
</Method>
<Indent>
Checks if fractional indexing is enabled.

**Returns:** True if enabled

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");

const enabled = tree.isFractionalIndexEnabled();
```
</Indent>

<Method id="LoroTree.kind">
```typescript no_run
kind(): "Tree"
```
</Method>
<Indent>
Returns the container type.

**Returns:** "Tree"

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");

const type = tree.kind(); // "Tree"
```
</Indent>

<Method id="LoroTree.toJSON">
```typescript no_run
toJSON(): any
```
</Method>
<Indent>
Converts the tree to JSON representation.

**Returns:** JSON tree structure

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");

const json = tree.toJSON();
```
</Indent>

<Method id="LoroTree.parent">
```typescript no_run
parent(): Container | undefined
```
</Method>
<Indent>
Gets the parent container if this tree is nested.

**Returns:** Parent container or undefined

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");
const tree = map.setContainer("tree", doc.getTree("nested"));

// Usage example:
const parent = tree.parent();
```
</Indent>

<Method id="LoroTree.isAttached">
```typescript no_run
isAttached(): boolean
```
</Method>
<Indent>
Checks if the container is attached to a document.

**Returns:** True if attached

**Example:**
```ts threeslash
import { LoroDoc, LoroTree } from "loro-crdt";
const doc = new LoroDoc();
const tree = new LoroTree();

const attached = tree.isAttached();
```
</Indent>

<Method id="LoroTree.getAttached">
```typescript no_run
getAttached(): LoroTree | undefined
```
</Method>
<Indent>
Gets the attached version of this container.

**Returns:** Attached container or undefined

**Example:**
```ts threeslash
import { LoroDoc, LoroTree } from "loro-crdt";
const doc = new LoroDoc();
const tree = new LoroTree();

const attached = tree.getAttached();
```
</Indent>

<Method id="LoroTree.isDeleted">
```typescript no_run
isDeleted(): boolean
```
</Method>
<Indent>
Checks if the container has been deleted.

**Returns:** True if deleted

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");

const deleted = tree.isDeleted();
```
</Indent>

<Method id="LoroTree.getShallowValue">
```typescript no_run
getShallowValue(): TreeNodeShallowValue[]
```
</Method>
<Indent>
Gets the tree values with sub-containers as IDs.

**Returns:** Array of tree node values

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");

const values = tree.getShallowValue();
```
</Indent>

<Method id="LoroTree.id">
```typescript no_run
readonly id: ContainerID
```
</Method>
<Indent>
Gets the unique container ID.

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");

const containerId = tree.id;
```
</Indent>

### LoroTreeNode

Represents a single node in the tree.

<Method id="LoroTreeNode.data">
```typescript no_run
data: LoroMap
```
</Method>
<Indent>
A map container for storing node metadata.

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");
const node = tree.createNode();
node.data.set("title", "Node Title");
node.data.set("expanded", true);
```
</Indent>

<Method id="LoroTreeNode.createNode">
```typescript no_run
createNode(index?: number): LoroTreeNode
```
</Method>
<Indent>
Creates a child node.

**Parameters:**
- `index` - Position among siblings

**Returns:** New node's handler

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");
const node = tree.createNode();
const childId = node.createNode(0);
```
</Indent>

<Method id="LoroTreeNode.move">
```typescript no_run
move(parent?: LoroTreeNode, index?: number): void
```
</Method>
<Indent>
Moves this node to a new parent.

**Parameters:**
- `parent` - New parent node
- `index` - Position among siblings

**Example:**
```ts threeslash
import { LoroDoc, LoroTreeNode } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");
const node = tree.createNode();
const parent = tree.createNode();

// Usage example:
node.move(parent, 0);
```
</Indent>

<Method id="LoroTreeNode.moveAfter">
```typescript no_run
moveAfter(target: LoroTreeNode): void
```
</Method>
<Indent>
Moves this node after a sibling.

**Parameters:**
- `target` - Sibling node

**Example:**
```ts threeslash
import { LoroDoc, LoroTreeNode } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");
const node = tree.createNode();
const sibling = tree.createNode();

// Usage example:
node.moveAfter(sibling);
```
</Indent>

<Method id="LoroTreeNode.moveBefore">
```typescript no_run
moveBefore(target: LoroTreeNode): void
```
</Method>
<Indent>
Moves this node before a sibling.

**Parameters:**
- `target` - Sibling node

**Example:**
```ts threeslash
import { LoroDoc, LoroTreeNode } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");
const node = tree.createNode();
const sibling = tree.createNode();

// Usage example:
node.moveBefore(sibling);
```
</Indent>

<Method id="LoroTreeNode.parent">
```typescript no_run
parent(): LoroTreeNode | undefined
```
</Method>
<Indent>
Gets the parent node.

**Returns:** Parent node or undefined

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");
const node = tree.createNode();

// Usage example:
const parentNode = node.parent();
```
</Indent>

<Method id="LoroTreeNode.children">
```typescript no_run
children(): LoroTreeNode[]
```
</Method>
<Indent>
Gets all child nodes.

**Returns:** Array of children

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");
const node = tree.createNode();

// Usage example:
const childNodes = node.children();
```
</Indent>

<Method id="LoroTreeNode.index">
```typescript no_run
index(): number | undefined
```
</Method>
<Indent>
Gets the position among siblings.

**Returns:** Index or undefined if root

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");
const node = tree.createNode();

// Usage example:
const position = node.index();
```
</Indent>

<Method id="LoroTreeNode.fractionalIndex">
```typescript no_run
fractionalIndex(): string | undefined
```
</Method>
<Indent>
Returns the node's fractional index used to sort siblings deterministically.
It is a hex string representation of the Fractional Index and is stable for ordering.
Returns `undefined` for the root node. Note: the tree must be attached to the document.

**Returns:** Hex string or `undefined` for root

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");
const parent = tree.createNode();
const a = parent.createNode(0);
const b = parent.createNode(1);

const aFi = a.fractionalIndex();
const bFi = b.fractionalIndex();
// aFi < bFi, because b is inserted after a
```
</Indent>

<Method id="LoroTreeNode.creationId">
```typescript no_run
creationId(): { peer: PeerID, counter: number }
```
</Method>
<Indent>
Returns the OpID that created this node.

**Returns:** `{ peer: PeerID, counter: number }` creation identifier

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");
const node = tree.createNode();

const { peer, counter } = node.creationId();
```
</Indent>

<Method id="LoroTreeNode.creator">
```typescript no_run
creator(): PeerID
```
</Method>
<Indent>
Returns the peer ID that created this node (equivalent to `creationId().peer`).

**Returns:** `PeerID`

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");
const node = tree.createNode();

const author = node.creator();
// author == doc.peerIdStr
```
</Indent>

<Method id="LoroTreeNode.getLastMoveId">
```typescript no_run
getLastMoveId(): { peer: PeerID, counter: number } | undefined
```
</Method>
<Indent>
Returns the OpID of the most recent move operation for this node, or `undefined` if the node has never been moved.

**Returns:** Creation/move OpID or `undefined`

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");
const node = tree.createNode();

const lastMove = node.getLastMoveId();
```
</Indent>

<Method id="LoroTreeNode.isDeleted">
```typescript no_run
isDeleted(): boolean
```
</Method>
<Indent>
Checks if this node has been deleted.

**Returns:** Boolean deletion status

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");
const node = tree.createNode();

// Usage example:
if (node.isDeleted()) {
  console.log("Node is deleted");
}
```
</Indent>

### LoroCounter

A counter CRDT for collaborative numeric values.

<Method id="LoroCounter.increment">
```typescript no_run
increment(value: number): void
```
</Method>
<Indent>
Increments the counter.

**Parameters:**
- `value` - Amount to increment (default: 1)

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const counter = doc.getCounter("counter");

counter.increment(5);   // +5
```
</Indent>

<Method id="LoroCounter.decrement">
```typescript no_run
decrement(value: number): void
```
</Method>
<Indent>
Decrements the counter.

**Parameters:**
- `value` - Amount to decrement (default: 1)

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const counter = doc.getCounter("counter");

counter.decrement(3);   // -3
```
</Indent>

<Method id="LoroCounter.value">
```typescript no_run
value: number
```
</Method>
<Indent>
Gets the current counter value.

**Returns:** Current numeric value

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const counter = doc.getCounter("counter");

console.log(`Counter value: ${counter.value}`);
```
</Indent>

### LoroMovableList

A list optimized for move operations. Designed for frequent reordering (drag-and-drop) with good behavior under
concurrent moves (concurrent moves resolve to one final position). See [List and Movable List](/docs/tutorial/list).

**ð MovableList vs List:**
- **Use MovableList** for: Drag-and-drop UIs, sortable lists, kanban boards
- **Use List** for: scenarios where the list items don't need to be moved
- **Key difference**: MovableList handles concurrent moves better (no duplicates) and supports set operations, List is more efficient in general.

<Method id="LoroMovableList.move">
```typescript no_run
move(from: number, to: number): void
```
</Method>
<Indent>
Moves an element from one position to another.

**Parameters:**
- `from` - Source index
- `to` - Target index

**Example:**
```typescript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const movableList = doc.getMovableList("list");
movableList.push("a");
movableList.push("b");
movableList.push("c");
movableList.push("d");
movableList.push("e");

movableList.move(0, 3); // Move first element to fourth position
```
</Indent>

<Method id="LoroMovableList.set">
```typescript no_run
set(pos: number, value: Value | Container): void
```
</Method>
<Indent>
Replaces the value at a position.

**Parameters:**
- `pos` - Position to update
- `value` - New value

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const movableList = doc.getMovableList("list");
movableList.push("a", "b", "c");

movableList.set(0, "Updated value");
```
</Indent>

<Method id="LoroMovableList.setContainer">
```typescript no_run
setContainer<T extends Container>(pos: number, container: T): T
```
</Method>
<Indent>
Replaces the value with a container.

**Parameters:**
- `pos` - Position to update
- `container` - New container

**Returns:** The set container

**Example:**
```ts threeslash
import { LoroDoc, LoroText } from "loro-crdt";
const doc = new LoroDoc();
const movableList = doc.getMovableList("list");
movableList.push("placeholder");

const text = movableList.setContainer(0, new LoroText());
```
</Indent>

## Synchronization

Import/export updates over any transport and choose the right encoding for speed and size. See [Sync Tutorial](/docs/tutorial/sync) and [Encoding & Export Modes](/docs/tutorial/encoding).

- **Import order**: Loro handles out-of-order updates automatically
- **Auto-commit**: Import and export operations trigger automatic commits

### Import/Export Patterns

#### Basic Synchronization

```ts threeslash
import { LoroDoc } from "loro-crdt";

// Peer A: Export updates
const doc1 = new LoroDoc();
doc1.getText("text").insert(0, "Hello");
const updates = doc1.export({ mode: "update" });

// Peer B: Import updates
const doc2 = new LoroDoc();
doc2.import(updates);
// now doc2.getText("text").toString() === "Hello"
```

#### Continuous Sync

```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc1 = new LoroDoc();
const doc2 = new LoroDoc();

// Usage example:
// Set up bidirectional sync
doc1.subscribeLocalUpdates((updates) => {
  doc2.import(updates);
});

doc2.subscribeLocalUpdates((updates) => {
  doc1.import(updates);
});
```

Performance tips:
- Prefer `mode: "update"` with a `VersionVector` to sync incrementally.
- Use `mode: "shallow-snapshot"` when you only need current state; it strips history for faster import/load.
- Loroâs LSM-based encoding and Eg-walker-inspired merge keep import/export fast, even for large histories.
  See [Encoding](/docs/tutorial/encoding) and v1.0 performance notes: https://loro.dev/blog/v1.0

#### Network Sync with WebSocket

```ts no_run threeslash
import { LoroDoc } from "loro-crdt";

// Assume we have:
declare const ws: {
  send: (data: Uint8Array) => void;
  on: (event: string, handler: (data: any) => void) => void;
};

// Client side
const doc = new LoroDoc();

// Send local updates to server
doc.subscribeLocalUpdates((updates) => {
  ws.send(updates);
});

// Receive updates from server
ws.on('message', (data) => {
  doc.import(new Uint8Array(data));
});
```

### Shallow Snapshots

Shallow snapshots allow for efficient storage by garbage collecting deleted operations.

```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
// Create shallow snapshot
const frontiers = doc.frontiers();
const shallowSnapshot = doc.export({
  mode: "shallow-snapshot",
  frontiers: frontiers
});

// Import shallow snapshot
const newDoc = new LoroDoc();
newDoc.import(shallowSnapshot);
```

---

## Version Control

### Time Travel

Navigate through document history:

```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
// Save current version
const v1 = doc.frontiers();

// Make changes
doc.getText("text").insert(0, "New text");

// Save new version
const v2 = doc.frontiers();

// Travel back
doc.checkout(v1);
console.log(doc.getText("text").toString()); // Original text

// Travel forward
doc.checkout(v2);
console.log(doc.getText("text").toString()); // New text

// Return to latest
doc.checkoutToLatest();
```

### Forking

Create document branches:

```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
// Fork at current state
const fork1 = doc.fork();
fork1.getText("text").insert(0, "Fork 1 changes");

// Fork at specific version
const historicalVersion = doc.frontiers();
const fork2 = doc.forkAt(historicalVersion);
fork2.getText("text").insert(0, "Fork 2 changes");

// Original document remains unchanged
```

### Version Vectors

Track document versions:

```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const vv = doc.version();
console.log(`Document has ${vv.length()} peers`);
```

---

## Events & Subscriptions

### Event Structure

```typescript no_run
interface LoroEventBatch {
  by: "local" | "import" | "checkout"
  origin?: string
  currentTarget?: ContainerID
  events: LoroEvent[]
  from: Frontiers
  to: Frontiers
}

interface LoroEvent {
  target: ContainerID
  diff: Diff
  path: Path
}
```

### Diff Types

Different containers produce different diff types:

#### TextDiff

<Indent>
```typescript no_run
type TextDiff = {
  type: "text"
  diff: Delta<string>[]
}
```
Represents changes to text content using Delta format.

**Properties:**
- `type` - Always "text" for text diffs
- `diff` - Array of Delta operations (insert, delete, retain)

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");

// Example
text.subscribe((e) => {
  for (const event of e.events) {
    if (event.diff.type === "text") {
      event.diff.diff.forEach(delta => {
        if (delta.insert) {
          console.log(`Inserted: "${delta.insert}"`);
        }
        if (delta.delete) {
          console.log(`Deleted ${delta.delete} characters`);
        }
      });
    }
  }
});
```
</Indent>

#### ListDiff

<Indent>
```typescript no_run
type ListDiff = {
  type: "list"
  diff: Delta<(Value | Container)[]>[]
}
```
Represents changes to list content using Delta format.

**Properties:**
- `type` - Always "list" for list diffs
- `diff` - Array of Delta operations on list items

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const list = doc.getList("list");

// Example
list.subscribe((e) => {
  for (const event of e.events) {
    if (event.diff.type === "list") {
      event.diff.diff.forEach(delta => {
        if (delta.insert) {
          console.log(`Inserted items:`, delta.insert);
        }
      });
    }
  }
});
```
</Indent>

#### MapDiff

<Indent>
```typescript no_run
type MapDiff = {
  type: "map"
  updated: Record<string, Value | Container | undefined>
}
```
Represents changes to map content.

**Properties:**
- `type` - Always "map" for map diffs
- `updated` - Record of key-value changes (undefined means deleted)

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");

// Example
map.subscribe((e) => {
  for (const event of e.events) {
    if (event.diff.type === "map") {
      Object.entries(event.diff.updated).forEach(([key, value]) => {
        if (value === undefined) {
          console.log(`Deleted key: ${key}`);
        } else {
          console.log(`Updated key: ${key} = ${value}`);
        }
      });
    }
  }
});
```
</Indent>

#### TreeDiff

<Indent>
```typescript no_run
type TreeDiff = {
  type: "tree"
  diff: TreeDiffItem[]
}

type TreeDiffItem =
  | {
      target: TreeID
      action: "create"
      parent: TreeID | undefined
      index: number
      fractionalIndex: string
    }
  | {
      target: TreeID
      action: "delete"
      oldParent: TreeID | undefined
      oldIndex: number
    }
  | {
      target: TreeID
      action: "move"
      parent: TreeID | undefined
      index: number
      fractionalIndex: string
      oldParent: TreeID | undefined
      oldIndex: number
    }
```
Represents changes to tree structure.

**Properties:**
- `type` - Always "tree" for tree diffs
- `diff` - Array of TreeDiffItem operations (create, delete, move)

**TreeDiffItem Actions:**
- `create` - Node creation with parent and position
- `delete` - Node deletion with old parent and position
- `move` - Node movement with old and new positions

**Example:**
```ts threeslash
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");

// Example
tree.subscribe((e) => {
  for (const event of e.events) {
    if (event.diff.type === "tree") {
      event.diff.diff.forEach(item => {
        switch (item.action) {
          case "create":
            console.log(`Created node ${item.target}`);
            break;
          case "move":
            console.log(`Moved node ${item.target}`);
            break;
          case "delete":
            console.log(`Deleted node ${item.target}`);
            break;
        }
      });
    }
  }
});
```
</Indent>

### Deep Subscription

Subscribe to nested changes:

```ts threeslash
import { LoroDoc } from "loro-crdt";

const doc = new LoroDoc();
// Subscribe to specific container
const text = doc.getText("text");
text.subscribe((event) => {
  console.log("Text changed:", event);
});

// Subscribe with deep observation
doc.subscribe((event) => {
  // Path shows the location of the change
  event.events.forEach(e => {
    console.log("Change path:", e.path);
    console.log("Container:", e.target);
    console.log("Diff:", e.diff);
  });
});
```

---

## Undo/Redo

Local undo operates on your own changes without breaking collaboration. See [Undo/Redo](/docs/advanced/undo) for design details and caveats.

### UndoManager

Provides local undo/redo functionality.

**â ï¸ Important Notes:**
- **Local-only**: UndoManager only undoes the local user's operations, not remote operations
- **Origin filtering**: Use `excludeOriginPrefixes` to exclude certain operations (e.g., sync operations) from undo
- **Cursor restoration**: Use `onPush`/`onPop` callbacks to save and restore cursor positions

<Method id="UndoManager.constructor">
```typescript no_run
constructor(doc: LoroDoc, config: UndoConfig)
```
</Method>
<Indent>
Creates a new UndoManager instance.

**Parameters:**
- `doc` - The LoroDoc to manage undo/redo for
- `config` - Configuration options
  - `mergeInterval?` - Time in ms to merge consecutive operations (default: 1000)
  - `maxUndoSteps?` - Maximum number of undo steps to keep (default: 100)
  - `excludeOriginPrefixes?` - Array of origin prefixes to exclude from undo
  - `onPush?` - Callback when adding to undo stack
  - `onPop?` - Callback when undoing/redoing

**Example:**
```ts threeslash
import { LoroDoc, UndoManager } from "loro-crdt";

const doc = new LoroDoc();
const undo = new UndoManager(doc, {
  mergeInterval: 1000,
  maxUndoSteps: 100,
  excludeOriginPrefixes: ["sync-"]
});
```
</Indent>

<Method id="UndoManager.undo">
```typescript no_run
undo(): boolean
```
</Method>
<Indent>
Undo the last operation.

**Returns:** True if undo was successful

**Example:**
```ts threeslash
import { LoroDoc, UndoManager } from "loro-crdt";
const doc = new LoroDoc();
const undo = new UndoManager(doc, {});
const text = doc.getText("text");
text.insert(0, "Hello");
doc.commit();

// Usage example:
const success = undo.undo();
console.log(success); // true
```
</Indent>

<Method id="UndoManager.redo">
```typescript no_run
redo(): boolean
```
</Method>
<Indent>
Redo the last undone operation.

**Returns:** True if redo was successful

**Example:**
```ts threeslash
import { LoroDoc, UndoManager } from "loro-crdt";
const doc = new LoroDoc();
const undo = new UndoManager(doc, {});
const text = doc.getText("text");
text.insert(0, "Hello");
doc.commit();
undo.undo();

// Usage example:
const success = undo.redo();
console.log(success); // true
```
</Indent>

<Method id="UndoManager.canUndo">
```typescript no_run
canUndo(): boolean
```
</Method>
<Indent>
Check if undo is available.

**Returns:** True if can undo

**Example:**
```ts threeslash
import { LoroDoc, UndoManager } from "loro-crdt";
const doc = new LoroDoc();
const undo = new UndoManager(doc, {});

// Usage example:
if (undo.canUndo()) {
  undo.undo();
}
```
</Indent>

<Method id="UndoManager.canRedo">
```typescript no_run
canRedo(): boolean
```
</Method>
<Indent>
Check if redo is available.

**Returns:** True if can redo

**Example:**
```ts threeslash
import { LoroDoc, UndoManager } from "loro-crdt";
const doc = new LoroDoc();
const undo = new UndoManager(doc, {});

// Usage example:
if (undo.canRedo()) {
  undo.redo();
}
```
</Indent>

<Method id="UndoManager.peer">
```typescript no_run
peer(): PeerID
```
</Method>
<Indent>
Get the peer ID of the undo manager.

**Returns:** The peer ID

**Example:**
```ts threeslash
import { LoroDoc, UndoManager } from "loro-crdt";
const doc = new LoroDoc();
const undo = new UndoManager(doc, {});

// Usage example:
const peerId = undo.peer();
console.log(peerId); // e.g., "123456"
```
</Indent>

<Method id="UndoManager.setMaxUndoSteps">
```typescript no_run
setMaxUndoSteps(steps: number): void
```
</Method>
<Indent>
Set the maximum number of undo steps.

**Parameters:**
- `steps` - Maximum number of undo steps

**Example:**
```ts threeslash
import { LoroDoc, UndoManager } from "loro-crdt";
const doc = new LoroDoc();
const undo = new UndoManager(doc, {});

// Usage example:
undo.setMaxUndoSteps(50);
```
</Indent>

<Method id="UndoManager.setMergeInterval">
```typescript no_run
setMergeInterval(interval: number): void
```
</Method>
<Indent>
Set the merge interval for grouping operations.

**Parameters:**
- `interval` - Merge interval in milliseconds

**Example:**
```ts threeslash
import { LoroDoc, UndoManager } from "loro-crdt";
const doc = new LoroDoc();
const undo = new UndoManager(doc, {});

// Usage example:
undo.setMergeInterval(2000); // 2 seconds
```
</Indent>

<Method id="UndoManager.groupStart">
```typescript no_run
groupStart(): void
```
</Method>
<Indent>
Begin a manual grouping of subsequent commits into a single undo step.

**Behavior:**
- Wrap consecutive `doc.commit()` calls so they undo together
- Calling `groupStart` again before `groupEnd` throws and leaves the current group unchanged
- Conflicting remote imports may automatically end the group and split the undo item

**Example:**
```ts threeslash
import { LoroDoc, UndoManager } from "loro-crdt";

const doc = new LoroDoc();
const undo = new UndoManager(doc, {});
const text = doc.getText("text");

undo.groupStart();

text.update("hello", undefined);
doc.commit();

text.update("hello world", undefined);
doc.commit();

undo.groupEnd();

undo.undo();

console.log(text.toString()); // ""
```
</Indent>

<Method id="UndoManager.groupEnd">
```typescript no_run
groupEnd(): void
```
</Method>
<Indent>
Close the active manual group and enqueue the grouped operations as a single undo item.

**Behavior:**
- Must be paired with a prior `groupStart`
- Safe to call after the group was auto-closed by a conflicting remote import (becomes a no-op)
- Non-conflicting remote updates from other peers remain outside the undo item but do not break the group

**Example:**
```ts threeslash
import { LoroDoc, UndoManager } from "loro-crdt";

const doc = new LoroDoc();
const undo = new UndoManager(doc, {});
const text = doc.getText("text");

undo.groupStart();

text.update("hello", undefined);
doc.commit();
text.update("hello world", undefined);
doc.commit();

const snapshot = doc.export({ mode: "snapshot" });
const doc2 = new LoroDoc();
doc2.import(snapshot);
doc2.getText("text2").update("hello world world", undefined); // touches a different container
doc2.commit();
const update = doc2.export({ mode: "update" });

doc.import(update); // remote, non-conflicting change

text.update("hello world world world", undefined);
doc.commit();

undo.groupEnd();

undo.undo();

console.log(text.toString()); // ""
```
</Indent>

<Method id="UndoManager.addExcludeOriginPrefix">
```typescript no_run
addExcludeOriginPrefix(prefix: string): void
```
</Method>
<Indent>
Add a prefix to exclude from undo stack.

**Parameters:**
- `prefix` - Origin prefix to exclude

**Example:**
```ts threeslash
import { LoroDoc, UndoManager } from "loro-crdt";
const doc = new LoroDoc();
const undo = new UndoManager(doc, {});

// Usage example:
undo.addExcludeOriginPrefix("sync-");
undo.addExcludeOriginPrefix("import-");
```
</Indent>

<Method id="UndoManager.clear">
```typescript no_run
clear(): void
```
</Method>
<Indent>
Clear the undo and redo stacks.

**Example:**
```ts threeslash
import { LoroDoc, UndoManager } from "loro-crdt";
const doc = new LoroDoc();
const undo = new UndoManager(doc, {});

// Usage example:
undo.clear();
```
</Indent>

### Custom Undo Handlers

Handle cursor restoration and side effects:

```typescript no_run threeslash
import { LoroDoc, UndoManager } from "loro-crdt";
const doc = new LoroDoc();
declare function saveCursorPositions(): any;
declare function restoreCursorPositions(cursors: any): void;

// Usage example:
const undo = new UndoManager(doc, {
  onPush: (isUndo, counterRange, event) => {
    // Save cursor positions when adding to undo stack
    const cursors = saveCursorPositions();
    return {
      value: doc.toJSON(),
      cursors: cursors
    };
  },

  onPop: (isUndo, { value, cursors }, counterRange) => {
    // Restore cursor positions when undoing
    restoreCursorPositions(cursors);
  }
});
```

---

## Types & Interfaces

Reference for core types used across the API. For conceptual background, see [Containers](/docs/concepts/container), [Version Vector](/docs/concepts/version_vector), [Frontiers](/docs/concepts/frontiers), and the [Versioning Deep Dive](/docs/advanced/version_deep_dive).

### Core Types

```typescript no_run
// Peer identifier
type PeerID = `${number}`

// Container identifier
type ContainerID =
  | `cid:root-${string}:${ContainerType}`
  | `cid:${number}@${PeerID}:${ContainerType}`

// Tree node identifier
type TreeID = `${number}@${PeerID}`

// Operation identifier
type OpId = {
  peer: PeerID
  counter: number
}

// Container types
type ContainerType = "Text" | "Map" | "List" | "Tree" | "MovableList" | "Counter"

// Value types
type Value =
  | ContainerID
  | string
  | number
  | boolean
  | null
  | { [key: string]: Value }
  | Uint8Array
  | Value[]
  | undefined
```

### Version Types
Loro uses two complementary version representations: Version Vectors (per-peer counters) and Frontiers (a compact set of heads). See [Version Vector](/docs/concepts/version_vector) and [Frontiers](/docs/concepts/frontiers). For the full DAG model, see [Version Deep Dive](/docs/advanced/version_deep_dive).

```typescript no_run
// Version vector class
class VersionVector {
  constructor(value: Map<PeerID, number> | Uint8Array | VersionVector | undefined | null)
  static parseJSON(version: Map<PeerID, number>): VersionVector
  toJSON(): Map<PeerID, number>
  encode(): Uint8Array
  static decode(bytes: Uint8Array): VersionVector
  get(peer_id: number | bigint | `${number}`): number | undefined
  compare(other: VersionVector): number | undefined
  setEnd(id: { peer: PeerID, counter: number }): void
  setLast(id: { peer: PeerID, counter: number }): void
  remove(peer: PeerID): void
  length(): number
}

// Frontiers represent a specific version
type Frontiers = OpId[]

// ID span for range queries
type IdSpan = {
  peer: PeerID
  counter: number
  length: number
}
```

### Change Types

```typescript no_run
// Change metadata
interface Change {
  peer: PeerID
  counter: number
  lamport: number
  length: number
  timestamp: number  // Unix timestamp in seconds
  deps: OpId[]
  message: string | undefined
}

// Change modifier for pre-commit hooks
interface ChangeModifier {
  setMessage(message: string): this
  setTimestamp(timestamp: number): this
}
```

### Cursor Types
Stable cursors survive concurrent edits and resolve to absolute positions on demand. See [Cursor and Stable Positions](/docs/concepts/cursor_stable_positions) and [Cursor tutorial](/docs/tutorial/cursor).

```typescript no_run
// Stable position in containers
class Cursor {
  containerId(): ContainerID
  pos(): OpId | undefined
  side(): Side  // -1 | 0 | 1
  encode(): Uint8Array
  static decode(data: Uint8Array): Cursor
}

// Cursor side affinity
type Side = -1 | 0 | 1
```

### Delta Type
Delta is a popular rich-text operation format (e.g., Quill). LoroText can export/import Delta; see [Text](/docs/tutorial/text).

```typescript no_run
// Rich text delta operations
type Delta<T> =
  | {
      insert: T
      attributes?: { [key in string]: {} }
      retain?: undefined
      delete?: undefined
    }
  | {
      delete: number
      attributes?: undefined
      retain?: undefined
      insert?: undefined
    }
  | {
      retain: number
      attributes?: { [key in string]: {} }
      delete?: undefined
      insert?: undefined
    }
```

---

## Utility Functions

Small helpers for type checks and IDs. See [Container IDs](/docs/advanced/cid) and [Versioning Deep Dive](/docs/advanced/version_deep_dive) for frontiers/version encoding.

### Frontier Encoding

<Method id="encodeFrontiers">
```typescript no_run
encodeFrontiers(frontiers: OpId[]): Uint8Array
```
</Method>
<Indent>
Encode frontiers for efficient transmission.

**Parameters:**
- `frontiers` - Array of operation IDs representing frontiers

**Returns:** Encoded bytes

**Example:**
```ts threeslash
import { LoroDoc, encodeFrontiers } from "loro-crdt";

const doc = new LoroDoc();
const frontiers = doc.frontiers();
const encoded = encodeFrontiers(frontiers);
// Send encoded to remote peers
```
</Indent>

<Method id="decodeFrontiers">
```typescript no_run
decodeFrontiers(bytes: Uint8Array): OpId[]
```
</Method>
<Indent>
Decode frontiers from bytes.

**Parameters:**
- `bytes` - Encoded frontier bytes

**Returns:** Array of operation IDs

**Example:**
```typescript no_run threeslash
import { decodeFrontiers } from "loro-crdt";

declare const encodedData: Uint8Array;
const frontiers = decodeFrontiers(encodedData);
console.log(frontiers); // [{ peer: "1", counter: 10 }, ...]
```
</Indent>

### Debugging

<Method id="setDebug">
```typescript no_run
setDebug(): void
```
</Method>
<Indent>
Enable debug mode for detailed logging.

**Example:**
```ts threeslash
import { setDebug } from "loro-crdt";

// Enable debug logging
setDebug();
```
</Indent>

<Method id="LORO_VERSION">
```typescript no_run
LORO_VERSION(): string
```
</Method>
<Indent>
Get the current Loro version.

**Returns:** Version string

**Example:**
```ts threeslash
import { LORO_VERSION } from "loro-crdt";

const version = LORO_VERSION();
console.log("Loro version:", version);
```
</Indent>

### Import Blob Metadata

<Method id="decodeImportBlobMeta">
```typescript no_run
decodeImportBlobMeta(blob: Uint8Array, check_checksum: boolean): ImportBlobMetadata
```
</Method>
<Indent>
Decode metadata from an import blob.

**Parameters:**
- `blob` - The import blob bytes
- `check_checksum` - Whether to verify checksum

**Returns:** Import blob metadata

**Example:**
```typescript no_run threeslash
import { decodeImportBlobMeta } from "loro-crdt";

declare const blob: Uint8Array;
const metadata = decodeImportBlobMeta(blob, true);
console.log("Blob metadata:", metadata);
```
</Indent>

---

## EphemeralStore

Manages ephemeral state like cursor positions and user presence. See [Ephemeral Store](/docs/tutorial/ephemeral) for concepts and usage patterns. Each entry uses timestamp-based LWW (Last-Write-Wins) for conflict resolution.

**â ï¸ Important:**
- EphemeralStore is a separate CRDT without history - history/operations are NOT persisted
- Perfect for temporary state: cursor positions, selections, typing indicators
- Each peer's state auto-expires after the timeout period
- Uses Last-Write-Wins

### EphemeralStore

<Method id="EphemeralStore.constructor">
```typescript no_run
constructor(timeout?: number)
```
</Method>
<Indent>
Creates a new EphemeralStore instance.

**Parameters:**
- `timeout` - Duration in milliseconds. A peer's state is considered outdated if its last update is older than this timeout. Default is 30000ms (30 seconds).

**Example:**
```ts threeslash
import { EphemeralStore } from "loro-crdt";

// Create ephemeral store with 30 second timeout
const store = new EphemeralStore(30000);
```
</Indent>

<Method id="EphemeralStore.set">
```typescript no_run
set<K extends keyof T>(key: K, value: T[K]): void
```
</Method>
<Indent>
Set an ephemeral value.

**Parameters:**
- `key` - The key to set
- `value` - The value to store

**Example:**
```ts threeslash
import { EphemeralStore } from "loro-crdt";
const store = new EphemeralStore(30000);

// Usage example:
store.set("cursor", { line: 10, column: 5 });
store.set("selection", { start: 0, end: 10 });
store.set("user", { name: "Alice", color: "#ff0000" });
```
</Indent>

<Method id="EphemeralStore.get">
```typescript no_run
get<K extends keyof T>(key: K): T[K] | undefined
```
</Method>
<Indent>
Get an ephemeral value.

**Parameters:**
- `key` - The key to get

**Returns:** The stored value or undefined

**Example:**
```ts threeslash
import { EphemeralStore } from "loro-crdt";
const store = new EphemeralStore(30000);
store.set("cursor", { line: 10 });

// Usage example:
const cursor = store.get("cursor");
console.log(cursor); // { line: 10 }
```
</Indent>

<Method id="EphemeralStore.delete">
```typescript no_run
delete<K extends keyof T>(key: K): void
```
</Method>
<Indent>
Delete an ephemeral value.

**Parameters:**
- `key` - The key to delete

**Example:**
```ts threeslash
import { EphemeralStore } from "loro-crdt";
const store = new EphemeralStore(30000);
store.set("cursor", { line: 10 });

// Usage example:
store.delete("cursor");
```
</Indent>

<Method id="EphemeralStore.getAllStates">
```typescript no_run
getAllStates(): Partial<T>
```
</Method>
<Indent>
Get all ephemeral states.

**Returns:** All stored states

**Example:**
```ts threeslash
import { EphemeralStore } from "loro-crdt";
const store = new EphemeralStore(30000);
store.set("cursor", { line: 10 });
store.set("user", { name: "Alice" });

// Usage example:
const allStates = store.getAllStates();
console.log(allStates);
```
</Indent>

<Method id="EphemeralStore.encode">
```typescript no_run
encode<K extends keyof T>(key: K): Uint8Array
```
</Method>
<Indent>
Encode a specific key's state for transmission.

**Parameters:**
- `key` - The key to encode

**Returns:** Encoded bytes

**Example:**
```ts threeslash
import { EphemeralStore } from "loro-crdt";
const store = new EphemeralStore(30000);
store.set("cursor", { line: 10 });

// Usage example:
const encoded = store.encode("cursor");
// Send encoded to remote peers
```
</Indent>

<Method id="EphemeralStore.encodeAll">
```typescript no_run
encodeAll(): Uint8Array
```
</Method>
<Indent>
Encode all states for transmission.

**Returns:** Encoded bytes

**Example:**
```ts threeslash
import { EphemeralStore } from "loro-crdt";
const store = new EphemeralStore(30000);
store.set("cursor", { line: 10 });
store.set("user", { name: "Alice" });

// Usage example:
const encoded = store.encodeAll();
// Send encoded to remote peers
```
</Indent>

<Method id="EphemeralStore.apply">
```typescript no_run
apply(bytes: Uint8Array): void
```
</Method>
<Indent>
Apply remote updates.

**Parameters:**
- `bytes` - Encoded updates from remote peer

**Example:**
```typescript no_run threeslash
import { EphemeralStore } from "loro-crdt";
const store = new EphemeralStore(30000);

// Usage example:
declare const remoteData: Uint8Array;
store.apply(remoteData);
```
</Indent>

<Method id="EphemeralStore.keys">
```typescript no_run
keys(): string[]
```
</Method>
<Indent>
Get all keys in the store.

**Returns:** Array of keys

**Example:**
```ts threeslash
import { EphemeralStore } from "loro-crdt";
const store = new EphemeralStore(30000);
store.set("cursor", { line: 10 });
store.set("user", { name: "Alice" });

// Usage example:
const allKeys = store.keys();
console.log(allKeys); // ["cursor", "user"]
```
</Indent>

<Method id="EphemeralStore.subscribe">
```typescript no_run
subscribe(listener: EphemeralListener): () => void
```
</Method>
<Indent>
Subscribe to all ephemeral state changes.

**Parameters:**
- `listener` - Callback function for state changes

**Returns:** Unsubscribe function

**Example:**
```ts threeslash
import { EphemeralStore } from "loro-crdt";
const store = new EphemeralStore(30000);

// Usage example:
const unsubscribe = store.subscribe((event) => {
  console.log("Ephemeral state changed:", event);
});

// Later, unsubscribe
unsubscribe();
```
</Indent>

<Method id="EphemeralStore.subscribeLocalUpdates">
```typescript no_run
subscribeLocalUpdates(listener: EphemeralLocalListener): () => void
```
</Method>
<Indent>
Subscribe to local ephemeral updates for syncing to remote peers.

**Parameters:**
- `listener` - Callback function that receives encoded updates

**Returns:** Unsubscribe function

**Example:**
```typescript no_run threeslash
import { EphemeralStore } from "loro-crdt";
const store = new EphemeralStore(30000);

// Usage example:
declare const websocket: { send: (data: Uint8Array) => void };

const unsubscribe = store.subscribeLocalUpdates((data) => {
  // Send to remote peers
  websocket.send(data);
});

// Later, unsubscribe
unsubscribe();
```
</Indent>

<Method id="EphemeralStore.destroy">
```typescript no_run
destroy(): void
```
</Method>
<Indent>
Clean up and destroy the ephemeral store.

**Example:**
```ts threeslash
import { EphemeralStore } from "loro-crdt";
const store = new EphemeralStore(30000);

// Usage example:
store.destroy();
```
</Indent>

### Complete Example

```typescript no_run threeslash
import { EphemeralStore } from "loro-crdt";

// Assume we have:
declare const websocket: {
  send: (data: Uint8Array) => void;
  on: (event: string, handler: (data: any) => void) => void;
};

const store = new EphemeralStore(30000);
const store2 = new EphemeralStore(30000);

// Subscribe to local updates
store.subscribeLocalUpdates((data) => {
  store2.apply(data);
});

// Subscribe to all updates
store2.subscribe((event) => {
  console.log("event:", event);
});

// Set a value
store.set("key", "value");

// Encode the value
const encoded = store.encode("key");

// Apply the encoded value
store2.apply(encoded);
```

---

</div>