Skip to content

Releases: loro-dev/loro

[email protected]

10 Apr 04:46
Compare
Choose a tag to compare

Patch Changes

  • ed4fe83: fix: from_snapshot with shallow snapshot err #712

[email protected]

08 Apr 11:31
Compare
Choose a tag to compare

Patch Changes

  • 81c7bb7: fix ephemeral store recursive use by adding mutex in the inner
  • bf94a03: feat: add functionality to delete and hide empty root containers #708

[email protected]

06 Apr 05:41
Compare
Choose a tag to compare

Patch Changes

[email protected]

04 Apr 03:30
Compare
Choose a tag to compare

Minor Changes

  • 8dfcad4: # New Hooks: pre-commit and first-commit-from-peer

    doc.subscribePreCommit(listener)

    The pre-commit hook enables users to modify commit options before any commit is processed.

    This hook is particularly useful because doc.commit() is often invoked implicitly in various methods such as doc.import, doc.export, doc.checkout, and doc.exportJsonUpdates. Without this hook, users attempting to add custom messages to each commit might miss these implicit commit triggers.

    const doc = new LoroDoc();
    doc.setPeerId(0);
    doc.subscribePreCommit((e) => {
      e.modifier.setMessage("test").setTimestamp(Date.now());
    });
    doc.getList("list").insert(0, 100);
    doc.commit();
    expect(doc.getChangeAt({ peer: "0", counter: 0 }).message).toBe("test");

    Advanced Example: Creating a Merkle DAG

    By combining doc.subscribePreCommit with doc.exportJsonInIdSpan, you can implement advanced features like representing Loro's editing history as a Merkle DAG:

    const doc = new LoroDoc();
    doc.setPeerId(0);
    doc.subscribePreCommit((e) => {
      const changes = doc.exportJsonInIdSpan(e.changeMeta);
      expect(changes).toHaveLength(1);
      const hash = crypto.createHash("sha256");
      const change = {
        ...changes[0],
        deps: changes[0].deps.map((d) => {
          const depChange = doc.getChangeAt(idStrToId(d));
          return depChange.message;
        }),
      };
      hash.update(JSON.stringify(change));
      const sha256Hash = hash.digest("hex");
      e.modifier.setMessage(sha256Hash);
    });
    
    console.log(change); // The output is shown below
    doc.getList("list").insert(0, 100);
    doc.commit();
    // Change 0
    // {
    //   id: '0@0',
    //   timestamp: 0,
    //   deps: [],
    //   lamport: 0,
    //   msg: undefined,
    //   ops: [
    //     {
    //       container: 'cid:root-list:List',
    //       content: { type: 'insert', pos: 0, value: [100] },
    //       counter: 0
    //     }
    //   ]
    // }
    
    doc.getList("list").insert(0, 200);
    doc.commit();
    // Change 1
    // {
    //   id: '1@0',
    //   timestamp: 0,
    //   deps: [
    //     '2af99cf93869173984bcf6b1ce5412610b0413d027a5511a8f720a02a4432853'
    //   ],
    //   lamport: 1,
    //   msg: undefined,
    //   ops: [
    //     {
    //       container: 'cid:root-list:List',
    //       content: { type: 'insert', pos: 0, value: [200] },
    //       counter: 1
    //     }
    //   ]
    // }
    
    expect(doc.getChangeAt({ peer: "0", counter: 0 }).message).toBe(
      "2af99cf93869173984bcf6b1ce5412610b0413d027a5511a8f720a02a4432853",
    );
    expect(doc.getChangeAt({ peer: "0", counter: 1 }).message).toBe(
      "aedbb442c554ecf59090e0e8339df1d8febf647f25cc37c67be0c6e27071d37f",
    );

    doc.subscribeFirstCommitFromPeer(listener)

    The first-commit-from-peer event triggers when a peer performs operations on the document for the first time.
    This provides an ideal point to associate peer information (such as author identity) with the document.

    const doc = new LoroDoc();
    doc.setPeerId(0);
    doc.subscribeFirstCommitFromPeer((e) => {
      doc.getMap("users").set(e.peer, "user-" + e.peer);
    });
    doc.getList("list").insert(0, 100);
    doc.commit();
    expect(doc.getMap("users").get("0")).toBe("user-0");
  • a997885: # EphemeralStore: An Alternative to Awareness

    Awareness is commonly used as a state-based CRDT for handling ephemeral states in real-time collaboration scenarios, such as cursor positions and application component highlights. As application complexity grows, Awareness may be set in multiple places, from cursor positions to user presence. However, the current version of Awareness doesn't support partial state updates, which means even minor mouse movements require synchronizing the entire Awareness state.

    awareness.setLocalState({
      ...awareness.getLocalState(),
      x: 167,
    });

    Since Awareness is primarily used in real-time collaboration scenarios where consistency requirements are relatively low, we can make it more flexible. We've introduced EphemeralStore as an alternative to Awareness. Think of it as a simple key-value store that uses timestamp-based last-write-wins for conflict resolution. You can choose the appropriate granularity for your key-value pairs based on your application's needs, and only modified key-value pairs are synchronized.

    Examples

    import {
        EphemeralStore,
        EphemeralListener,
        EphemeralStoreEvent,
    } from "loro-crdt";
    
    const store = new EphemeralStore();
    // Set ephemeral data
    store.set("loro-prosemirror", {
        anchor: ...,
        focus: ...,
        user: "Alice"
    });
    store.set("online-users", ["Alice", "Bob"]);
    
    expect(storeB.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
    });

Patch Changes

  • 742842f: fix: apply multiple styles via text delta at the end "\n" char #692
  • 4cb7ae3: feat: get ops from current txn as json #676

[email protected]

03 Mar 04:20
Compare
Choose a tag to compare

Patch Changes

  • 0b0ac7c: fix: entity index when the tree is empty

[email protected]

27 Feb 14:43
Compare
Choose a tag to compare

Patch Changes

[email protected]

26 Feb 10:46
Compare
Choose a tag to compare

Patch Changes

  • 28d1264: feat(wasm): enhance toJsonWithReplacer to handle nested containers in replacer returned value

  • 28d1264: fix(wasm): add toJSON to LoroText

    Now all containers have toJSON method.

[email protected]

24 Feb 13:51
Compare
Choose a tag to compare

Patch Changes

  • 2a82396: feat: add new ways to control commit options (#656)
  • 2a82396: fix: mark err on detached LoroText (#659)

[email protected]

14 Feb 09:18
Compare
Choose a tag to compare

Patch Changes

  • e0948d8: feat: add container existence check methods & avoid panic in wasm/js #651
  • 9955500: fix: an internal iter_change err that may cause fork_at panic #649

[email protected]

13 Feb 17:37
Compare
Choose a tag to compare

Patch Changes