Class LoroDoc<T>

The CRDTs document. Loro supports different CRDTs include List, RichText, Map and Movable Tree, you could build all kind of applications by these.

Important: Loro is a pure library and does not handle network protocols. It is the responsibility of the user to manage the storage, loading, and synchronization of the bytes exported by Loro in a manner suitable for their specific environment.

import { LoroDoc } from "loro-crdt"

const loro = new LoroDoc();
const text = loro.getText("text");
const list = loro.getList("list");
const map = loro.getMap("Map");
const tree = loro.getTree("tree");

Type Parameters

Hierarchy (View Summary)

Constructors

  • Create a new loro document.

    New document will have a random peer id.

    Type Parameters

    Returns LoroDoc<T>

Properties

peerId: bigint

Peer ID of the current writer.

peerIdStr: `${number}`

Get peer id in decimal string.

Methods

  • Apply a batch of diff to the document

    A diff batch represents a set of changes between two versions of the document. You can calculate a diff batch using doc.diff().

    Changes are associated with container IDs. During diff application, if new containers were created in the source document, they will be assigned fresh IDs in the target document. Loro automatically handles remapping these container IDs from their original IDs to the new IDs as the diff is applied.

    Parameters

    Returns void

    const doc1 = new LoroDoc();
    const doc2 = new LoroDoc();

    // Make some changes to doc1
    const text = doc1.getText("text");
    text.insert(0, "Hello");

    // Calculate diff between empty and current state
    const diff = doc1.diff([], doc1.frontiers());

    // Apply changes to doc2
    doc2.applyDiff(diff);
    console.log(doc2.getText("text").toString()); // "Hello"
  • Attach the document state to the latest known version.

    The document becomes detached during a checkout operation. Being detached implies that the DocState is not synchronized with the latest version of the OpLog. In a detached state, the document is not editable, and any import operations will be recorded in the OpLog without being applied to the DocState.

    This method has the same effect as invoking checkoutToLatest.

    Returns void

    import { LoroDoc } from "loro-crdt";

    const doc = new LoroDoc();
    const text = doc.getText("text");
    const frontiers = doc.frontiers();
    text.insert(0, "Hello World!");
    doc.checkout(frontiers);
    // you need call `attach()` or `checkoutToLatest()` before changing the doc.
    doc.attach();
    text.insert(0, "Hi");
  • Get the number of changes in the oplog.

    Returns number

  • Checkout the DocState to a specific version.

    The document becomes detached during a checkout operation. Being detached implies that the DocState is not synchronized with the latest version of the OpLog. In a detached state, the document is not editable, and any import operations will be recorded in the OpLog without being applied to the DocState.

    You should call attach to attach the DocState to the latest version of OpLog.

    Parameters

    • frontiers: { counter: number; peer: `${number}` }[]

      the specific frontiers

    Returns void

    import { LoroDoc } from "loro-crdt";

    const doc = new LoroDoc();
    const text = doc.getText("text");
    const frontiers = doc.frontiers();
    text.insert(0, "Hello World!");
    doc.checkout(frontiers);
    console.log(doc.toJSON()); // {"text": ""}
  • Checkout the DocState to the latest version of OpLog.

    The document becomes detached during a checkout operation. Being detached implies that the DocState is not synchronized with the latest version of the OpLog. In a detached state, the document is not editable by default, and any import operations will be recorded in the OpLog without being applied to the DocState.

    This has the same effect as attach.

    Returns void

    import { LoroDoc } from "loro-crdt";

    const doc = new LoroDoc();
    const text = doc.getText("text");
    const frontiers = doc.frontiers();
    text.insert(0, "Hello World!");
    doc.checkout(frontiers);
    // you need call `checkoutToLatest()` or `attach()` before changing the doc.
    doc.checkoutToLatest();
    text.insert(0, "Hi");
  • Clear the options of the next commit

    Returns void

  • Compare the ordering of two Frontiers.

    It's assumed that both Frontiers are included by the doc. Otherwise, an error will be thrown.

    Return value:

    • -1: a < b
    • 0: a == b
    • 1: a > b
    • undefined: a ∥ b: a and b are concurrent

    Parameters

    • a: { counter: number; peer: `${number}` }[]
    • b: { counter: number; peer: `${number}` }[]

    Returns -1 | 0 | 1

  • Compare the version of the OpLog with the specified frontiers.

    This method is useful to compare the version by only a small amount of data.

    This method returns an integer indicating the relationship between the version of the OpLog (referred to as 'self') and the provided 'frontiers' parameter:

    • -1: The version of 'self' is either less than 'frontiers' or is non-comparable (parallel) to 'frontiers', indicating that it is not definitively less than 'frontiers'.
    • 0: The version of 'self' is equal to 'frontiers'.
    • 1: The version of 'self' is greater than 'frontiers'.

    Internal

    Frontiers cannot be compared without the history of the OpLog.

    Parameters

    • frontiers: { counter: number; peer: `${number}` }[]

    Returns number

  • Commit the cumulative auto-committed transaction.

    You can specify the origin, timestamp, and message of the commit.

    • The origin is used to mark the event
    • The message works like a git commit message, which will be recorded and synced to peers
    • The timestamp is the number of seconds that have elapsed since 00:00:00 UTC on January 1, 1970. It defaults to Date.now() / 1000 when timestamp recording is enabled

    The events will be emitted after a transaction is committed. A transaction is committed when:

    • doc.commit() is called.
    • doc.export(mode) is called.
    • doc.import(data) is called.
    • doc.checkout(version) is called.

    NOTE: Timestamps are forced to be in ascending order. If you commit a new change with a timestamp that is less than the existing one, the largest existing timestamp will be used instead.

    NOTE: The origin will not be persisted, but the message will.

    Parameters

    • Optionaloptions: { message?: string; origin?: string; timestamp?: number }

    Returns void

  • Configures the default text style for the document.

    This method sets the default text style configuration for the document when using LoroText. If None is provided, the default style is reset.

    Parameters

    • style: { expand: "before" | "after" | "none" | "both" }

    Returns void

  • Set the rich text format configuration of the document.

    You need to config it if you use rich text mark method. Specifically, you need to config the expand property of each style.

    Expand is used to specify the behavior of expanding when new text is inserted at the beginning or end of the style.

    You can specify the expand option to set the behavior when inserting text at the boundary of the range.

    • after(default): when inserting text right after the given range, the mark will be expanded to include the inserted text
    • before: when inserting text right before the given range, the mark will be expanded to include the inserted text
    • none: the mark will not be expanded to include the inserted text at the boundaries
    • both: when inserting text either right before or right after the given range, the mark will be expanded to include the inserted text

    Parameters

    • styles: { [key: string]: { expand: "before" | "after" | "none" | "both" } }

    Returns void

    const doc = new LoroDoc();
    doc.configTextStyle({
    bold: { expand: "after" },
    link: { expand: "before" }
    });
    const text = doc.getText("text");
    text.insert(0, "Hello World!");
    text.mark({ start: 0, end: 5 }, "bold", true);
    expect(text.toDelta()).toStrictEqual([
    {
    insert: "Hello",
    attributes: {
    bold: true,
    },
    },
    {
    insert: " World!",
    },
    ] as Delta<string>[]);
  • Debug the size of the history

    Returns void

  • Detach the document state from the latest known version.

    After detaching, all import operations will be recorded in the OpLog without being applied to the DocState. When detached, the document is not editable.

    Returns void

    import { LoroDoc } from "loro-crdt";

    const doc = new LoroDoc();
    doc.detach();
    console.log(doc.isDetached()); // true
  • Calculate the differences between two frontiers

    The entries in the returned object are sorted by causal order: the creation of a child container will be presented before its use.

    Parameters

    • from: OpId[]

      The source frontier to diff from. A frontier represents a consistent version of the document.

    • to: OpId[]

      The target frontier to diff to. A frontier represents a consistent version of the document.

    • for_json: false

      Controls the diff format: - If true, returns JsonDiff format suitable for JSON serialization - If false, returns Diff format that shares the same type as LoroEvent - The default value is true

    Returns [ContainerID, Diff][]

  • Parameters

    Returns [ContainerID, JsonDiff][]

  • Parameters

    • from: OpId[]
    • to: OpId[]
    • for_json: undefined

    Returns [ContainerID, JsonDiff][]

  • Parameters

    • from: OpId[]
    • to: OpId[]
    • Optionalfor_json: boolean

    Returns [
        ContainerID,
        | ListDiff
        | TextDiff
        | MapDiff
        | TreeDiff
        | CounterDiff
        | ListJsonDiff
        | MapJsonDiff,
    ][]

  • Export the document based on the specified ExportMode.

    Parameters

    • mode: ExportMode

      The export mode to use. Can be one of:

      • { mode: "snapshot" }: Export a full snapshot of the document.
      • { mode: "update", from?: VersionVector }: Export updates from the given version vector.
      • { mode: "updates-in-range", spans: { id: ID, len: number }[] }: Export updates within the specified ID spans.
      • { mode: "shallow-snapshot", frontiers: Frontiers }: Export a garbage-collected snapshot up to the given frontiers.

    Returns Uint8Array

    A byte array containing the exported data.

    import { LoroDoc, LoroText } from "loro-crdt";

    const doc = new LoroDoc();
    doc.setPeerId("1");
    doc.getText("text").update("Hello World");

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

    // Export updates from a specific version
    const vv = doc.oplogVersion();
    doc.getText("text").update("Hello Loro");
    const updateBytes = doc.export({ mode: "update", from: vv });

    // Export a shallow snapshot that only includes the history since the frontiers
    const shallowBytes = doc.export({ mode: "shallow-snapshot", frontiers: doc.oplogFrontiers() });

    // Export updates within specific ID spans
    const spanBytes = doc.export({
    mode: "updates-in-range",
    spans: [{ id: { peer: "1", counter: 0 }, len: 10 }]
    });
  • Export updates from the specific version to the current version

    Parameters

    Returns Uint8Array

    Use export({mode: "update", from: version}) instead

     import { LoroDoc } from "loro-crdt";

    const doc = new LoroDoc();
    const text = doc.getText("text");
    text.insert(0, "Hello");
    // get all updates of the doc
    const updates = doc.exportFrom();
    const version = doc.oplogVersion();
    text.insert(5, " World");
    // get updates from specific version to the latest version
    const updates2 = doc.exportFrom(version);
  • Export the readable [Change]s in the given [IdSpan].

    The peers are not compressed in the returned changes.

    Parameters

    • idSpan: IdSpan

      The id span to export.

    Returns JsonChange[]

    The changes in the given id span.

  • Export the updates in the given range.

    Parameters

    • Optionalstart: VersionVector

      The start version vector.

    • Optionalend: VersionVector

      The end version vector.

    • OptionalwithPeerCompression: boolean

      Whether to compress the peer IDs in the updates. Defaults to true. If you want to process the operations in application code, set this to false.

    Returns JsonSchema

    The updates in the given range.

  • Export the snapshot of current version. It includes all the history and the document state

    Returns Uint8Array

    Use export({mode: "snapshot"}) instead

  • Find the op id spans that between the from version and the to version.

    You can combine it with exportJsonInIdSpan to get the changes between two versions.

    You can use it to travel all the changes from from to to. from and to are frontiers, and they can be concurrent to each other. You can use it to find all the changes related to an event:

    Parameters

    • from: { counter: number; peer: `${number}` }[]
    • to: { counter: number; peer: `${number}` }[]

    Returns VersionVectorDiff

    import { LoroDoc } from "loro-crdt";

    const docA = new LoroDoc();
    docA.setPeerId("1");
    const docB = new LoroDoc();

    docA.getText("text").update("Hello");
    docA.commit();
    const snapshot = docA.export({ mode: "snapshot" });
    let done = false;
    docB.subscribe(e => {
    const spans = docB.findIdSpansBetween(e.from, e.to);
    const changes = docB.exportJsonInIdSpan(spans.forward[0]);
    console.log(changes);
    // [{
    // id: "0@1",
    // timestamp: expect.any(Number),
    // deps: [],
    // lamport: 0,
    // msg: undefined,
    // ops: [{
    // container: "cid:root-text:Text",
    // counter: 0,
    // content: {
    // type: "insert",
    // pos: 0,
    // text: "Hello"
    // }
    // }]
    // }]
    });
    docB.import(snapshot);
  • Duplicate the document with a different PeerID

    The time complexity and space complexity of this operation are both O(n),

    When called in detached mode, it will fork at the current state frontiers. It will have the same effect as forkAt(&self.frontiers()).

    Returns LoroDoc

  • Creates a new LoroDoc at a specified version (Frontiers)

    The created doc will only contain the history before the specified frontiers.

    Parameters

    • frontiers: { counter: number; peer: `${number}` }[]

    Returns LoroDoc

  • Returns void

  • Get the frontiers of the current document state.

    If you checkout to a specific version, this value will change.

    Returns { counter: number; peer: `${number}` }[]

  • Convert frontiers to a version vector

    Learn more about frontiers and version vector here

    Parameters

    • frontiers: { counter: number; peer: `${number}` }[]

    Returns VersionVector

    import { LoroDoc } from "loro-crdt";

    const doc = new LoroDoc();
    const text = doc.getText("text");
    text.insert(0, "Hello");
    const frontiers = doc.frontiers();
    const version = doc.frontiersToVV(frontiers);
  • Get all of changes in the oplog.

    Note: this method is expensive when the oplog is large. O(n)

    Returns Map<`${number}`, Change[]>

    import { LoroDoc, LoroText } from "loro-crdt";

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

    for (let [peer, c] of changes.entries()){
    console.log("peer: ", peer);
    for (let change of c){
    console.log("change: ", change);
    }
    }
  • Get the value or container at the given path

    The path can be specified in different ways depending on the container type:

    For Tree:

    1. Using node IDs: tree/{node_id}/property
    2. Using indices: tree/0/1/property

    For List and MovableList:

    • Using indices: list/0 or list/1/property

    For Map:

    • Using keys: map/key or map/nested/property

    For tree structures, index-based paths follow depth-first traversal order. The indices start from 0 and represent the position of a node among its siblings.

    Parameters

    • path: string

    Returns Container | Value

    import { LoroDoc } from "loro-crdt";

    const doc = new LoroDoc();
    const map = doc.getMap("map");
    map.set("key", 1);
    console.log(doc.getByPath("map/key")); // 1
    console.log(doc.getByPath("map")); // LoroMap
  • Get the change that contains the specific ID

    Parameters

    • id: { counter: number; peer: `${number}` }

    Returns Change

  • Get the change of with specific peer_id and lamport <= given lamport

    Parameters

    • peer_id: string
    • lamport: number

    Returns Change

  • Gets container IDs modified in the given ID range.

    NOTE: This method will implicitly commit.

    This method identifies which containers were affected by changes in a given range of operations. It can be used together with doc.travelChangeAncestors() to analyze the history of changes and determine which containers were modified by each change.

    Parameters

    • id: { counter: number; peer: `${number}` }

      The starting ID of the change range

    • len: number

      The length of the change range to check

    Returns ContainerID[]

    An array of container IDs that were modified in the given range

  • Get the container corresponding to the container id

    Parameters

    Returns Container

     import { LoroDoc } from "loro-crdt";

    const doc = new LoroDoc();
    let text = doc.getText("text");
    const textId = text.id;
    text = doc.getContainerById(textId);
  • Get a LoroCounter by container id

    If the container does not exist, an error will be thrown.

    Parameters

    • cid: string

    Returns LoroCounter

  • Get the absolute position of the given Cursor

    Parameters

    Returns { offset: number; side: Side; update?: Cursor }

    const doc = new LoroDoc();
    const text = doc.getText("text");
    text.insert(0, "123");
    const pos0 = text.getCursor(0, 0);
    {
    const ans = doc.getCursorPos(pos0!);
    expect(ans.offset).toBe(0);
    }
    text.insert(0, "1");
    {
    const ans = doc.getCursorPos(pos0!);
    expect(ans.offset).toBe(1);
    }
  • Get deep value of the document with container id

    Returns any

  • Get a LoroList by container id

    The object returned is a new js object each time because it need to cross the WASM boundary.

    Type Parameters

    • Key extends string | number | symbol

    Parameters

    Returns T[Key] extends LoroList<unknown> ? any[any] : LoroList<unknown>

    import { LoroDoc } from "loro-crdt";

    const doc = new LoroDoc();
    const list = doc.getList("list");
  • Get a LoroMap by container id

    The object returned is a new js object each time because it need to cross the WASM boundary.

    Type Parameters

    • Key extends string | number | symbol

    Parameters

    Returns T[Key] extends LoroMap<Record<string, unknown>>
        ? any[any]
        : LoroMap<Record<string, unknown>>

    import { LoroDoc } from "loro-crdt";

    const doc = new LoroDoc();
    const map = doc.getMap("map");
  • Get a LoroMovableList by container id

    The object returned is a new js object each time because it need to cross the WASM boundary.

    Type Parameters

    • Key extends string | number | symbol

    Parameters

    Returns T[Key] extends LoroMovableList<unknown> ? any[any] : LoroMovableList<unknown>

    import { LoroDoc } from "loro-crdt";

    const doc = new LoroDoc();
    const list = doc.getList("list");
  • Get all ops of the change that contains the specific ID

    Parameters

    • id: { counter: number; peer: `${number}` }

    Returns any[]

  • Get the path from the root to the container

    Parameters

    Returns (string | number)[]

  • Get the number of operations in the pending transaction.

    The pending transaction is the one that is not committed yet. It will be committed automatically after calling doc.commit(), doc.export(mode) or doc.checkout(version).

    Returns number

  • Get the shallow json format of the document state.

    Unlike toJSON() which recursively resolves all containers to their values, getShallowValue() returns container IDs as strings for any nested containers.

    Returns Record<string, ContainerID>

    import { LoroDoc } from "loro-crdt";

    const doc = new LoroDoc();
    const list = doc.getList("list");
    const tree = doc.getTree("tree");
    const map = doc.getMap("map");
    const shallowValue = doc.getShallowValue();
    console.log(shallowValue);
    // {
    // list: 'cid:root-list:List',
    // tree: 'cid:root-tree:Tree',
    // map: 'cid:root-map:Map'
    // }

    // It points to the same container as `list`
    const listB = doc.getContainerById(shallowValue.list);
  • Parameters

    • key: string

    Returns LoroText

  • Get a LoroText by container id.

    The object returned is a new js object each time because it need to cross the WASM boundary.

    If the container does not exist, an error will be thrown.

    Parameters

    • cid: string

    Returns LoroText

    import { LoroDoc } from "loro-crdt";

    const doc = new LoroDoc();
    const text = doc.getText("text");
  • Get a LoroTree by container id

    The object returned is a new js object each time because it need to cross the WASM boundary.

    Type Parameters

    • Key extends string | number | symbol

    Parameters

    Returns T[Key] extends LoroTree<Record<string, unknown>>
        ? any[any]
        : LoroTree<Record<string, unknown>>

     import { LoroDoc } from "loro-crdt";

    const doc = new LoroDoc();
    const tree = doc.getTree("tree");
  • Check if the doc contains the target container.

    A root container always exists, while a normal container exists if it has ever been created on the doc.

    Parameters

    Returns boolean

    import { LoroDoc, LoroMap, LoroText, LoroList } from "loro-crdt";

    const doc = new LoroDoc();
    doc.setPeerId("1");
    const text = doc.getMap("map").setContainer("text", new LoroText());
    const list = doc.getMap("map").setContainer("list", new LoroList());
    expect(doc.isContainerExists("cid:root-map:Map")).toBe(true);
    expect(doc.isContainerExists("cid:0@1:Text")).toBe(true);
    expect(doc.isContainerExists("cid:1@1:List")).toBe(true);

    const doc2 = new LoroDoc();
    // Containers exist, as long as the history or the doc state include it
    doc.detach();
    doc2.import(doc.export({ mode: "update" }));
    expect(doc2.isContainerExists("cid:root-map:Map")).toBe(true);
    expect(doc2.isContainerExists("cid:0@1:Text")).toBe(true);
    expect(doc2.isContainerExists("cid:1@1:List")).toBe(true);
  • Import snapshot or updates into current doc.

    Note:

    • Updates within the current version will be ignored
    • Updates with missing dependencies will be pending until the dependencies are received

    Parameters

    • update_or_snapshot: Uint8Array

    Returns ImportStatus

    import { LoroDoc } from "loro-crdt";

    const doc = new LoroDoc();
    const text = doc.getText("text");
    text.insert(0, "Hello");
    // get all updates of the doc
    const updates = doc.export({ mode: "update" });
    const snapshot = doc.export({ mode: "snapshot" });
    const doc2 = new LoroDoc();
    // import snapshot
    doc2.import(snapshot);
    // or import updates
    doc2.import(updates);
  • Import a batch of updates or snapshots.

    It's more efficient than importing updates one by one.

    Parameters

    • data: Uint8Array<ArrayBufferLike>[]

    Returns ImportStatus

    import { LoroDoc } from "loro-crdt";

    const doc = new LoroDoc();
    const text = doc.getText("text");
    text.insert(0, "Hello");
    const updates = doc.export({ mode: "update" });
    const snapshot = doc.export({ mode: "snapshot" });
    const doc2 = new LoroDoc();
    doc2.importBatch([snapshot, updates]);
  • Import updates from the JSON format.

    only supports backward compatibility but not forward compatibility.

    Parameters

    Returns ImportStatus

  • Import a batch of updates and snapshots.

    It's more efficient than importing updates one by one.

    Parameters

    • data: Uint8Array<ArrayBufferLike>[]

    Returns ImportStatus

    Use importBatch instead.

    import { LoroDoc } from "loro-crdt";

    const doc = new LoroDoc();
    const text = doc.getText("text");
    text.insert(0, "Hello");
    const updates = doc.export({ mode: "update" });
    const snapshot = doc.export({ mode: "snapshot" });
    const doc2 = new LoroDoc();
    doc2.importBatch([snapshot, updates]);
  • detached indicates that the DocState is not synchronized with the latest version of OpLog.

    The document becomes detached during a checkout operation. Being detached implies that the DocState is not synchronized with the latest version of the OpLog. In a detached state, the document is not editable by default, and any import operations will be recorded in the OpLog without being applied to the DocState.

    Returns boolean

    import { LoroDoc } from "loro-crdt";

    const doc = new LoroDoc();
    const text = doc.getText("text");
    const frontiers = doc.frontiers();
    text.insert(0, "Hello World!");
    console.log(doc.isDetached()); // false
    doc.checkout(frontiers);
    console.log(doc.isDetached()); // true
    doc.attach();
    console.log(doc.isDetached()); // false
  • Whether the editing is enabled in detached mode.

    The doc enter detached mode after calling detach or checking out a non-latest version.

    Important Notes:

    • This mode uses a different PeerID for each checkout.
    • Ensure no concurrent operations share the same PeerID if set manually.
    • Importing does not affect the document's state or version; changes are recorded in the [OpLog] only. Call checkout to apply changes.

    Returns boolean

  • Check if the doc contains the full history.

    Returns boolean

  • Evaluate JSONPath against a LoroDoc

    Parameters

    • jsonpath: string

    Returns any[]

  • Get the number of ops in the oplog.

    Returns number

  • Get the frontiers of the latest version in OpLog.

    If you checkout to a specific version, this value will not change.

    Returns { counter: number; peer: `${number}` }[]

  • Get the version vector of the latest known version in OpLog.

    If you checkout to a specific version, this version vector will not change.

    Returns VersionVector

  • Revert the document to the given frontiers.

    The doc will not become detached when using this method. Instead, it will generate a series of operations to revert the document to the given version.

    Parameters

    • frontiers: { counter: number; peer: `${number}` }[]

    Returns void

    const doc = new LoroDoc();
    doc.setPeerId("1");
    const text = doc.getText("text");
    text.insert(0, "Hello");
    doc.commit();
    doc.revertTo([{ peer: "1", counter: 1 }]);
    expect(doc.getText("text").toString()).toBe("He");
  • If two continuous local changes are within (<=) the interval(in seconds), they will be merged into one change.

    The default value is 1_000 seconds.

    By default, we record timestamps in seconds for each change. So if the merge interval is 1, and changes A and B have timestamps of 3 and 4 respectively, then they will be merged into one change

    Parameters

    • interval: number

    Returns void

  • Enables editing in detached mode, which is disabled by default.

    The doc enter detached mode after calling detach or checking out a non-latest version.

    Important Notes:

    • This mode uses a different PeerID for each checkout.
    • Ensure no concurrent operations share the same PeerID if set manually.
    • Importing does not affect the document's state or version; changes are recorded in the [OpLog] only. Call checkout to apply changes.

    Parameters

    • enable: boolean

    Returns void

  • Set the commit message of the next commit

    Parameters

    • msg: string

    Returns void

  • Set the options of the next commit

    Parameters

    • options: { message?: string; origin?: string; timestamp?: number }

    Returns void

  • Set the origin of the next commit

    Parameters

    • origin: string

    Returns void

  • Set the timestamp of the next commit

    Parameters

    • timestamp: number

    Returns void

  • Set the peer ID of the current writer.

    It must be a number, a BigInt, or a decimal string that can be parsed to a unsigned 64-bit integer.

    Note: use it with caution. You need to make sure there is not chance that two peers have the same peer ID. Otherwise, we cannot ensure the consistency of the document.

    Parameters

    • peer_id: number | bigint | `${number}`

    Returns void

  • Set whether to record the timestamp of each change. Default is false.

    If enabled, the Unix timestamp (in seconds) will be recorded for each change automatically.

    You can also set each timestamp manually when you commit a change. The timestamp manually set will override the automatic one.

    NOTE: Timestamps are forced to be in ascending order in the OpLog's history. If you commit a new change with a timestamp that is less than the existing one, the largest existing timestamp will be used instead.

    Parameters

    • auto_record: boolean

    Returns void

  • The doc only contains the history since this version

    This is empty if the doc is not shallow.

    The ops included by the shallow history start frontiers are not in the doc.

    Returns { counter: number; peer: `${number}` }[]

  • The doc only contains the history since this version

    This is empty if the doc is not shallow.

    The ops included by the shallow history start version vector are not in the doc.

    Returns VersionVector

  • Subscribe to updates from local edits.

    This method allows you to listen for local changes made to the document. It's useful for syncing changes with other instances or saving updates.

    Parameters

    • f: (bytes: Uint8Array) => void

      A callback function that receives a Uint8Array containing the update data.

    Returns () => void

    A function to unsubscribe from the updates.

    const loro = new Loro();
    const text = loro.getText("text");

    const unsubscribe = loro.subscribeLocalUpdates((update) => {
    console.log("Local update received:", update);
    // You can send this update to other Loro instances
    });

    text.insert(0, "Hello");
    loro.commit();

    // Later, when you want to stop listening:
    unsubscribe();
    const loro1 = new Loro();
    const loro2 = new Loro();

    // Set up two-way sync
    loro1.subscribeLocalUpdates((updates) => {
    loro2.import(updates);
    });

    loro2.subscribeLocalUpdates((updates) => {
    loro1.import(updates);
    });

    // Now changes in loro1 will be reflected in loro2 and vice versa
  • Get the json format of the entire document state.

    Unlike getShallowValue() which returns container IDs as strings, toJSON() recursively resolves all containers to their actual values.

    Returns any

    import { LoroDoc, LoroText, LoroMap } from "loro-crdt";

    const doc = new LoroDoc();
    const list = doc.getList("list");
    list.insert(0, "Hello");
    const text = list.insertContainer(0, new LoroText());
    text.insert(0, "Hello");
    const map = list.insertContainer(1, new LoroMap());
    map.set("foo", "bar");
    console.log(doc.toJSON());
    // {"list": ["Hello", {"foo": "bar"}]}
  • Convert the document to a JSON value with a custom replacer function.

    This method works similarly to JSON.stringify's replacer parameter. The replacer function is called for each value in the document and can transform how values are serialized to JSON.

    Parameters

    • replacer: (key: any, value: Container | Value) => Container | Value

      A function that takes a key and value, and returns how that value should be serialized. Similar to JSON.stringify's replacer. If return undefined, the value will be skipped.

    Returns Value

    The JSON representation of the document after applying the replacer function.

    const doc = new LoroDoc();
    const text = doc.getText("text");
    text.insert(0, "Hello");
    text.mark({ start: 0, end: 2 }, "bold", true);

    // Use delta to represent text
    const json = doc.toJsonWithReplacer((key, value) => {
    if (value instanceof LoroText) {
    return value.toDelta();
    }

    return value;
    });
  • Visit all the ancestors of the changes in causal order.

    Parameters

    • ids: { counter: number; peer: `${number}` }[]

      the changes to visit

    • f: (change: Change) => boolean

      the callback function, return true to continue visiting, return false to stop

    Returns void

  • Get the version vector of the current document state.

    If you checkout to a specific version, the version vector will change.

    Returns VersionVector

  • Convert a version vector to frontiers

    Parameters

    Returns { counter: number; peer: `${number}` }[]

    import { LoroDoc } from "loro-crdt";

    const doc = new LoroDoc();
    const text = doc.getText("text");
    text.insert(0, "Hello");
    const version = doc.version();
    const frontiers = doc.vvToFrontiers(version);
  • Create a loro document from the snapshot.

    Parameters

    • snapshot: Uint8Array

    Returns LoroDoc

    You can learn more here.

    import { LoroDoc } from "loro-crdt"

    const doc = new LoroDoc();
    // ...
    const bytes = doc.export({ mode: "snapshot" });
    const loro = LoroDoc.fromSnapshot(bytes);