### Example Version Vectors
Source: https://loro.dev/docs/advanced/version_deep_dive
Illustrates example Version Vectors A and B, showing operation counts for different peers.
```javascript
const A = {
0: 2,
1: 3,
};
```
```javascript
const B = {
0: 5,
1: 3,
2: 9,
};
```
--------------------------------
### Parallel Container Initialization and Overwrite Example
Source: https://loro.dev/docs/advanced/cid
Demonstrates a scenario where child containers are initialized in parallel, leading to potential overwrites and data loss if not handled carefully. This example highlights the risk associated with concurrent container creation.
```typescript
const a: string = "hello";
const doc = new LoroDoc();
const map = doc.getMap("map");
// Parallel initialization of child containers
const docB = doc.fork();
const textA = doc.getMap("map").setContainer("text", new LoroText());
textA.insert(0, "A");
const textB = docB.getMap("map").setContainer("text", new LoroText());
textB.insert(0, "B");
doc.import(docB.export({ mode: "update" }));
// Result: Either { "meta": { "text": "A" } } or { "meta": { "text": "B" } }
```
--------------------------------
### Undo/Redo with Cursor Restoration Example
Source: https://loro.dev/docs/advanced/undo
Demonstrates how to use the UndoManager to store and restore cursor positions during undo/redo operations. This example shows cursor transformation after text deletion and verifies cursor positions after an undo action.
```typescript
import { LoroDoc, UndoManager, Cursor } from "loro-crdt";
import { expect } from "expect";
// ---cut---
const doc = new LoroDoc();
let cursors: Cursor[] = [];
let poppedCursors: Cursor[] = [];
const undo = new UndoManager(doc, {
mergeInterval: 0,
onPop: (isUndo, value, counterRange) => {
poppedCursors = value.cursors;
},
onPush: () => {
return { value: null, cursors: cursors };
},
});
doc.getText("text").insert(0, "hello world");
doc.commit();
cursors = [
doc.getText("text").getCursor(0)ீர்கள்!,
doc.getText("text").getCursor(5)ீர்கள்!,
];
// delete "hello ", the cursors should be transformed
doc.getText("text").delete(0, 6);
doc.commit();
expect(poppedCursors.length).toBe(0);
undo.undo();
expect(poppedCursors.length).toBe(2);
expect(doc.toJSON()).toStrictEqual({
text: "hello world",
});
// expect the cursors to be transformed back
expect(doc.getCursorPos(poppedCursors[0]).offset).toBe(0);
expect(doc.getCursorPos(poppedCursors[1]).offset).toBe(5);
```
--------------------------------
### Install loro-crdt with npm, pnpm, or yarn
Source: https://loro.dev/docs/tutorial/get_started
Choose your preferred package manager to install the loro-crdt NPM package.
```bash
npm install loro-crdt
# Or
pnpm install loro-crdt
# Or
yarn add loro-crdt
```
--------------------------------
### Get Version Vector
Source: https://loro.dev/docs/tutorial/loro_doc
Retrieve the current version vector of the document. Also shows how to get the oplog version vector.
```javascript
const doc = new LoroDoc();
// Get current version vector
const vv = doc.version();
// Get oplog version vector (latest known version)
const oplogVv = doc.oplogVersion();
```
--------------------------------
### Using Map Container
Source: https://loro.dev/docs/tutorial/loro_doc
Illustrates setting and getting key-value pairs in a map, including nested containers.
```javascript
const doc = new LoroDoc();
const map = doc.getMap("map");
map.set("name", "John");
map.set("age", 30);
console.log(map.get("name")); // "John"
// Nested containers
const userText = map.setContainer("bio", new LoroText());
userText.insert(0, "Software Engineer");
```
--------------------------------
### Get or Create LoroTree Container
Source: https://loro.dev/docs/api/js
Use `getTree` to get or create a tree container. This is ideal for representing hierarchical data structures. Learn about hierarchical editing and moves in Tree.
```javascript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("fileSystem");
const root = tree.createNode();
```
--------------------------------
### Get or Create Container
Source: https://loro.dev/docs/api/js
Gets an existing container or creates a new regular child container. Prefer `ensureMergeable*` methods for concurrently created shared children.
```javascript
import { LoroDoc, LoroText } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");
const text = map.getOrCreateContainer("description", new LoroText());
```
--------------------------------
### Get or Create LoroMap Container
Source: https://loro.dev/docs/api/js
Use `getMap` to get or create a map container. This is suitable for key-value data structures. Refer to Map for basics and patterns.
```javascript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("settings");
map.set("theme", "dark");
```
--------------------------------
### Get or Create LoroText Container
Source: https://loro.dev/docs/api/js
Use `getText` to get or create a text container. This is suitable for collaborative text editing. See Text and Marks for more details.
```javascript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("content");
text.insert(0, "Hello");
```
--------------------------------
### Get Root Nodes of LoroTree
Source: https://loro.dev/docs/api/js
Retrieves all root nodes from a LoroTree. This is useful for starting traversal or understanding the top-level structure of the tree.
```javascript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");
const rootNodes = tree.roots();
```
--------------------------------
### Prepare Sample Data for JSONPath Queries
Source: https://loro.dev/docs/advanced/jsonpath
This snippet sets up a Loro document with sample bookstore data using JavaScript objects. It demonstrates how to initialize a LoroDoc, populate a map with test data, and commit the changes. Prefer explicit LoroMap and LoroList composition in production.
```javascript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const testData = {
books: [
{ title: "1984", author: "George Orwell", price: 10, available: true, isbn: "978-0451524935" },
{ title: "Animal Farm", author: "George Orwell", price: 8, available: true },
{ title: "Brave New World", author: "Aldous Huxley", price: 12, available: false },
{ title: "Fahrenheit 451", author: "Ray Bradbury", price: 9, available: true, isbn: "978-1451673318" },
{ title: "The Great Gatsby", author: "F. Scott Fitzgerald", price: null, available: true },
{ title: "To Kill a Mockingbird", author: "Harper Lee", price: 11, available: true },
{ title: "The Catcher in the Rye", author: "J.D. Salinger", price: 10, available: false },
{ title: "Lord of the Flies", author: "William Golding", price: 9, available: true },
{ title: "Pride and Prejudice", author: "Jane Austen", price: 7, available: true },
{ title: "The Hobbit", author: "J.R.R. Tolkien", price: 14, available: true }
],
featured_author: "George Orwell",
min_price: 10,
featured_authors: ["George Orwell", "Jane Austen"],
};
const store = doc.getMap("store");
Object.entries(testData).forEach(([key, value]) => store.set(key, value));
doc.commit();
```
--------------------------------
### Get or Create LoroList Container
Source: https://loro.dev/docs/api/js
Use `getList` to get or create a list container. This is suitable for ordered collections where elements might be added or removed. Consult List and Movable List and the type selection guide Choosing CRDT Types for guidance.
```javascript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const list = doc.getList("items");
list.push("Item 1");
```
--------------------------------
### Minimal Browser Example using ESM Imports
Source: https://loro.dev/docs/tutorial/get_started
This HTML snippet demonstrates how to use Loro directly in the browser via ESM imports from a CDN.
```html
ESM Module Example
```
--------------------------------
### Get Loro Version
Source: https://loro.dev/docs/api/js
Retrieves the current version string of the Loro CRDT library. Use this to check the installed version or for compatibility checks.
```javascript
import { LORO_VERSION } from "loro-crdt";
const version = LORO_VERSION();
console.log("Loro version:", version);
```
--------------------------------
### Counter System Example
Source: https://loro.dev/docs/concepts/peerid_management
Demonstrates how the monotonic counter works per peer. Each operation increments the counter for the assigned Peer ID.
```javascript
const doc = new LoroDoc();
doc.setPeerId("1");
const text = doc.getText("text");
text.insert(0, "H"); // Operation ("1", 0)
text.insert(1, "i!"); // Operation ("1", 1) and Operation ("1", 2) are created
console.log(doc.version()); // { "1": 2 }
```
--------------------------------
### Track Cursor Position Across Edits
Source: https://loro.dev/docs/tutorial/loro_doc
Demonstrates how to get and track cursor positions in a Loro document's text. Ensure the cursor is obtained after initial text setup.
```typescript
import { LoroDoc } from "loro-crdt";
// ---cut---
const doc = new LoroDoc();
const text = doc.getText("text");
text.insert(0, "123");
// Get cursor at position with side (-1, 0, or 1)
const cursor = text.getCursor(0, 0);
if (cursor) {
// Get current cursor position
const pos = doc.getCursorPos(cursor);
console.log(pos.offset); // Current position
console.log(pos.side); // Cursor side
// Cursor position updates automatically with concurrent edits
text.insert(0, "abc");
const newPos = doc.getCursorPos(cursor);
console.log(newPos.offset); // Position updated
}
```
--------------------------------
### Workflow Example: Squash-like Diffs
Source: https://loro.dev/docs/api/js
Demonstrates using diff and applyDiff to send a compact change set between a base version and a new version, compressing out canceling operations.
```javascript
import { LoroDoc } from "loro-crdt";
const baseDoc = new LoroDoc();
const baseText = baseDoc.getText("text");
baseText.insert(0, "hello world");
// Fork to make isolated edits
const newDoc = baseDoc.fork();
const newText = newDoc.getText("text");
newText.insert(0, "abc");
newText.delete(0, 4);
const diff = newDoc.diff(baseDoc.frontiers(), newDoc.frontiers());
console.log(diff);
// [
// [
// "cid:root-text:Text",
// { type: "text", diff: [ { delete: 1 } ] }
// ]
// ]
baseDoc.applyDiff(diff);
console.log(baseDoc.toJSON());
// { text: "ello world" }
```
--------------------------------
### get
Source: https://loro.dev/docs/api/js
Gets the value for a key from the map. Returns undefined if the key does not exist.
```APIDOC
## get(key: string): Value | Container | undefined
### Description
Gets the value for a key.
### Parameters
#### Path Parameters
* **key** (string) - Required - The key
### Returns
The value or undefined
### Example
```javascript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");
const name = map.get("name");
```
```
--------------------------------
### Defining Text Selection Ranges
Source: https://loro.dev/docs/concepts/cursor_stable_positions
Shows how to define the start and end cursors for a text selection range.
```javascript
const doc = new LoroDoc();
const text = doc.getText("text");
text.insert(0, "Hello World");
// Selection range
const start = text.getCursor(0, -1); // Anchor
const end = text.getCursor(5, 1); // Head
```
--------------------------------
### Practical Example: Saving and Restoring Checkpoints
Source: https://loro.dev/docs/concepts/frontiers
Illustrates saving document states using frontiers as checkpoints and restoring to these saved states with LoroDoc.
```typescript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("content");
const checkpoints = new Map();
// Save checkpoint with frontiers
text.insert(0, "Draft version");
checkpoints.set("draft", doc.frontiers());
// Make changes
text.delete(0, 5);
text.insert(0, "Final");
checkpoints.set("final", doc.frontiers());
// Restore to any checkpoint
doc.checkout(checkpoints.get("draft"));
console.log(text.toString()); // "Draft version"
```
--------------------------------
### Using Tree Container
Source: https://loro.dev/docs/tutorial/loro_doc
Demonstrates creating a tree structure with nodes and setting data for each node.
```javascript
const doc = new LoroDoc();
const tree = doc.getTree("tree");
const root = tree.createNode();
root.data.set("name", "Root");
const child1 = root.createNode();
child1.data.set("name", "Child 1");
const child2 = root.createNode();
child2.data.set("name", "Child 2");
```
--------------------------------
### Basic Document Operations and Versioning
Source: https://loro.dev/docs/concepts/oplog_docstate
Demonstrates basic text insertion and how to check the OpLog version versus the current document state version. Assumes the document is attached, meaning the state and log versions are synchronized.
```typescript
const doc = new LoroDoc();
// Edit updates both
doc.getText("text").insert(0, "Hello");
console.log(doc.oplogVersion()); // Latest known version
console.log(doc.version()); // The version of the current state of the document
// If the document is attached, they are the same.
```
--------------------------------
### Get All Ephemeral States
Source: https://loro.dev/docs/api/js
Retrieves all currently stored ephemeral states as a partial object. This is useful for getting a snapshot of all transient data.
```typescript
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);
```
--------------------------------
### Using List Container
Source: https://loro.dev/docs/tutorial/loro_doc
Shows how to insert items into a list and add nested containers like text.
```javascript
const doc = new LoroDoc();
const list = doc.getList("list");
list.insert(0, "first");
list.insert(1, "second");
console.log(list.toArray()); // ["first", "second"]
// Nested containers
const nestedText = list.insertContainer(2, new LoroText());
nestedText.insert(0, "nested text");
```
--------------------------------
### Get Frontiers
Source: https://loro.dev/docs/tutorial/loro_doc
Obtain the current frontiers of the document, which represent the latest operations from each peer. Also shows how to get oplog frontiers.
```javascript
const doc = new LoroDoc();
doc.setPeerId("0");
doc.getMap("map").set("text", "Hello");
// Get current frontiers
const frontiers = doc.frontiers();
// Get oplog frontiers (latest known version)
const oplogFrontiers = doc.oplogFrontiers(); // { "0": 0 }
```
--------------------------------
### Using Text Container
Source: https://loro.dev/docs/tutorial/loro_doc
Demonstrates inserting text and applying rich text formatting like bold and links.
```javascript
const doc = new LoroDoc();
const text = doc.getText("text");
text.insert(0, "Hello");
text.insert(5, " World!");
console.log(text.toString()); // "Hello World!"
// Rich text support
doc.configTextStyle({
bold: { expand: "after" },
link: { expand: "none" },
});
text.mark({ start: 0, end: 5 }, "bold", true);
```
--------------------------------
### EphemeralStore Synchronization Example
Source: https://loro.dev/docs/api/js
This snippet shows how to initialize two EphemeralStore instances, subscribe to local updates from one store and apply them to the other, and subscribe to all updates on the second store. It also demonstrates setting a value, encoding it, and applying the encoded data.
```typescript
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);
```
--------------------------------
### LoroList Methods
Source: https://loro.dev/docs/api/js
Provides documentation for various methods of the LoroList CRDT, including getting a cursor, converting to an array, clearing the list, accessing its length, checking its kind, converting to JSON, retrieving the parent container, checking attachment status, getting the attached version, checking deletion status, and getting shallow values.
```APIDOC
## getCursor(index: number)
### Description
Retrieves a cursor object at the specified index in the list.
### Parameters
#### Path Parameters
- **index** (number) - Required - The index at which to get the cursor.
### Returns
Cursor object or undefined
### Example
```javascript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const list = doc.getList("list");
list.push("a", "b", "c");
const cursor = list.getCursor(2);
```
```
```APIDOC
## toArray(): (Value | Container)[]
### Description
Converts the list to a JavaScript array.
### Returns
Array of values
### Example
```javascript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const list = doc.getList("list");
list.push("a", "b", "c");
const array = list.toArray();
```
```
```APIDOC
## clear(): void
### Description
Removes all elements from the list.
### Example
```javascript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const list = doc.getList("list");
list.push("a", "b", "c");
list.clear();
```
```
```APIDOC
## length: number
### Description
Gets the number of elements in the list.
### Returns
List length
### Example
```javascript
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`);
```
```
```APIDOC
## kind(): "List"
### Description
Returns the container type.
### Returns
“List”
### Example
```javascript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const list = doc.getList("list");
const type = list.kind(); // "List"
```
```
```APIDOC
## toJSON(): any
### Description
Converts the list to JSON representation.
### Returns
JSON array
### Example
```javascript
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]
```
```
```APIDOC
## parent(): Container | undefined
### Description
Gets the parent container if this list is nested.
### Returns
Parent container or undefined
### Example
```javascript
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
```
```
```APIDOC
## isAttached(): boolean
### Description
Checks if the container is attached to a document.
### Returns
True if attached
### Example
```javascript
import { LoroDoc, LoroList } from "loro-crdt";
const doc = new LoroDoc();
const list = new LoroList();
const attached = list.isAttached(); // false until attached to doc
```
```
```APIDOC
## getAttached(): LoroList | undefined
### Description
Gets the attached version of this container.
### Returns
Attached container or undefined
### Example
```javascript
import { LoroDoc, LoroList } from "loro-crdt";
const doc = new LoroDoc();
const list = new LoroList();
const attached = list.getAttached();
```
```
```APIDOC
## isDeleted(): boolean
### Description
Checks if the container has been deleted.
### Returns
True if deleted
### Example
```javascript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const list = doc.getList("list");
const deleted = list.isDeleted();
```
```
```APIDOC
## getShallowValue(): Value[]
### Description
Gets the list values with sub-containers as IDs.
### Returns
Array of values
### Example
```javascript
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]
```
```
```APIDOC
## readonly id: ContainerID
### Description
Gets the unique container ID.
### Example
```javascript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const list = doc.getList("list");
const containerId = list.id;
```
```
--------------------------------
### Using the Counter in LoroDoc
Source: https://loro.dev/docs/tutorial/counter
Demonstrates how to initialize a Counter, apply changes, and check its final value. Ensure LoroDoc is imported and initialized before use.
```javascript
const doc = new LoroDoc();
const counter = doc.getCounter("counter");
counter.increment(1);
counter.increment(2);
counter.decrement(1);
expect(counter.value).toBe(2);
```
--------------------------------
### Get or Create LoroCounter Container
Source: https://loro.dev/docs/api/js
Use `getCounter` to get or create a counter container. Counters are specialized CRDTs for summing concurrent increments. See Counter for more information.
```javascript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const counter = doc.getCounter("likes");
counter.increment(1);
```
--------------------------------
### Shallow Snapshots
Source: https://loro.dev/docs/api/js
Demonstrates creating and importing shallow snapshots. This mode is efficient for storage as it strips history, suitable for state synchronization where history is not needed.
```javascript
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);
```
--------------------------------
### Initialize UndoManager with Options
Source: https://loro.dev/docs/advanced/undo
Instantiate an UndoManager, configuring options like maximum undo steps, merge interval, excluded operation prefixes, and callback functions for push and pop events.
```javascript
const undoManager = new UndoManager(doc, {
maxUndoSteps: 100, // default 100
mergeInterval: 1000, // default 1000ms
excludeOriginPrefixes: ["sys:"], // default []
onPush: (isUndo, range, event) => {
return { value: null, cursors: [] };
},
onPop: (isUndo, value, counterRange) => {
return;
},
});
```
--------------------------------
### slice(start: number, end: number): string
Source: https://loro.dev/docs/api/js
Extracts a section of the text using UTF-16 code unit positions. It takes a start and end index and returns the corresponding substring.
```APIDOC
## slice(start: number, end: number): string
### Description
Extracts a section of the text using UTF-16 code unit positions.
### Parameters
#### Path Parameters
- **start** (number) - Required - Start UTF-16 code unit index
- **end** (number) - Required - End UTF-16 code unit index (exclusive)
### Returns
Sliced text (string)
### Request Example
```javascript
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"
```
```
--------------------------------
### nodes
Source: https://loro.dev/docs/api/js
Gets all nodes in the LoroTree.
```APIDOC
## nodes
### Description
Gets all nodes in the tree.
### Returns
Array of all nodes
### Example
```javascript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");
const allNodes = tree.nodes();
```
```
--------------------------------
### Comparing Batch Import vs. Individual Import
Source: https://loro.dev/docs/advanced/import_batch
Demonstrates the difference in performance between importing multiple updates using `importBatch` and importing them individually in a loop. Use `importBatch` for significantly faster updates.
```javascript
const doc = new LoroDoc();
doc.getText("text").update("Hello");
const update1 = doc.export({ mode: "update" });
const version = doc.version();
doc.getText("text").update("Hello World");
const update2 = doc.export({ mode: "update", from: version });
const newDoc1 = new LoroDoc();
newDoc1.importBatch([update1, update2]); // faster
const newDoc2 = new LoroDoc();
for (const update of [update1, update2]) { // slower
newDoc2.import(update);
}
```
--------------------------------
### Basic Document Operations
Source: https://loro.dev/docs/advanced/doc_state_and_oplog
Demonstrates basic document creation and editing. Use this to understand how edits update both the OpLog and the current document state. `oplogVersion()` returns the latest known version, while `version()` returns the version of the current state.
```typescript
const doc = new LoroDoc();
// Edit updates both
doc.getText("text").insert(0, "Hello");
console.log(doc.oplogVersion()); // Latest known version
console.log(doc.version()); // The version of the current state of the document
// If the document is attached, they are the same.
```
--------------------------------
### Get or Create LoroMovableList Container
Source: https://loro.dev/docs/api/js
Use `getMovableList` to get or create a movable list container. This type is optimized for scenarios involving concurrent reordering of elements. See List and Movable List and Choosing CRDT Types.
```javascript
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
```
--------------------------------
### Create and Set Node Data
Source: https://loro.dev/docs/api/js
Demonstrates how to create a new tree node and set its metadata using a LoroMap. This is useful for storing custom information associated with each node.
```javascript
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);
```
--------------------------------
### Example: Successful Import Operations
Source: https://loro.dev/docs/tutorial/sync
Logs the 'success' field of the import result to show which operation ranges from each peer were applied.
```javascript
// Assuming importResult is the return value of doc.import(bytes)
console.log(importResult.success);
// Example output:
// {
// "clientA_peerId": { "start": 0, "end": 50 },
// "server_peerId": { "start": 120, "end": 150 }
// }
// This means operations from clientA (counters 0-49) and
// operations from server (counters 120-149) were successfully imported.
```
--------------------------------
### roots
Source: https://loro.dev/docs/api/js
Gets all root nodes in the LoroTree.
```APIDOC
## roots
### Description
Gets all root nodes in the tree.
### Returns
Array of root nodes
### Example
```javascript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const tree = doc.getTree("tree");
const rootNodes = tree.roots();
```
```
--------------------------------
### Basic Synchronization
Source: https://loro.dev/docs/api/js
Demonstrates exporting updates from one document and importing them into another. This is useful for one-time sync operations between peers.
```javascript
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"
```
--------------------------------
### Example: Pending Import Operations Due to Dependencies
Source: https://loro.dev/docs/tutorial/sync
Checks if the 'pending' field exists and logs it, indicating operation ranges that are waiting for prerequisite updates.
```javascript
// Assuming importResult is the return value of doc.import(bytes)
if (importResult.pending) {
console.log(importResult.pending);
// Example output:
// {
// "clientA_peerId": { "start": 50, "end": 60 },
// "clientB_peerId": { "start": 10, "end": 25 }
// }
// This means operations from clientA (counters 50-59) and
// operations from clientB (counters 10-24) are pending due to missing dependencies.
```
--------------------------------
### id
Source: https://loro.dev/docs/api/js
Gets the unique container ID.
```APIDOC
## id
### Description
Gets the unique container ID.
### Example
```javascript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");
const containerId = map.id;
```
```
--------------------------------
### getAttached
Source: https://loro.dev/docs/api/js
Gets the attached version of this container.
```APIDOC
## getAttached
### Description
Gets the attached version of this container.
### Returns
Attached container or undefined
### Example
```javascript
import { LoroDoc, LoroMap } from "loro-crdt";
const doc = new LoroDoc();
const map = new LoroMap();
const attached = map.getAttached();
```
```
--------------------------------
### Basic LoroDoc Usage
Source: https://loro.dev/docs/api/js
Demonstrates basic LoroDoc initialization, text manipulation, subscribing to changes, and exporting updates for synchronization.
```typescript
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" });
```
--------------------------------
### values
Source: https://loro.dev/docs/api/js
Gets all values present in the map.
```APIDOC
## values(): (Value | Container)[]
### Description
Gets all values in the map.
### Returns
Array of values
### Example
```javascript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");
const allValues = map.values();
```
```
--------------------------------
### Loro API Usage with Eg-Walker Inspired Optimizations
Source: https://loro.dev/docs/advanced/event_graph_walker
Demonstrates the simple, index-based API for text manipulation in Loro, leveraging Eg-Walker-inspired efficiency for operations and merging.
```javascript
// Simple API with powerful internals
const doc = new Loro();
const text = doc.getText("content");
// Just use indices - Eg-Walker handles the complexity
text.insert(5, "Hello");
text.delete(2, 3);
// Efficient merging happens automatically
doc.import(remoteUpdate);
```
--------------------------------
### keys
Source: https://loro.dev/docs/api/js
Gets all keys present in the map.
```APIDOC
## keys(): string[]
### Description
Gets all keys in the map.
### Returns
Array of keys
### Example
```javascript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");
const allKeys = map.keys();
```
```
--------------------------------
### groupStart()
Source: https://loro.dev/docs/api/js
Begins a manual grouping of subsequent operations, allowing them to be undone as a single unit.
```APIDOC
## groupStart(): void
### Description
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
```javascript
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()); // ""
```
```
--------------------------------
### Basic List Usage with Concurrent Updates
Source: https://loro.dev/docs/tutorial/list
Demonstrates basic List operations like push, delete, and insert. Shows how concurrent updates to the same index are handled and merged.
```typescript
const docA = new LoroDoc();
docA.setPeerId("1");
const listA = docA.getList("list");
listA.push(0);
listA.push(1);
listA.push(2);
const bytes: Uint8Array = docA.export({ mode: "snapshot" });
const docB = Loro.fromSnapshot(bytes);
docB.setPeerId("2");
const listB = docB.getList("list");
{
// Concurrently docA and docB update element at index 2
// docA updates it to 8
// docB updates it to 9
// docA.toJSON() should return { list: [0, 1, 8] }
// docB.toJSON() should return { list: [0, 1, 9] }
listB.delete(2, 1);
listB.insert(2, 9);
expect(docB.toJSON()).toStrictEqual({ list: [0, 1, 9] });
listA.delete(2, 1);
listA.insert(2, 8);
expect(docA.toJSON()).toStrictEqual({ list: [0, 1, 8] });
}
{
// Merge docA and docB
docA.import(docB.export({ mode: "update", from: docA.version() }));
docB.import(docA.export({ mode: "update", from: docB.version() }));
}
expect(docA.toJSON()).toStrictEqual({ list: [0, 1, 8, 9] });
expect(docB.toJSON()).toStrictEqual({ list: [0, 1, 8, 9] });
```
--------------------------------
### Get Text Length with UTF-16 Consideration
Source: https://loro.dev/docs/api/js
Gets the length of the text in UTF-16 code units. Note that characters outside the Basic Multilingual Plane, like complex emojis, count as 2 units, affecting index-based operations.
```javascript
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)
```
--------------------------------
### getNodeByID
Source: https://loro.dev/docs/api/js
Gets a node handler by its ID from the LoroTree.
```APIDOC
## getNodeByID
### Description
Gets a node handler by its ID.
### Parameters
* `id` - Node ID
### Returns
Node handler or undefined
### Example
```javascript
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");
}
```
```
--------------------------------
### Create EphemeralStore Instance
Source: https://loro.dev/docs/api/js
Initializes a new EphemeralStore. Set a timeout in milliseconds to define how long peer states are considered valid. Defaults to 30 seconds.
```typescript
import { EphemeralStore } from "loro-crdt";
// Create ephemeral store with 30 second timeout
const store = new EphemeralStore(30000);
```
--------------------------------
### Basic Container Operations (List and Map)
Source: https://loro.dev/docs/tutorial/get_started
Demonstrates basic operations on List and Map containers, including insertion, deletion, and setting values. Shows how to insert and set container types within other containers.
```typescript
const doc = new LoroDoc();
const list: LoroList = doc.getList("list");
list.insert(0, "A");
list.insert(1, "B");
list.insert(2, "C");
const map: LoroMap = doc.getMap("map");
// map can only has string key
map.set("key", "value");
expect(doc.toJSON()).toStrictEqual({
list: ["A", "B", "C"],
map: { key: "value" },
});
// delete 2 element at index 0
list.delete(0, 2);
expect(doc.toJSON()).toStrictEqual({
list: ["C"],
map: { key: "value" },
});
// Insert a text container to the list
const text = list.insertContainer(0, new LoroText());
text.insert(0, "Hello");
text.insert(0, "Hi! ");
expect(doc.toJSON()).toStrictEqual({
list: ["Hi! Hello", "C"],
map: { key: "value" },
});
// Insert a list container to the map
const list2 = map.setContainer("test", new LoroList());
list2.insert(0, 1);
expect(doc.toJSON()).toStrictEqual({
list: ["Hi! Hello", "C"],
map: { key: "value", test: [1] },
});
```
--------------------------------
### getShallowValue
Source: https://loro.dev/docs/api/js
Gets the map values with sub-containers as IDs.
```APIDOC
## getShallowValue
### Description
Gets the map values with sub-containers as IDs.
### Returns
Object with values
### Example
```javascript
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" }
```
```
--------------------------------
### parent
Source: https://loro.dev/docs/api/js
Gets the parent container if the map is nested.
```APIDOC
## parent
### Description
Gets the parent container if this map is nested.
### Returns
Parent container or undefined
### Example
```javascript
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
```
```
--------------------------------
### Basic LoroDoc Operations and Commit
Source: https://loro.dev/docs/concepts/operations_changes
Demonstrates basic text insertion operations and how they are grouped into a single change upon committing.
```javascript
const doc = new LoroDoc();
const text = doc.getText("text");
text.insert(0, "Hello"); // Operation
text.insert(5, " World"); // Operation
doc.commit(); // Groups into one Change
```
--------------------------------
### Real-time Sync with Offline Changes
Source: https://loro.dev/docs/tutorial/sync
Illustrates how two peers can synchronize when one has made offline changes. It covers exchanging version information, requesting missing updates, and establishing real-time sync subscriptions.
```typescript
const doc1 = new LoroDoc();
doc1.getText("text").insert(0, "Hello");
// Peer2 joins the network
const doc2 = new LoroDoc();
// ... doc2 may import its local snapshot
// 1. Exchange version information
const peer2Version = doc2.oplogVersion();
const peer1Version = doc1.oplogVersion();
// 2. Request missing updates from existing peers
const missingOps = doc1.export({
mode: "update",
from: peer2Version,
});
doc2.import(missingOps);
const missingOps2 = doc2.export({
mode: "update",
from: peer1Version,
});
doc1.import(missingOps2);
// 3. Establish realtime sync
doc2.subscribeLocalUpdates((update) => {
// websocket.send(update);
});
doc1.subscribeLocalUpdates((update) => {
// websocket.send(update);
});
// Now both peers are in sync and can collaborate
```
--------------------------------
### size
Source: https://loro.dev/docs/api/js
Gets the number of key-value pairs in the map.
```APIDOC
## size
### Description
Gets the number of key-value pairs.
### Returns
Map size
### Example
```javascript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const map = doc.getMap("map");
console.log(`Map has ${map.size} entries`);
```
```
--------------------------------
### Using MovableList Container
Source: https://loro.dev/docs/tutorial/loro_doc
Shows how to insert items into a movable list and change their order.
```javascript
const doc = new LoroDoc();
const movableList = doc.getMovableList("tasks");
movableList.insert(0, "Task 1");
movableList.insert(1, "Task 2");
movableList.move(0, 1); // Move Task 1 after Task 2
```
--------------------------------
### entries
Source: https://loro.dev/docs/api/js
Gets all key-value pairs present in the map.
```APIDOC
## entries(): [string, Value | Container][]
### Description
Gets all key-value pairs.
### Returns
Array of entries
### Example
```javascript
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}`);
}
```
```
--------------------------------
### Basic Usage of Loro Frontiers
Source: https://loro.dev/docs/concepts/frontiers
Demonstrates how to get current frontiers, use them for checkpoints, and restore a document to a previous state using LoroDoc.
```typescript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("content");
text.insert(0, "Hello World");
// Get current frontiers (usually 1-2 elements)
const frontiers = doc.frontiers();
console.log(frontiers); // [{ peer: "...", counter: 0 }]
// Use frontiers for checkpoints
const checkpoint = doc.frontiers();
text.insert(5, " Beautiful");
// Restore to checkpoint
doc.checkout(checkpoint);
console.log(text.toString()); // Back to "Hello World"
```
--------------------------------
### Text.id
Source: https://loro.dev/docs/api/js
Gets the unique identifier for the LoroText container.
```APIDOC
## Text.id
### Description
Gets the unique container ID.
### Example
```javascript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const text = doc.getText("text");
const containerId = text.id;
```
```
--------------------------------
### Ephemeral Store Basic Usage
Source: https://loro.dev/docs/tutorial/ephemeral
Demonstrates setting, getting, encoding, and subscribing to changes in the Ephemeral Store. Ensure you handle subscription objects to prevent garbage collection.
```typescript
import { EphemeralStore, EphemeralStoreEvent } from "loro-crdt";
import { expect } from "expect";
const store = new EphemeralStore();
// Set ephemeral data
store.set("loro-prosemirror", {
anchor: { pos: 0 },
focus: { pos: 5 },
user: "Alice",
});
store.set("online-users", ["Alice", "Bob"]);
expect(store.get("online-users")).toEqual(["Alice", "Bob"]);
// Encode only the data for `loro-prosemirror`
const encoded = store.encode("loro-prosemirror");
store.subscribe((e: EphemeralStoreEvent) => {
// Listen to changes from `local`, `remote`, or `timeout` events
});
```
--------------------------------
### LoroCounter Value
Source: https://loro.dev/docs/api/js
Gets the current numeric value of the counter.
```APIDOC
## value: number
### Description
Gets the current numeric value of the counter.
### Method
value
### Returns
Current numeric value
### Request Example
```javascript
import { LoroDoc } from "loro-crdt";
const doc = new LoroDoc();
const counter = doc.getCounter("counter");
console.log(`Counter value: ${counter.value}`);
```
```