Yjs

点对点共享类型。「Peer-to-peer shared types」

Github stars Tracking Chart

Yjs

一个具有强大的共享数据抽象的 CRDT 框架。

Yjs 是一个 CRDT 实现,它以共享类型的形式公开其内部数据结构。共享类型是像 Map 或 Array 这样常见的数据类型,具有超强的功能:更改会自动分发到其他对等方,并在没有合并冲突的情况下进行合并。

Yjs 是网络不可知的(p2p!),支持许多现有的富文本编辑器,离线编辑,版本快照,撤销/重做和共享游标。它的用户数量不受限制,并且非常适合于大型文档。

如果你正在寻找专业的(付费)支持来构建协作式或分布式应用,请发邮件给我们:yjs@tag1consulting.com。否则,你可以在我们的讨论区找到帮助。

谁在使用 Yjs

  • Relm 一个团队合作和社区的协作游戏世界。
  • nput 一个协作记事的应用。
  • oom.sh 一款集成了协作绘图、编辑和编码工具的会议应用程序。
  • http://coronavirustechhandbook.com/ 一个由数千名不同的人编辑的合作维基,致力于快速和复杂地应对冠状病毒的爆发和后续影响。
  • Nimbus Note 一个由 Nimbus Web 设计的笔记应用。
  • JoeDocs 一个开放的协作式维基。
  • Pluxbox RadioManager 一个基于 Web 的应用程序,用于协作组织电台广播。
  • Cattaz 一个可以在 wiki 页面中运行自定义应用程序的 wiki。

概述

此存储库包含一个共享类型的集合,可以观察这些类型的更改并同时进行操作。网络功能和双向绑定在单独的模块中实现。

绑定(Bindings)

名称 光标 绑定 演示
ProseMirror heavy_check_mark y-prosemirror demo
Quill heavy_check_mark y-quill demo
CodeMirror heavy_check_mark y-codemirror demo
Monaco heavy_check_mark y-monaco demo

提供商(Providers)

设置客户之间的沟通,管理意识信息,以及存储共享数据以供离线使用是相当麻烦的。提供商为您管理这一切,是您的协作应用的完美起点。

y-webrtc
使用 WebRTC 点对点传播文件更新。点对点通过信令服务器交换信令数据。可以使用公开的信令服务器。通过信令服务器进行的通信可以通过提供共享秘密进行加密,保持连接信息和共享文档的私密性。
y-websocket
包含一个简单的 websocket 后端和一个连接到该后端的 websocket 客户端的模块。后台可以扩展到 leveldb 数据库中持久化更新。
y-indexeddb
有效地将文档更新坚持到浏览器的 indexeddb 数据库中。文档立即可用,只需要通过网络提供商同步差异。
y-dat
[WIP]使用 multifeed 将文档更新有效地写入 dat 网络。每个客户机都有一个仅附加的 CRDT 本地更新日志(hypercore)。multified 管理和同步超线程,y-dat 监听更改并将其应用于 Yjs 文档。

入门

用你喜欢的软件包管理器安装 Yjs 和一个提供商。

npm i yjs y-websocket

启动 y-websocket 服务器

PORT=1234 node ./node_modules/y-websocket/bin/server.js

示例:观察类型(Observe types)

const yarray = doc.getArray('my-array')
yarray.observe(event => {
  console.log('yarray was modified')
})
// 每次本地或远程客户端修改 yarray 时,都会调用观察者
yarray.insert(0, ['val']) // => "yarray was modified"

示例:嵌套类型(Nest types)

记住,共享类型只是普通的数据类型。唯一的限制是,一个共享类型在共享文档中只能存在一次。

const ymap = doc.getMap('map')
const foodArray = new Y.Array()
foodArray.insert(0, ['apple', 'banana'])
ymap.set('food', foodArray)
ymap.get('food') === foodArray // => true
ymap.set('fruit', foodArray) // => Error! foodArray is already defined

现在你明白了如何在共享文档上定义类型了。接下来你可以跳转到 演示仓库 或继续阅读 API 文档。

示例:使用和组合提供者

任何一个 Yjs 供应商都可以相互组合。所以你可以通过不同的网络技术来同步数据。

在大多数情况下,你希望将网络提供者(如y-websocket或y-webrtc)与持久化提供者(浏览器中的y-indexeddb)结合使用。持久性允许你更快地加载文档,并持久化离线时创建的数据。

在本演示中,我们将两个不同的网络提供者与一个持久性提供者结合起来。

import * as Y from 'yjs'
import { WebrtcProvider } from 'y-webrtc'
import { WebsocketProvider } from 'y-websocket'
import { IndexeddbPersistence } from 'y-indexeddb'
const ydoc = new Y.Doc()
// 这允许您立即获取(缓存)文档数据
const indexeddbProvider = new IndexeddbPersistence('count-demo', ydoc)
indexeddbProvider.whenSynced.then(() => {
  console.log('loaded data from indexed db')
})
// 将客户端与 y-webrtc provider 程序同步。
const webrtcProvider = new WebrtcProvider('count-demo', ydoc)
// Sync clients with the y-websocket provider
const websocketProvider = new WebsocketProvider(
  'wss://demos.yjs.dev', 'count-demo', ydoc
)
// 产生和的数的数组
const yarray = ydoc.getArray('count')
// 观察总数的变化
yarray.observe(event => {
  // print updates when the data changes
  console.log('new sum: ' + yarray.toArray().reduce((a,b) => a + b))
})
// 总和加1
yarray.push([1]) // => "new sum: 1"

