35 releases (15 breaking)

new 0.16.0 Jan 24, 2023
0.14.1 Dec 14, 2022
0.13.0 Nov 23, 2022
0.11.1 Jul 25, 2022
0.0.1 Oct 20, 2020

#97 in Data structures

Download history 352/week @ 2022-10-07 553/week @ 2022-10-14 761/week @ 2022-10-21 948/week @ 2022-10-28 955/week @ 2022-11-04 860/week @ 2022-11-11 706/week @ 2022-11-18 750/week @ 2022-11-25 725/week @ 2022-12-02 773/week @ 2022-12-09 433/week @ 2022-12-16 158/week @ 2022-12-23 169/week @ 2022-12-30 294/week @ 2023-01-06 280/week @ 2023-01-13 403/week @ 2023-01-20

1,175 downloads per month
Used in 4 crates

MIT license

8MB
17K SLoC

Yrs

Yrs (read: wires) is a Rust port of the Yjs framework.

It's a library used on collaborative document editing using Conflict-free Replicated Data Types. This enables to provide a shared document editing experience on a client devices without explicit requirement for hosting a single server - CRDTs can resolve potential update conflicts on their own with no central authority - as well as provide first-class offline editing capabilities, where document replicas are modified without having connection to each other, and then synchronize automatically once such connection is enabled.

This library contains Rust API, that's used further on by other projects in this repository:

  • C foreign function interface to provide native interop that could be used by other host languages like Swift or Java.
  • ywasm which targets Web Assembly bindings and can be used directly from JavaScript.

Example

use yrs::*;

fn main() {    
    let doc = Doc::new();
    let text = doc.get_or_insert_text("name");
    // every operation in Yrs happens in scope of a transaction
    let mut txn = doc.transact_mut(); 
    // append text to our collaborative document
    text.push(&mut txn, "Hello from yrs!");
    // add formatting section to part of the text
    text.format(&mut txn, 11, 3, HashMap::from([
      ("link".into(), "https://github.com/y-crdt/y-crdt".into())
    ]));
    
    // simulate update with remote peer
    let remote_doc = Doc::new();
    let remote_text = remote_doc.get_or_insert_text("name");
    let mut remote_txn = remote_doc.transact();

    // in order to exchange data with other documents 
    // we first need to create a state vector
    let state_vector = remote_txn.state_vector();
    
    // now compute a differential update based on remote document's 
    // state vector
    let bytes = txn.encode_diff_v1(&state_vector);
    
    // both update and state vector are serializable, we can pass them 
    // over the wire now apply update to a remote document
    let update = Update::decode_v1(&bytes).unwrap();
    remote_txn.apply_update(update);

    // display raw text (no attributes)
    println!("{}", remote_text.to_string(&remote_txn));
  
    // create sequence of text chunks with optional format attributes
    let diff = remote_text.diff(&remote_txn, YChange::identity);
}

Documentation

Features

We're in ongoing process of reaching the feature compatibility with Yjs project. Current feature list:

  • Yjs update binary format (v1).
  • Yjs update binary format (v2).
  • Support for state vectors, delta diff updates and merges.
  • Subscription events for incoming updates.
  • Support for shared (CRDT) types:
    • Text
    • Array
    • Map
    • XML data types (XmlFragment, XmlElement, XmlText, XmlHook)
    • Subdocuments
    • Subscription events on particular data type
  • Cross-platform support for unicode code points
  • Transaction origins
  • Undo manager

Internal Documentation

Yrs implements the same algorithm and uses the same data structures as Yjs. We hope to achieve better performance because we can manually manage memory. Eventually Yrs might replace the Yjs module by generating a wasm module from this package.

In this package we want to use better terminology and experiment with new data structures to index data.

Each change on the document generates a small piece of information that is integrated into the document. In Yrs we call these small pieces blocks. Think of them as small Lego blocks that compose your document. Each block is uniquely identified by an identifier consisting of client id and sequence number (see: Lamport Clock). When you receive a duplicate block, you discard it. In some cases blocks can be put together to form a larger block. If necessary, blocks can be disassembled if only a part of the composed block is needed.

More information on the implemented algorithm can be found here:

Dependencies