#crdt #local-first #collaborative #rich-text #collaboration #data #framework

loro

Loro is a high-performance CRDTs framework. Make your app collaborative efforlessly.

14 releases (1 stable)

1.1.0 Nov 9, 2024
0.16.12 Sep 7, 2024
0.16.2 May 29, 2024
0.3.0 Feb 16, 2024
0.1.0 Aug 7, 2022

#190 in Web programming

Download history 134/week @ 2024-08-22 250/week @ 2024-08-29 431/week @ 2024-09-05 213/week @ 2024-09-12 155/week @ 2024-09-19 138/week @ 2024-09-26 25/week @ 2024-10-03 123/week @ 2024-10-10 155/week @ 2024-10-17 52/week @ 2024-10-24 88/week @ 2024-10-31 170/week @ 2024-11-07 123/week @ 2024-11-14 104/week @ 2024-11-21 39/week @ 2024-11-28 41/week @ 2024-12-05

313 downloads per month

MIT license

2.5MB
55K SLoC

Loro

Loro is a high-performance CRDTs framework offering Rust and JavaScript APIs.

Designed for local-first software, it enables effortless collaboration in app states.

PS: Version control is forthcoming. Time travel functionality is already accessible at https://loro.dev/docs/tutorial/time_travel.

Examples

Map/List/Text

use loro::{LoroDoc, LoroList, LoroText, LoroValue, ToJson};
use serde_json::json;

let doc = LoroDoc::new();
let map = doc.get_map("map");
map.insert("key", "value").unwrap();
map.insert("true", true).unwrap();
map.insert("null", LoroValue::Null).unwrap();
map.insert("deleted", LoroValue::Null).unwrap();
map.delete("deleted").unwrap();
let list = map.insert_container("list", LoroList::new()).unwrap();
list.insert(0, "List").unwrap();
list.insert(1, 9).unwrap();
let text = map.insert_container("text", LoroText::new()).unwrap();
text.insert(0, "Hello world!").unwrap();
assert_eq!(
    doc.get_deep_value().to_json_value(),
    json!({
        "map": {
            "key": "value",
            "true": true,
            "null": null,
            "list": ["List", 9],
            "text": "Hello world!"
        }
    })
);

Rich Text

use loro::{ExpandType, LoroDoc, ToJson};
use serde_json::json;

let doc = LoroDoc::new();
let text = doc.get_text("text");
text.insert(0, "Hello world!").unwrap();
text.mark(0..5, "bold", true).unwrap();
assert_eq!(
    text.to_delta().to_json_value(),
    json!([
        { "insert": "Hello", "attributes": {"bold": true} },
        { "insert": " world!" },
    ])
);
text.unmark(3..5, "bold").unwrap();
assert_eq!(
    text.to_delta().to_json_value(),
    json!([
          { "insert": "Hel", "attributes": {"bold": true} },
          { "insert": "lo world!" },
    ])
);

Sync

use loro::{LoroDoc, ToJson, ExpandType};
use serde_json::json;

let doc = LoroDoc::new();
let text = doc.get_text("text");
text.insert(0, "Hello world!").unwrap();
let bytes = doc.export_from(&Default::default());
let doc_b = LoroDoc::new();
doc_b.import(&bytes).unwrap();
assert_eq!(doc.get_deep_value(), doc_b.get_deep_value());
let text_b = doc_b.get_text("text");
text_b
    .mark(0..5, "bold", true)
    .unwrap();
doc.import(&doc_b.export_from(&doc.oplog_vv())).unwrap();
assert_eq!(
    text.to_delta().to_json_value(),
    json!([
        { "insert": "Hello", "attributes": {"bold": true} },
        { "insert": " world!" },
    ])
);

Save

use loro::LoroDoc;

let doc = LoroDoc::new();
let text = doc.get_text("text");
text.insert(0, "123").unwrap();
let snapshot = doc.export_snapshot();

let new_doc = LoroDoc::new();
new_doc.import(&snapshot).unwrap();
assert_eq!(new_doc.get_deep_value(), doc.get_deep_value());

Dependencies

~8–14MB
~178K SLoC