API

import * as Y from 'yjs'

(恕删略。请参考自述文件)

Yjs CRDT 算法

用于协作编辑的无冲突复制数据类型(CRDT)是操作转换(OT)的另一种方法。这两种方法的一个非常简单的区别是,OT 试图转换索引位置以确保收敛(所有客户端最终得到的是相同的内容),而 CRDT 使用数学模型,通常不涉及索引转换,如链接列表。OT 是目前文本上共享编辑的事实标准。支持共享编辑的 OT 方法如果没有一个中心真理源(中心服务器),就需要太多的记账工作,在实践中是不可行的。CRDTs 更适合分布式系统,它提供了额外的保证,即文档可以与远程客户端同步,并且不需要中央真实源。

Yjs 实现了 本论文 所述算法的修改版本。本文解释了在 CRDT 模型上的简单优化,并对 Yjs 中的性能特性做了更多的介绍。更多关于具体实现的信息可以在 INTERNALS.md本篇 Yjs 代码库的演练中获得。

适合共享文本编辑的 CRDT 的苦恼是,它们的体积只会越来越大。有的 CRDT 虽然不会变大,但却不具备共享文本编辑所需的特性(如意图保存)。Yjs 在原有算法的基础上做了很多改进,减少了文档只增长大小的折衷。我们不能在保证结构的唯一顺序的同时,对删除的结构(墓碑)进行垃圾回收。但是我们可以:1.将前面的结构合并到一个结构中,以减少元信息量;2.如果结构被删除,我们可以从结构中删除内容;3.如果我们不再关心结构的顺序,我们可以垃圾回收墓碑(例如,如果父结构被删除)。

举个例子:

如果用户按顺序插入元素,结构将被合并成一个结构。例如:array.insert(0, ['a']), array.insert(0, ['b']); 首先表示为两个结构([{id: {client, clock: 0}, content: 'a'}, {id: {client, clock: 1}, content: 'b'}),然后合并成一个结构。[{id: {client, clock: 0}, content: 'ab'}]。

当一个包含内容的结构(如 ItemString)被删除时,该结构将被一个不再包含内容的 ItemDeleted 代替。

当一个类型被删除时,所有的子元素都会转化为 GC 结构。一个 GC 结构只表示一个结构的存在以及它被删除。如果 id 相邻,GC 结构总是可以与其他 GC 结构合并。
特别是在处理结构化内容时(例如在 ProseMirror 上的共享编辑),当对随机文档编辑进行基准测试时,这些改进会产生非常好的结果。在实践中,它们显示出了更好的结果,因为用户通常会按顺序编辑文本,导致结构很容易被合并。基准测试表明,即使在最坏的情况下,用户从右到左编辑文本,Yjs 也能实现良好的性能,即使是巨大的文档。

状态向量

Yjs 有能力在同步两个客户端时只交换差异。我们使用 lamport 时间戳来识别 structs,并跟踪客户端创建它们的顺序。每个结构都有一个 struct.id = { client: number, clock: number},它唯一地标识一个结构。我们将每个客户端的下一个预期时钟定义为状态向量。这个数据结构类似于版本向量数据结构。但是我们只用状态向量来描述本地文档的状态,所以我们可以计算远程客户端丢失的结构。我们不使用它来跟踪因果关系。

许可和作者

Yjs 和所有相关项目都是 MIT 授权

Yjs 是基于我在 RWTH i5 的学生时代的研究。现在我在业余时间从事 Yjs 的研究。
通过在 GitHub Sponsors 上捐款来资助这个项目,或者雇佣作为你的合作应用的承包商。


(The first version translated by vz on 2020.10.03)

Overview

Name With Owneryjs/yjs
Primary LanguageJavaScript
Program languageJavaScript (Language Count: 2)
PlatformLinux, Mac, Windows
License:Other
Release Count312
Last Release Namev13.6.15 (Posted on 2024-04-27 00:50:32)
First Release Namev0.0.0 (Posted on )
Created At2014-07-29 19:29:45
Pushed At2024-05-02 06:17:14
Last Commit At2024-04-12 16:00:14
Stargazers Count15.2k
Watchers Count119
Fork Count557
Commits Count1.8k
Has Issues Enabled
Issues Count438
Issue Open Count88
Pull Requests Count140
Pull Requests Open Count9
Pull Requests Close Count53
Has Wiki Enabled
Is Archived
Is Fork
Is Locked
Is Mirror
Is Private

Yjs

A CRDT framework with a powerful abstraction of shared data

Yjs is a CRDT implementation that exposes its internal
data structure as shared types. Shared types are common data types like Map
or Array with superpowers: changes are automatically distributed to other
peers and merged without merge conflicts.

Yjs is network agnostic (p2p!), supports many existing rich text
editors
, offline editing, version snapshots, undo/redo and
shared cursors. It scales well with an unlimited number of users and is well
suited for even large documents.

:warning: This is the documentation for v13 (still in alpha). For the stable v12
release checkout the v12 docs :warning:

Table of Contents

Overview

This repository contains a collection of shared types that can be observed for
changes and manipulated concurrently. Network functionality and two-way-bindings
are implemented in separate modules.

Bindings

To the top