#![doc(
html_logo_url = "https://raw.githubusercontent.com/y-crdt/y-crdt/main/logo-yrs.svg",
html_favicon_url = "https://raw.githubusercontent.com/y-crdt/y-crdt/main/logo-yrs.svg"
)]
//! Yrs (read: "wires") is a high performance CRDT implementation based on the idea of **Shared Types**.
//! It is a compatible port of the [Yjs](https://github.com/yjs/yjs) CRDT.
//!
//! **Shared Types** work just like normal data types, but they automatically sync with other peers.
//!
//! A **Document** is the access point to create shared types, and to listen to update events.
//!
//! # Features
//!
//! - `weak` this feature enables weak references and quotations (see: [crate::WeakRef]).
//! - `sync` this feature modifies observers callback constraints to use `Send` and `Sync` traits.
//! These are required when using yrs features in multithreaded environments.
//!
//! # Quick start
//!
//! Let's discuss basic features of Yrs. We'll introduce these concepts starting from
//! the following code snippet:
//!
//! ```rust
//! use yrs::{Doc, GetString, ReadTxn, StateVector, Text, Transact, Update};
//! use yrs::updates::decoder::Decode;
//! use yrs::updates::encoder::Encode;
//!
//! let doc = Doc::new();
//! let text = doc.get_or_insert_text("article");
//!
//! {
//! let mut txn = doc.transact_mut();
//! text.insert(&mut txn, 0, "hello");
//! text.insert(&mut txn, 5, " world");
//! // other rich text operations include formatting or inserting embedded elements
//! } // transaction is automatically committed when dropped
//!
//! assert_eq!(text.get_string(&doc.transact()), "hello world".to_owned());
//!
//! // synchronize state with remote replica
//! let remote_doc = Doc::new();
//! let remote_text = remote_doc.get_or_insert_text("article");
//! let remote_timestamp = remote_doc.transact().state_vector().encode_v1();
//!
//! // get update with contents not observed by remote_doc
//! let update = doc.transact().encode_diff_v1(&StateVector::decode_v1(&remote_timestamp).unwrap());
//! // apply update on remote doc
//! remote_doc.transact_mut().apply_update(Update::decode_v1(&update).unwrap());
//!
//! assert_eq!(text.get_string(&doc.transact()), remote_text.get_string(&remote_doc.transact()));
//! ```
//!
//! [Doc] is a core structure of Yrs. All other structures and operations are performed in
//! context of their document. All documents gets randomly generated [Doc::client_id] (which can be
//! also defined explicitly), which must be unique per active peer. It's crucial, as potential
//! concurrent changes made by different peers sharing the same [ClientID] will cause a document
//! state corruption.
//!
//! Next line defines [TextRef] - a shared collection specialized in providing collaborative rich text
//! operations. In snippet above it has been defined by calling [Doc::get_or_insert_text] method.
//! Shared types defined at the document level are so called root types. Root level types sharing
//! the same name across different peers are considered to be replicas of the same logical entity,
//! regardless of their type. It's highly recommended for all collaborating clients to define all
//! root level types they are going to use up front, during document creation. A list of supported
//! shared types include: [TextRef], [ArrayRef], [MapRef], [XmlTextRef], [XmlFragmentRef] and [XmlElementRef].
//!
//! Next section of code performs an update over the defined text replica. In Yrs all operations
//! must be executed in a scope of transaction created by the document they were defined in. We
//! can differentiate two transaction types:
//!
//! 1. [Read-only transactions](Transaction), created via [Transact::transact]/[Transact::try_transact].
//! They are used only to access the contents of an underlying document store but they never
//! alter it. They are useful for methods like reading the structure state or for serialization.
//! It's allowed to have multiple active read-only transactions as long as no read-write
//! transaction is in progress.
//! 2. [Read-write transactions](TransactionMut), create via [Transact::transact_mut]/[Transact::try_transact_mut].
//! These can be used to modify the internal document state. These transactions work as
//! intelligent batches. They are automatically committed when dropped, performing tasks like
//! state cleaning, metadata compression and triggering event callbacks. Read-write transactions
//! require exclusive access to an underlying document store - no other transaction (neither
//! read-write nor read-only one) can be active while read-write transaction is to be created.
//!
//! In order to synchronize state between the document replicas living on a different peer processes,
//! there are two possible cases:
//!
//! 1. Peer who wishes to receive an update first encodes its document's [state vector](ReadTxn::state_vector).
//! It's a logical timestamp describing which updates that has been observed by this document instance
//! so far. This [StateVector] can be later serialized and passed to the remote collaborator. This
//! collaborator can then deserialize it back and [generate an update](ReadTxn::encode_diff) which
//! will contain all new changes performed since provided state vector. Finally this update can
//! be passed back to the requester, [deserialized](Update::decode) and integrated into a document
//! store via [TransactionMut::apply_update].
//! 2. Another propagation mechanism relies on subscribing to [Doc::observe_update_v1] or
//! [Doc::observe_update_v2] events, which will be fired whenever an referenced document will
//! detect new changes.
//!
//! While the 2nd option can produce smaller binary payload than 1st one at times and doesn't require
//! request-response cycles, it cannot pass the document state prior the observer callback
//! registration and expects that all changes will surely be delivered to other peer. A practical
//! approach (used i.e. by [y-sync protocol](https://crates.io/crates/y-sync)) is usually a combination
//! of both variants: use 1st one on connection initialization between two peers followed by
//! 2nd approach to deliver subsequent changes.
//!
//! # Formatting and embedding
//!
//! While the quick start example covered only a simple text insertions, structures such as
//! [TextRef]/[XmlTextRef] are capable of including more advanced operators, such as adding
//! [formatting attributes](Text::format), [inserting embedded content](Text::insert_embed)
//! (eg. image binaries or [ArrayRef]s that we could interpret in example as nested tables).
//!
//! ```rust
//! use yrs::{Any, Array, ArrayPrelim, Doc, GetString, Text, Transact, WriteTxn, XmlFragment, XmlTextPrelim};
//! use yrs::types::Attrs;
//!
//! let doc = Doc::new();
//! let mut txn = doc.transact_mut();
//! let f = txn.get_or_insert_xml_fragment("article");
//! let xml = f.insert(&mut txn, 0, XmlTextPrelim::new(""));
//!
//! let bold = Attrs::from([("b".into(), true.into())]);
//! let italic = Attrs::from([("i".into(), true.into())]);
//!
//! xml.insert(&mut txn, 0, "hello ");
//! xml.insert_with_attributes(&mut txn, 6, "world", italic);
//! xml.format(&mut txn, 0, 5, bold);
//!
//! assert_eq!(xml.get_string(&txn), "helloworld");
//!
//! // remove formatting
//! let remove_italic = Attrs::from([("i".into(), Any::Null)]);
//! xml.format(&mut txn, 6, 5, remove_italic);
//!
//! assert_eq!(xml.get_string(&txn), "hello world");
//!
//! // insert binary payload eg. images
//! let image = b"deadbeaf".to_vec();
//! xml.insert_embed(&mut txn, 1, image);
//!
//! // insert nested shared type eg. table as ArrayRef of ArrayRefs
//! let table = xml.insert_embed(&mut txn, 5, ArrayPrelim::default());
//! let header = table.insert(&mut txn, 0, ArrayPrelim::from(["Book title", "Author"]));
//! let row = table.insert(&mut txn, 1, ArrayPrelim::from(["\"Moby-Dick\"", "Herman Melville"]));
//! ```
//!
//! Keep in mind that this kind of special content may not be displayed using standard methods
//! ([TextRef::get_string] returns only inserted text and ignores other content, while
//! [XmlTextRef::get_string] renders formatting attributes as XML nodes, but still ignores embedded
//! values). Reason behind this behavior is that as generic collaboration library, Yrs cannot make
//! opinionated decisions in this regard - whenever a full collection of text chunks, formatting
//! attributes or embedded items is required, use [Text::diff] instead.
//!
//! # Cursor positioning
//!
//! Another common problem of collaborative text editors is a requirement of keeping track of cursor
//! position in the face of concurrent updates incoming from remote peers. Let's present the problem
//! on following example:
//!
//! ```rust
//! use yrs::{Doc, GetString, ReadTxn, StateVector, Text, Transact, Update};
//! use yrs::updates::decoder::Decode;
//!
//! let doc1 = Doc::with_client_id(1);
//! let text1 = doc1.get_or_insert_text("article");
//! let mut txn1 = doc1.transact_mut();
//! text1.insert(&mut txn1, 0, "hello");
//!
//! let doc2 = Doc::with_client_id(2);
//! let text2 = doc2.get_or_insert_text("article");
//! let mut txn2 = doc2.transact_mut();
//! text2.insert(&mut txn2, 0, "world");
//!
//! const INDEX: usize = 1;
//!
//! // Doc 2: cursor at index 1 points to character 'o'
//! let str = text2.get_string(&txn2);
//! assert_eq!(str.chars().nth(INDEX), Some('o'));
//!
//! // synchronize full state of doc1 -> doc2
//! txn2.apply_update(Update::decode_v1(&txn1.encode_diff_v1(&StateVector::default())).unwrap());
//!
//! // Doc 2: cursor at index 1 no longer points to the same character
//! let str = text2.get_string(&txn2);
//! assert_ne!(str.chars().nth(INDEX), Some('o'));
//! ```
//!
//! Since [TransactionMut::apply_update] merges updates performed by remote peer, some of these
//! them may shift the cursor position. However in such case the old index that we used (`1` in the
//! example above) is no longer valid.
//!
//! To address these issues, we can make use of [StickyIndex] struct to save the permanent
//! location, that will persist between concurrent updates being made:
//!
//! ```rust
//! use yrs::{Assoc, Doc, GetString, ReadTxn, IndexedSequence, StateVector, Text, Transact, Update};
//! use yrs::updates::decoder::Decode;
//!
//! let doc1 = Doc::with_client_id(1);
//! let text1 = doc1.get_or_insert_text("article");
//! let mut txn1 = doc1.transact_mut();
//! text1.insert(&mut txn1, 0, "hello");
//!
//! let doc2 = Doc::with_client_id(2);
//! let text2 = doc2.get_or_insert_text("article");
//! let mut txn2 = doc2.transact_mut();
//! text2.insert(&mut txn2, 0, "world");
//!
//! const INDEX: usize = 1;
//!
//! // Doc 2: cursor at index 1 points to character 'o'
//! let str = text2.get_string(&txn2);
//! assert_eq!(str.chars().nth(INDEX), Some('o'));
//!
//! // get a permanent index for cursor at index 1
//! let pos = text2.sticky_index(&mut txn2, INDEX as u32, Assoc::After).unwrap();
//!
//! // synchronize full state of doc1 -> doc2
//! txn2.apply_update(Update::decode_v1(&txn1.encode_diff_v1(&StateVector::default())).unwrap());
//!
//! // restore the index from position saved previously
//! let idx = pos.get_offset(&txn2).unwrap();
//! let str = text2.get_string(&txn2);
//! assert_eq!(str.chars().nth(idx.index as usize), Some('o'));
//! ```
//!
//! [StickyIndex] structure is serializable and can be persisted or passed over the network as
//! well, which may help with tracking and displaying the cursor location of other peers.
//!
//! # Weak links and quotations
//!
//! This functionality requires a "weak" feature flag to be turned on:
//! ```toml
//! yrs = { version = "0.17", features = ["weak"] }
//! ```
//!
//! Yrs document structure can be represented as a tree of elements. That means that usually a node
//! can have only one parent and cannot be referenced by any other node. This can be changed by
//! usage of weak links - they offer you a way to reference to values existing in other parts of
//! the document (also other collections):
//!
//! - [Map] elements can be references via [Map::link] method.
//! - Other collections like text and arrays, can quote entire ranges of values via [Quotable::quote]
//! method.
//!
//! Both of these methods return a [WeakPrelim] struct can be integrated as an input value in other
//! collections and convert into [WeakRef] shared type.
//!
//! ```rust
//! use yrs::{Doc, Text, Transact, GetString, Quotable, Map};
//!
//! let doc = Doc::new();
//! let text = doc.get_or_insert_text("text");
//! let map = doc.get_or_insert_map("map");
//! let mut txn = doc.transact_mut();
//! text.insert(&mut txn, 0, "hello!");
//! let quote = text.quote(&txn, 0..5).unwrap();
//! let quote = map.insert(&mut txn, "title", quote);
//!
//! // retrieve quoted text fragment
//! assert_eq!(quote.get_string(&txn), "hello".to_string());
//!
//! // quotations are actively reacting to changes happening at source within quoted range
//! text.insert(&mut txn, 5, " world");
//! // since quoted range 0..5 was right-side exclusive, index 5 itself is not included in range,
//! // but inserts between position 4 and 5 are
//! assert_eq!(quote.get_string(&txn), "hello world".to_string());
//! ```
//!
//! Weak refs also expose observer API that allows to subscribe to changes happening in source
//! collections within quoted range.
//!
//! Keep in mind that weak refs don't maintain ownership over quoted elements. If a source
//! collection removes a quoted element, it will no longer be accessible from weak ref:
//!
//! ```rust
//! use yrs::{Doc, Transact, Quotable, Map};
//!
//! let doc = Doc::new();
//! let map = doc.get_or_insert_map("map");
//! let mut txn = doc.transact_mut();
//! map.insert(&mut txn, "origin", "value");
//! // establish a link 'origin' entry
//! let link = map.link(&txn, "origin").unwrap();
//! let link = map.insert(&mut txn, "link", link);
//! let linked_value: String = link.try_deref(&txn).unwrap();
//! assert_eq!(linked_value, "value".to_string());
//!
//! // remove original value
//! map.remove(&mut txn, "origin");
//! let linked_value = link.try_deref_value(&txn);
//! assert_eq!(linked_value, None); // linked value is no longer accessible
//! ```
//!
//! # Undo/redo
//!
//! Among very popular features of many user-facing applications is an ability to revert/reapply
//! operations performed by user. This becomes even more complicated, once we consider multiple peers
//! collaborating on the same document, as we may need to skip over the changes synchronized from
//! remote peers - even thou they could have happened later - in order to only undo our own actions.
//! [UndoManager] is a Yrs response for these needs, supporting wide variety of options:
//!
//! ```rust
//! use yrs::{Doc, GetString, ReadTxn, Text, Transact, UndoManager, Update};
//! use yrs::undo::Options;
//! use yrs::updates::decoder::Decode;
//!
//! let local = Doc::with_client_id(123);
//! let text1 = local.get_or_insert_text("article");
//! let mut mgr = UndoManager::with_scope_and_options(&local, &text1, Options::default());
//! mgr.include_origin(local.client_id()); // only track changes originating from local peer
//!
//! let remote = Doc::with_client_id(321);
//! let text2 = remote.get_or_insert_text("article");
//!
//! // perform changes locally
//! text1.push(&mut local.transact_mut_with(local.client_id()), "hello ");
//! mgr.reset(); // prevent previous and next operation to be treated by Undo manager as one batch
//! text1.push(&mut local.transact_mut_with(local.client_id()), "world");
//! assert_eq!(text1.get_string(&local.transact()), "hello world");
//!
//! // perform remote changes - these are not being tracked by mgr
//! {
//! let mut remote_txn = remote.transact_mut_with(remote.client_id());
//! text2.push(&mut remote_txn, "everyone");
//! assert_eq!(text2.get_string(&remote_txn), "everyone");
//! }
//!
//! // sync changes from remote to local
//! let update = remote.transact().encode_state_as_update_v1(&local.transact().state_vector());
//! local.transact_mut().apply_update(Update::decode_v1(&update).unwrap());
//! assert_eq!(text1.get_string(&local.transact()), "hello worldeveryone"); // remote changes synced
//!
//! // undo last performed change on local
//! mgr.undo_blocking();
//! assert_eq!(text1.get_string(&local.transact()), "hello everyone");
//!
//! // redo change we undone
//! mgr.redo_blocking();
//! assert_eq!(text1.get_string(&local.transact()), "hello worldeveryone");
//! ```
//!
//! > Keep in mind, that in order to serve its purpose, undo manager may need to implicitly create
//! transactions over underlying [Doc] - for that reason make sure that no other transaction is
//! active while calling methods like [UndoManager::undo] or [UndoManager::redo].
//!
//! It's important to understand a context, in which [UndoManager] operates:
//!
//! - **origins** can be specific classifiers attached to read-write transactions upon creation (see:
//! [Transact::transact_mut_with]). Undo manager can [include](UndoManager::include_origin) any
//! number of origins to its track scope. By default no origin is specified: in such case undo
//! manager will track all changes without looking at transaction origin.
//! - **scope** refers to shared collection references such as [TextRef], [MapRef] etc. Undo manager
//! will only track operations performed on tracked scope refs. By default at least one such
//! reference must be specified, but they can be [expanded](UndoManager::expand_scope) to include
//! multiple collections at once if necessary.
//!
//! Notice, that undo/redo changes are **not** mapped 1-1 on the update batches committed by
//! transactions. As an example: a frequent case includes establishing a new transaction for every
//! user key stroke. Meanwhile we may decide to use different granularity of undo/redo actions.
//! These are grouped together on time-based ranges (configurable in [undo::Options], which is
//! 500ms by default). You can also set them explicitly by calling [UndoManager::reset] method.
//!
//! # Showing past revisions of the document
//!
//! Another feature of Yrs is an ability to snapshot and recover versions of the document,
//! as well as show the differences between them:
//!
//! ```rust
//! use yrs::{Doc, GetString, Options, ReadTxn, Text, Transact, Update, WriteTxn, XmlFragment, XmlTextPrelim};
//! use yrs::types::Attrs;
//! use yrs::types::text::{Diff, YChange};
//! use yrs::updates::decoder::Decode;
//! use yrs::updates::encoder::{Encoder, EncoderV1};
//!
//! let doc = Doc::with_options(Options {
//! skip_gc: true, // in order to support revisions we cannot garbage collect deleted blocks
//! ..Options::default()
//! });
//! let mut txn = doc.transact_mut();
//! let f = txn.get_or_insert_xml_fragment("article");
//! let text = f.insert(&mut txn, 0, XmlTextPrelim::new(""));
//!
//! const INIT: &str = "hello world";
//! text.push(&mut txn, INIT);
//!
//! // save current state of the document: "hello world"
//! let rev = txn.snapshot();
//!
//! // change document state
//! let italic = Attrs::from([("i".into(), true.into())]);
//! text.format(&mut txn, 6, 5, italic.clone());
//! text.remove_range(&mut txn, 2, 3);
//! assert_eq!(text.get_string(&txn), "he world");
//!
//! // encode past version of the document
//! let mut encoder = EncoderV1::new();
//! txn.encode_state_from_snapshot(&rev, &mut encoder).unwrap();
//! let update = encoder.to_vec();
//!
//! // restore the past state
//! let doc = Doc::new();
//! let mut txn = doc.transact_mut();
//! let f = txn.get_or_insert_xml_fragment("article");
//! txn.apply_update(Update::decode_v1(&update).unwrap());
//! let text = f.get(&mut txn, 0).unwrap().into_xml_text().unwrap();
//!
//! assert_eq!(text.get_string(&txn), INIT);
//! ```
//!
//! Keep in mind that an update created from past snapshot via [TransactionMut::encode_state_from_snapshot]
//! doesn't contain updates that happened after that snapshot. What does that mean? While you can
//! continue making new updates on top of that revision, they will be no longer compatible with any
//! changes made on the original document since the snapshot has been made, therefore [TransactionMut::apply_update]
//! on updates generated between two document revisions that branched their state is no longer possible.
//!
//! For the reason above main use case of this feature is rendering read-only state of the [Doc]
//! or restoring document from its past state ie. when current state has been irrecoverably corrupted.
//!
//! # Other shared types
//!
//! So far we only discussed rich text oriented capabilities of Yrs. However, it's possible to make
//! use of Yrs to represent any tree-like object:
//!
//! - [ArrayRef] can be used to represent any indexable sequence of values. If there are multiple
//! peers inserting values at the same position, a [ClientID] will be used to determine a final
//! deterministic order once all peers get in sync.
//! - [MapRef] is a map object (with keys limited to be strings), where values can be of any given
//! type. If there are multiple peers updating the same entry concurrently - creating an update
//! conflict in the result - Yrs will prioritize update belonging to a peer with higher [ClientID]
//! to make conflict resolution algorithm deterministic.
//! - Yrs also provides support fo XML nodes in form of [XmlElementRef], [XmlTextRef] and [XmlFragmentRef].
//!
//! Underneath all of these types are represented by the same abstract [types::Branch] type. Each
//! branch is always capable of working as both indexed sequence of elements and a map. In practice
//! specialized shared types are actually projections over branch type and can be used interchangeably
//! if needed, i.e.: [XmlElementRef] can be also interpreted as [MapRef], in which case the collection
//! of that XML node attributes become key-value entries of reinterpreted map's.
//!
//! # Preliminary vs Integrated types
//!
//! In Yrs core library, every shared type has 2 representations:
//!
//! - **Integrated type** (eg. [TextRef], [ArrayRef], [MapRef]) represents a reference that has already
//! been attached to its parent [Doc]. As such, its state is tracked as part of that document, it
//! can be modified concurrently by multiple peers and any conflicts that occurred due to such
//! actions will be automatically resolved accordingly to [YATA conflict resolution algorithm](https://www.researchgate.net/publication/310212186_Near_Real-Time_Peer-to-Peer_Shared_Editing_on_Extensible_Data_Types).
//! - **Preliminary type** (eg. [TextPrelim], [ArrayPrelim], [MapPrelim]) represents a content that
//! we want to eventually turn into an integrated reference, but it has not been integrated yet.
//!
//! Whenever we want to nest shared types one into another - using methods such as [Array::insert],
//! [Map::insert] or [Text::insert_embed] - we always must do so using preliminary types. These
//! methods will return an integrated representation of the preliminary content we wished to integrate.
//!
//! Keep in mind that we cannot integrate references that have been already integrated - neither in
//! the same document nor in any other one. Yjs/Yrs doesn't allow to have the same object to be linked
//! in multiple places. Same rule concerns primitive types (they will eventually be serialized and
//! deserialized as unique independent objects), integrated types or sub-documents.
//!
//! # Shared collection hooks
//!
//! While type like [TextRef], [MapRef] etc. refer to addresses of objects living in memory,
//! sometime you'd like to be able to uniquely identify the same collection across its different
//! replicas living on other peers. This is possible via hooks:
//!
//! ```rust
//! use yrs::{Array, ArrayRef, Doc, Hook, MapPrelim, ReadTxn, RootRef, SharedRef, Transact, Update};
//! use yrs::types::ToJson;
//! use yrs::updates::decoder::Decode;
//!
//! // create a logical identifier to a root type
//! let root = ArrayRef::root("root");
//!
//! let local = Doc::with_client_id(1);
//! let local_array = root.get_or_create(&mut local.transact_mut());
//! assert_eq!(local_array.hook(), Hook::from(root.clone())); // another way to get hook for existing type
//!
//! let local_map = local_array.push_back(&mut local.transact_mut(), MapPrelim::from([("key", "old")]));
//! let nested = local_map.hook(); // logical identifier to a nested shared type
//!
//! let remote = Doc::with_client_id(2);
//! let remote_array = root.get_or_create(&mut local.transact_mut());
//! // we haven't synchronized yet, so nested element doesn't exist on remote
//! assert!(nested.get(&remote.transact()).is_none());
//!
//! // synchronize the changes
//! {
//! let mut txn = remote.transact_mut();
//! let update = local.transact().encode_state_as_update_v1(&txn.state_vector());
//! txn.apply_update(Update::decode_v1(&update).unwrap());
//! }
//!
//! // after synchronizing, we can now instantiate instance of the same logical type
//! let remote_map = nested.get(&remote.transact()).unwrap();
//! assert_eq!(local_map.hook(), remote_map.hook());
//! assert_eq!(local_map.to_json(&local.transact()), remote_map.to_json(&remote.transact()));
//! ```
//!
//! Logical references are serializable. They also can help to avoid segfaults in cases when we hold
//! an unsafe reference to a nested collection that has been already deleted by concurrent operation.
//!
//! # Transaction event lifecycle
//!
//! Yrs provides a variety of lifecycle events, which enable users to react on various situations
//! and changes performed. Some of these events are used by Yrs own features (e.g. [UndoManager]).
//! They are always triggered once performed update is committed by dropping or
//! [committing](TransactionMut::commit) a read-write transaction.
//!
//! An order in which these updates are fired is as follows:
//!
//! 1. Observers on updated shared types: [TextRef::observe], [ArrayRef::observe], [MapRef::observe],
//! [XmlTextRef::observe], [XmlFragmentRef::observe] and [XmlElementRef::observe].
//! 2. [Deep observers](DeepObservable::observe_deep) (special kind of observers that are bubbled up
//! from nested shared types through their parent collections hierarchy).
//! 3. After transaction callbacks: [Doc::observe_after_transaction].
//! 4. After transaction cleanup callbacks (moment after all changes performed by transaction have
//! been compressed an integrated into document store): [Doc::observe_transaction_cleanup].
//! 5. Update callbacks: [Doc::observe_update_v1] and [Doc::observe_update_v2]. Useful when we want
//! to encode and propagate incremental changes made by transaction to other peers.
//! 6. Sub-document change callbacks: [Doc::observe_subdocs].
//!
//! # Update encoding v1 vs. v2
//!
//! Yrs ships with so called lib0 encoding, which offers two different variants, both of which are
//! compatible with [Yjs](https://docs.yjs.dev/) and can be used to bridge between applications
//! written in different languages using Yrs underneath. They offer a highly compact format,
//! that aims to produce small binary payload size and high encoding/decoding speed.
//!
//! V1 encoding is the default one and should be preferred most of the time. V2 encoding aims to
//! perform payload size optimizations whenever multiple updates are being encoded and passed
//! together (e.g. when you want to serialize an entire document state), however for small updates
//! (e.g. sending individual user keystrokes) V2 may turn out to be less optimal than its V1
//! equivalent.
//!
//! # Awareness and y-sync protocol
//!
//! Sometimes it's not always feasible to keep all state changes related to a document within a
//! document update history - especially if they are notifications bound to a single user or session.
//! For this reason most of the editor bindings using Yjs supplement the document state with
//! [sync::Awareness] - it's a separate entity that keeps user related data such as username
//! or cursor position, but without the overhead of keeping metadata necessary for conflict
//! resolution.
//!
//! [sync::Awareness] together with [Doc] updates combined create basis of y-sync [sync::Protocol].
//! It's used by most of the network providers in Yjs/Yrs ecosystem and enables universal way of
//! communicating between different systems.
//!
//! In its core y-sync protocol can operate as a simple state machine that serves exchanging and
//! responding to different message types described by the protocol. The [sync::DefaultProtocol]
//! provides all the message handlers necessary to make basic communication possible.
//!
//! y-sync protocol is extensible leaves a space for the users to define their own messages if
//! necessary:
//!
//! ```rust
//! use yrs::sync::{Awareness, Message, Protocol, Error};
//!
//! struct MyProtocol;
//! impl Protocol for MyProtocol {
//! fn missing_handle(&self, awareness: &Awareness, tag: u8, data: Vec) -> Result