| 1 | # Yrs |
| 2 | |
| 3 | Yrs (read: *wires*) is a Rust port of the [Yjs framework](https://yjs.dev/). |
| 4 | |
| 5 | It's a library used on collaborative document editing using Conflict-free Replicated Data Types. |
| 6 | 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. |
| 7 | |
| 8 | This library contains Rust API, that's used further on by other projects in this repository: |
| 9 | |
| 10 | - [C foreign function interface](https://github.com/y-crdt/y-crdt/tree/main/yffi) to provide native interop that could be used by other host languages like Swift or Java. |
| 11 | - [ywasm](https://github.com/y-crdt/y-crdt/tree/main/ywasm) which targets Web Assembly bindings and can be used directly from JavaScript. |
| 12 | |
| 13 | ## Example |
| 14 | |
| 15 | ```rust |
| 16 | use yrs::*; |
| 17 | |
| 18 | fn main() { |
| 19 | let doc = Doc::new(); |
| 20 | let text = doc.get_or_insert_text("name"); |
| 21 | // every operation in Yrs happens in scope of a transaction |
| 22 | let mut txn = doc.transact_mut(); |
| 23 | // append text to our collaborative document |
| 24 | text.push(&mut txn, "Hello from yrs!"); |
| 25 | // add formatting section to part of the text |
| 26 | text.format(&mut txn, 11, 3, HashMap::from([ |
| 27 | ("link".into(), "https://github.com/y-crdt/y-crdt".into()) |
| 28 | ])); |
| 29 | |
| 30 | // simulate update with remote peer |
| 31 | let remote_doc = Doc::new(); |
| 32 | let remote_text = remote_doc.get_or_insert_text("name"); |
| 33 | let mut remote_txn = remote_doc.transact_mut(); |
| 34 | |
| 35 | // in order to exchange data with other documents |
| 36 | // we first need to create a state vector |
| 37 | let state_vector = remote_txn.state_vector(); |
| 38 | |
| 39 | // now compute a differential update based on remote document's |
| 40 | // state vector |
| 41 | let bytes = txn.encode_diff_v1(&state_vector); |
| 42 | |
| 43 | // both update and state vector are serializable, we can pass them |
| 44 | // over the wire now apply update to a remote document |
| 45 | let update = Update::decode_v1(&bytes).unwrap(); |
| 46 | remote_txn.apply_update(update); |
| 47 | |
| 48 | // display raw text (no attributes) |
| 49 | println!("{}", remote_text.get_string(&remote_txn)); |
| 50 | |
| 51 | // create sequence of text chunks with optional format attributes |
| 52 | let diff = remote_text.diff(&remote_txn, YChange::identity); |
| 53 | } |
| 54 | ``` |
| 55 | |
| 56 | ## [Documentation](https://docs.rs/yrs/) |
| 57 | |
| 58 | ## Features |
| 59 | |
| 60 | We're in ongoing process of reaching the feature compatibility with Yjs project. Current feature list: |
| 61 | |
| 62 | - [x] Yjs update binary format (v1). |
| 63 | - [x] Yjs update binary format (v2). |
| 64 | - [x] Support for state vectors, delta diff updates and merges. |
| 65 | - [x] Subscription events for incoming updates. |
| 66 | - [x] Support for shared (CRDT) types: |
| 67 | - [x] Text |
| 68 | - [x] Array |
| 69 | - [x] Map |
| 70 | - [x] XML data types (XmlFragment, XmlElement, XmlText, XmlHook) |
| 71 | - [x] Subdocuments |
| 72 | - [x] Subscription events on particular data type |
| 73 | - [x] Cross-platform support for unicode code points |
| 74 | - [x] Transaction origins |
| 75 | - [x] Undo manager |
| 76 | |
| 77 | ## Internal Documentation |
| 78 | |
| 79 | Yrs implements the same algorithm and uses the same data structures as Yjs. We |
| 80 | hope to achieve better performance because we can manually manage memory. |
| 81 | Eventually Yrs might replace the Yjs module by generating a wasm module from |
| 82 | this package. |
| 83 | |
| 84 | In this package we want to use better terminology and experiment with new data |
| 85 | structures to index data. |
| 86 | |
| 87 | Each change on the document generates a small piece of information that is |
| 88 | integrated into the document. In Yrs we call these small pieces **blocks**. |
| 89 | Think of them as small Lego blocks that compose your document. Each block is |
| 90 | uniquely identified by an identifier consisting of client id and sequence number |
| 91 | (see: [Lamport Clock](https://en.wikipedia.org/wiki/Lamport_timestamp)). |
| 92 | When you receive a duplicate block, you discard it. In some cases blocks can be put |
| 93 | together to form a larger block. If necessary, blocks can be disassembled if only |
| 94 | a part of the composed block is needed. |
| 95 | |
| 96 | More information on the implemented algorithm can be found here: |
| 97 | |
| 98 | * [Paper describing the YATA conflict resolution algorithm](https://www.researchgate.net/publication/310212186_Near_Real-Time_Peer-to-Peer_Shared_Editing_on_Extensible_Data_Types) |
| 99 | * [Short overview of the Yjs optimizations](https://github.com/yjs/yjs/blob/main/README.md#yjs-crdt-algorithm) |
| 100 | * [Motivation for composable blocks](https://blog.kevinjahns.de/are-crdts-suitable-for-shared-editing/) |
| 101 | * [Internal documentation of the Yjs project](https://github.com/yjs/yjs/blob/main/INTERNALS.md) |
| 102 | * [Walkthrough of the Yjs codebase](https://youtu.be/0l5XgnQ6rB4) |
| 103 | |