@y/y 14.0.0-16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +1406 -0
  3. package/dist/Skip-j0kX7pdq.js +12173 -0
  4. package/dist/Skip-j0kX7pdq.js.map +1 -0
  5. package/dist/Skip-wRT7BKFP.js +11877 -0
  6. package/dist/Skip-wRT7BKFP.js.map +1 -0
  7. package/dist/index-DyTeTfmj.js +163 -0
  8. package/dist/index-DyTeTfmj.js.map +1 -0
  9. package/dist/index-R7GxO-36.js +165 -0
  10. package/dist/index-R7GxO-36.js.map +1 -0
  11. package/dist/internals.cjs +286 -0
  12. package/dist/internals.cjs.map +1 -0
  13. package/dist/internals.mjs +25 -0
  14. package/dist/internals.mjs.map +1 -0
  15. package/dist/src/index.d.ts +2 -0
  16. package/dist/src/index.d.ts.map +1 -0
  17. package/dist/src/internals.d.ts +43 -0
  18. package/dist/src/internals.d.ts.map +1 -0
  19. package/dist/src/structs/AbstractStruct.d.ts +42 -0
  20. package/dist/src/structs/AbstractStruct.d.ts.map +1 -0
  21. package/dist/src/structs/ContentAny.d.ts +67 -0
  22. package/dist/src/structs/ContentAny.d.ts.map +1 -0
  23. package/dist/src/structs/ContentBinary.d.ts +64 -0
  24. package/dist/src/structs/ContentBinary.d.ts.map +1 -0
  25. package/dist/src/structs/ContentDeleted.d.ts +64 -0
  26. package/dist/src/structs/ContentDeleted.d.ts.map +1 -0
  27. package/dist/src/structs/ContentDoc.d.ts +72 -0
  28. package/dist/src/structs/ContentDoc.d.ts.map +1 -0
  29. package/dist/src/structs/ContentEmbed.d.ts +67 -0
  30. package/dist/src/structs/ContentEmbed.d.ts.map +1 -0
  31. package/dist/src/structs/ContentFormat.d.ts +69 -0
  32. package/dist/src/structs/ContentFormat.d.ts.map +1 -0
  33. package/dist/src/structs/ContentJSON.d.ts +70 -0
  34. package/dist/src/structs/ContentJSON.d.ts.map +1 -0
  35. package/dist/src/structs/ContentString.d.ts +70 -0
  36. package/dist/src/structs/ContentString.d.ts.map +1 -0
  37. package/dist/src/structs/ContentType.d.ts +83 -0
  38. package/dist/src/structs/ContentType.d.ts.map +1 -0
  39. package/dist/src/structs/GC.d.ts +31 -0
  40. package/dist/src/structs/GC.d.ts.map +1 -0
  41. package/dist/src/structs/Item.d.ts +212 -0
  42. package/dist/src/structs/Item.d.ts.map +1 -0
  43. package/dist/src/structs/Skip.d.ts +33 -0
  44. package/dist/src/structs/Skip.d.ts.map +1 -0
  45. package/dist/src/types/AbstractType.d.ts +239 -0
  46. package/dist/src/types/AbstractType.d.ts.map +1 -0
  47. package/dist/src/types/YArray.d.ts +128 -0
  48. package/dist/src/types/YArray.d.ts.map +1 -0
  49. package/dist/src/types/YMap.d.ts +112 -0
  50. package/dist/src/types/YMap.d.ts.map +1 -0
  51. package/dist/src/types/YText.d.ts +216 -0
  52. package/dist/src/types/YText.d.ts.map +1 -0
  53. package/dist/src/types/YXmlElement.d.ts +106 -0
  54. package/dist/src/types/YXmlElement.d.ts.map +1 -0
  55. package/dist/src/types/YXmlFragment.d.ts +143 -0
  56. package/dist/src/types/YXmlFragment.d.ts.map +1 -0
  57. package/dist/src/types/YXmlHook.d.ts +32 -0
  58. package/dist/src/types/YXmlHook.d.ts.map +1 -0
  59. package/dist/src/types/YXmlText.d.ts +34 -0
  60. package/dist/src/types/YXmlText.d.ts.map +1 -0
  61. package/dist/src/utils/AbstractConnector.d.ts +20 -0
  62. package/dist/src/utils/AbstractConnector.d.ts.map +1 -0
  63. package/dist/src/utils/AttributionManager.d.ts +224 -0
  64. package/dist/src/utils/AttributionManager.d.ts.map +1 -0
  65. package/dist/src/utils/Doc.d.ts +267 -0
  66. package/dist/src/utils/Doc.d.ts.map +1 -0
  67. package/dist/src/utils/EventHandler.d.ts +19 -0
  68. package/dist/src/utils/EventHandler.d.ts.map +1 -0
  69. package/dist/src/utils/ID.d.ts +26 -0
  70. package/dist/src/utils/ID.d.ts.map +1 -0
  71. package/dist/src/utils/IdMap.d.ts +161 -0
  72. package/dist/src/utils/IdMap.d.ts.map +1 -0
  73. package/dist/src/utils/IdSet.d.ts +163 -0
  74. package/dist/src/utils/IdSet.d.ts.map +1 -0
  75. package/dist/src/utils/RelativePosition.d.ts +91 -0
  76. package/dist/src/utils/RelativePosition.d.ts.map +1 -0
  77. package/dist/src/utils/Snapshot.d.ts +40 -0
  78. package/dist/src/utils/Snapshot.d.ts.map +1 -0
  79. package/dist/src/utils/StructSet.d.ts +27 -0
  80. package/dist/src/utils/StructSet.d.ts.map +1 -0
  81. package/dist/src/utils/StructStore.d.ts +41 -0
  82. package/dist/src/utils/StructStore.d.ts.map +1 -0
  83. package/dist/src/utils/Transaction.d.ts +136 -0
  84. package/dist/src/utils/Transaction.d.ts.map +1 -0
  85. package/dist/src/utils/UndoManager.d.ts +188 -0
  86. package/dist/src/utils/UndoManager.d.ts.map +1 -0
  87. package/dist/src/utils/UpdateDecoder.d.ts +167 -0
  88. package/dist/src/utils/UpdateDecoder.d.ts.map +1 -0
  89. package/dist/src/utils/UpdateEncoder.d.ts +164 -0
  90. package/dist/src/utils/UpdateEncoder.d.ts.map +1 -0
  91. package/dist/src/utils/YEvent.d.ts +120 -0
  92. package/dist/src/utils/YEvent.d.ts.map +1 -0
  93. package/dist/src/utils/delta-helpers.d.ts +6 -0
  94. package/dist/src/utils/delta-helpers.d.ts.map +1 -0
  95. package/dist/src/utils/encoding.d.ts +30 -0
  96. package/dist/src/utils/encoding.d.ts.map +1 -0
  97. package/dist/src/utils/isParentOf.d.ts +3 -0
  98. package/dist/src/utils/isParentOf.d.ts.map +1 -0
  99. package/dist/src/utils/logging.d.ts +3 -0
  100. package/dist/src/utils/logging.d.ts.map +1 -0
  101. package/dist/src/utils/types.d.ts +7 -0
  102. package/dist/src/utils/types.d.ts.map +1 -0
  103. package/dist/src/utils/updates.d.ts +89 -0
  104. package/dist/src/utils/updates.d.ts.map +1 -0
  105. package/dist/testHelper.cjs +780 -0
  106. package/dist/testHelper.cjs.map +1 -0
  107. package/dist/testHelper.mjs +617 -0
  108. package/dist/testHelper.mjs.map +1 -0
  109. package/dist/tests/IdMap.tests.d.ts +9 -0
  110. package/dist/tests/IdMap.tests.d.ts.map +1 -0
  111. package/dist/tests/IdSet.tests.d.ts +9 -0
  112. package/dist/tests/IdSet.tests.d.ts.map +1 -0
  113. package/dist/tests/attribution.tests.d.ts +8 -0
  114. package/dist/tests/attribution.tests.d.ts.map +1 -0
  115. package/dist/tests/compatibility.tests.d.ts +5 -0
  116. package/dist/tests/compatibility.tests.d.ts.map +1 -0
  117. package/dist/tests/delta.tests.d.ts +7 -0
  118. package/dist/tests/delta.tests.d.ts.map +1 -0
  119. package/dist/tests/doc.tests.d.ts +13 -0
  120. package/dist/tests/doc.tests.d.ts.map +1 -0
  121. package/dist/tests/encoding.tests.d.ts +5 -0
  122. package/dist/tests/encoding.tests.d.ts.map +1 -0
  123. package/dist/tests/index.d.ts +2 -0
  124. package/dist/tests/index.d.ts.map +1 -0
  125. package/dist/tests/relativePositions.tests.d.ts +11 -0
  126. package/dist/tests/relativePositions.tests.d.ts.map +1 -0
  127. package/dist/tests/snapshot.tests.d.ts +13 -0
  128. package/dist/tests/snapshot.tests.d.ts.map +1 -0
  129. package/dist/tests/testHelper.d.ts +167 -0
  130. package/dist/tests/testHelper.d.ts.map +1 -0
  131. package/dist/tests/undo-redo.tests.d.ts +27 -0
  132. package/dist/tests/undo-redo.tests.d.ts.map +1 -0
  133. package/dist/tests/updates.tests.d.ts +24 -0
  134. package/dist/tests/updates.tests.d.ts.map +1 -0
  135. package/dist/tests/y-array.tests.d.ts +45 -0
  136. package/dist/tests/y-array.tests.d.ts.map +1 -0
  137. package/dist/tests/y-map.tests.d.ts +45 -0
  138. package/dist/tests/y-map.tests.d.ts.map +1 -0
  139. package/dist/tests/y-text.tests.d.ts +49 -0
  140. package/dist/tests/y-text.tests.d.ts.map +1 -0
  141. package/dist/tests/y-xml.tests.d.ts +15 -0
  142. package/dist/tests/y-xml.tests.d.ts.map +1 -0
  143. package/dist/yjs.cjs +151 -0
  144. package/dist/yjs.cjs.map +1 -0
  145. package/dist/yjs.mjs +26 -0
  146. package/dist/yjs.mjs.map +1 -0
  147. package/package.json +101 -0
  148. package/src/index.js +153 -0
  149. package/src/internals.js +44 -0
  150. package/src/structs/AbstractStruct.js +59 -0
  151. package/src/structs/ContentAny.js +115 -0
  152. package/src/structs/ContentBinary.js +93 -0
  153. package/src/structs/ContentDeleted.js +101 -0
  154. package/src/structs/ContentDoc.js +141 -0
  155. package/src/structs/ContentEmbed.js +98 -0
  156. package/src/structs/ContentFormat.js +105 -0
  157. package/src/structs/ContentJSON.js +119 -0
  158. package/src/structs/ContentString.js +113 -0
  159. package/src/structs/ContentType.js +176 -0
  160. package/src/structs/GC.js +80 -0
  161. package/src/structs/Item.js +845 -0
  162. package/src/structs/Skip.js +75 -0
  163. package/src/types/AbstractType.js +1434 -0
  164. package/src/types/YArray.js +270 -0
  165. package/src/types/YMap.js +244 -0
  166. package/src/types/YText.js +934 -0
  167. package/src/types/YXmlElement.js +227 -0
  168. package/src/types/YXmlFragment.js +266 -0
  169. package/src/types/YXmlHook.js +68 -0
  170. package/src/types/YXmlText.js +66 -0
  171. package/src/utils/AbstractConnector.js +25 -0
  172. package/src/utils/AttributionManager.js +619 -0
  173. package/src/utils/Doc.js +372 -0
  174. package/src/utils/EventHandler.js +87 -0
  175. package/src/utils/ID.js +89 -0
  176. package/src/utils/IdMap.js +629 -0
  177. package/src/utils/IdSet.js +823 -0
  178. package/src/utils/RelativePosition.js +352 -0
  179. package/src/utils/Snapshot.js +220 -0
  180. package/src/utils/StructSet.js +137 -0
  181. package/src/utils/StructStore.js +289 -0
  182. package/src/utils/Transaction.js +489 -0
  183. package/src/utils/UndoManager.js +391 -0
  184. package/src/utils/UpdateDecoder.js +281 -0
  185. package/src/utils/UpdateEncoder.js +320 -0
  186. package/src/utils/YEvent.js +216 -0
  187. package/src/utils/delta-helpers.js +54 -0
  188. package/src/utils/encoding.js +623 -0
  189. package/src/utils/isParentOf.js +21 -0
  190. package/src/utils/logging.js +21 -0
  191. package/src/utils/types.js +28 -0
  192. package/src/utils/updates.js +715 -0
  193. package/tests/testHelper.js +600 -0
package/README.md ADDED
@@ -0,0 +1,1406 @@
1
+
2
+ # ![Yjs](https://yjs.dev/images/logo/yjs-120x120.png)
3
+
4
+ > A CRDT framework with a powerful abstraction of shared data
5
+
6
+ Yjs is a [CRDT implementation](#yjs-crdt-algorithm) that exposes its internal
7
+ data structure as *shared types*. Shared types are common data types like `Map`
8
+ or `Array` with superpowers: changes are automatically distributed to other
9
+ peers and merged without merge conflicts.
10
+
11
+ Yjs is **network agnostic** (p2p!), supports many existing **rich text
12
+ editors**, **offline editing**, **version snapshots**, **undo/redo** and
13
+ **shared cursors**. It scales well with an unlimited number of users and is well
14
+ suited for even large documents.
15
+
16
+ * Demos: [https://github.com/yjs/yjs-demos](https://github.com/yjs/yjs-demos)
17
+ * Discuss: [https://discuss.yjs.dev](https://discuss.yjs.dev)
18
+ * Chat: [Gitter](https://gitter.im/Yjs/community) | [Discord](https://discord.gg/T3nqMT6qbM)
19
+ * Benchmark Yjs vs. Automerge:
20
+ [https://github.com/dmonad/crdt-benchmarks](https://github.com/dmonad/crdt-benchmarks)
21
+ * Podcast [**"Yjs Deep Dive into real time collaborative editing solutions":**](https://www.tag1consulting.com/blog/deep-dive-real-time-collaborative-editing-solutions-tagteamtalk-001-0)
22
+ * Podcast [**"Google Docs-style editing in Gutenberg with the YJS framework":**](https://publishpress.com/blog/yjs/)
23
+
24
+ :construction_worker_woman: If you are looking for professional support, please
25
+ consider supporting this project via a "support contract" on
26
+ [GitHub Sponsors](https://github.com/sponsors/dmonad). I will attend your issues
27
+ quicker and we can discuss questions and problems in regular video conferences.
28
+ Otherwise you can find help on our community [discussion board](https://discuss.yjs.dev).
29
+
30
+ ## Sponsorship
31
+
32
+ Please contribute to the project financially - especially if your company relies
33
+ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%20Sponsor&message=%E2%9D%A4&logo=GitHub&style=flat&color=d42f2d)](https://github.com/sponsors/dmonad)
34
+
35
+ ## Professional Support
36
+
37
+ * [Support Contract with the Maintainer](https://github.com/sponsors/dmonad) -
38
+ By contributing financially to the open-source Yjs project, you can receive
39
+ professional support directly from the author. This includes the opportunity for
40
+ weekly video calls to discuss your specific challenges.
41
+ * [Synergy Codes](https://synergycodes.com/yjs-services/) - Specializing in
42
+ consulting and developing real-time collaborative editing solutions for visual
43
+ apps, Synergy Codes focuses on interactive diagrams, complex graphs, charts, and
44
+ various data visualization types. Their expertise empowers developers to build
45
+ engaging and interactive visual experiences leveraging the power of Yjs. See
46
+ their work in action at [Visual Collaboration
47
+ Showcase](https://yjs-diagram.synergy.codes/).
48
+
49
+ ## Who is using Yjs
50
+
51
+ * [AFFiNE](https://affine.pro/) A local-first, privacy-first, open source
52
+ knowledge base. :star2:
53
+ * [Huly](https://huly.io/) - Open Source All-in-One Project Management Platform :star2:
54
+ * [Cargo](https://cargo.site/) Site builder for designers and artists :star2:
55
+ * [Gitbook](https://gitbook.com) Knowledge management for technical teams :star2:
56
+ * [Evernote](https://evernote.com) Note-taking app :star2:
57
+ * [Lessonspace](https://thelessonspace.com) Enterprise platform for virtual
58
+ classrooms and online training :star2:
59
+ * [Ellipsus](ellipsus.com) - Collaborative writing app for storytelling etc.
60
+ Supports versioning, change attribution, and "blame". A solution for the whole
61
+ publishing process (also selling) :star:
62
+ * [Dynaboard](https://dynaboard.com/) Build web apps collaboratively. :star:
63
+ * [Relm](https://www.relm.us/) A collaborative gameworld for teamwork and
64
+ community. :star:
65
+ * [Room.sh](https://room.sh/) A meeting application with integrated
66
+ collaborative drawing, editing, and coding tools. :star:
67
+ * [Nimbus Note](https://nimbusweb.me/note.php) A note-taking app designed by
68
+ Nimbus Web. :star:
69
+ * [Pluxbox RadioManager](https://getradiomanager.com/) A web-based app to
70
+ collaboratively organize radio broadcasts. :star:
71
+ * [modyfi](https://www.modyfi.com) - Modyfi is the design platform built for
72
+ multidisciplinary designers. Design, generate, animate, and more — without
73
+ switching between apps. :star:
74
+ * [Sana](https://sanalabs.com/) A learning platform with collaborative text
75
+ editing powered by Yjs.
76
+ * [Serenity Notes](https://www.serenity.re/en/notes) End-to-end encrypted
77
+ collaborative notes app.
78
+ * [PRSM](https://prsm.uk/) Collaborative mind-mapping and system visualisation.
79
+ *[(source)](https://github.com/micrology/prsm)*
80
+ * [Alldone](https://alldone.app/) A next-gen project management and
81
+ collaboration platform.
82
+ * [Living Spec](https://livingspec.com/) A modern way for product teams to collaborate.
83
+ * [Slidebeamer](https://slidebeamer.com/) Presentation app.
84
+ * [BlockSurvey](https://blocksurvey.io) End-to-end encryption for your forms/surveys.
85
+ * [Skiff](https://skiff.org/) Private, decentralized workspace.
86
+ * [JupyterLab](https://jupyter.org/) Collaborative computational Notebooks
87
+ * [JupyterCad](https://jupytercad.readthedocs.io/en/latest/) Extension to
88
+ JupyterLab that enables collaborative editing of 3d FreeCAD Models.
89
+ * [JupyterGIS](https://github.com/geojupyter/jupytergis) Collaborative GIS
90
+ (Geographic Information System) editor in Jupyter
91
+ * [Hyperquery](https://hyperquery.ai/) A collaborative data workspace for
92
+ sharing analyses, documentation, spreadsheets, and dashboards.
93
+ * [Nosgestesclimat](https://nosgestesclimat.fr/groupe) The french carbon
94
+ footprint calculator has a group P2P mode based on yjs
95
+ * [oorja.io](https://oorja.io) Online meeting spaces extensible with
96
+ collaborative apps, end-to-end encrypted.
97
+ * [LegendKeeper](https://legendkeeper.com) Collaborative campaign planner and
98
+ worldbuilding app for tabletop RPGs.
99
+ * [IllumiDesk](https://illumidesk.com/) Build courses and content with A.I.
100
+ * [btw](https://www.btw.so) Open-source Medium alternative
101
+ * [AWS SageMaker](https://aws.amazon.com/sagemaker/) Tools for building Machine
102
+ Learning Models
103
+ * [linear](https://linear.app) Streamline issues, projects, and product roadmaps.
104
+ * [Arkiter](https://www.arkiter.com/) - Live interview software
105
+ * [Appflowy](https://www.appflowy.io/) - They use Yrs
106
+ * [Multi.app](https://multi.app) - Multiplayer app sharing: Point, draw and edit
107
+ in shared apps as if they're on your computer. They are using Yrs.
108
+ * [AppMaster](https://appmaster.io) A No-Code platform for creating
109
+ production-ready applications with source code generation.
110
+ * [Synthesia](https://www.synthesia.io) - Collaborative Video Editor
111
+ * [thinkdeli](https://thinkdeli.com) - A fast and simple notes app powered by AI
112
+ * [ourboard](https://github.com/raimohanska/ourboard) - A collaborative whiteboard
113
+ application
114
+ * [Ellie.ai](https://ellie.ai) - Data Product Design and Collaboration
115
+ * [GoPeer](https://gopeer.org/) - Collaborative tutoring
116
+ * [screen.garden](https://screen.garden) - Collaborative backend for PKM apps.
117
+ * [NextCloud](https://nextcloud.com/) - Content Collaboration Platform
118
+ * [keystatic](https://github.com/Thinkmill/keystatic) - git-based CMS
119
+ * [QDAcity](https://qdacity.com) - Collaborative qualitative data analysis platform
120
+ * [Kanbert](https://kanbert.com) - Project management software
121
+ * [Eclipse Theia](https://github.com/eclipse-theia/theia) - A cloud & desktop
122
+ IDE that runs in the browser.
123
+ * [ScienHub](https://scienhub.com) - Collaborative LaTeX editor in the browser.
124
+ * [Open Collaboration Tools](https://www.open-collab.tools/) - Collaborative
125
+ editing for your IDE or custom editor
126
+ * [Typst](https://typst.app/) - Compose, edit, and automate technical documents
127
+ * [Kedyou](https://kedyou.com/) - Digital workspaces for tutoring
128
+ * [Lightpage](https://lightpage.com/) - Personal living notebook
129
+ * [reearth-flow](https://github.com/reearth/reearth-flow) -
130
+ Collaboratively calculate and convert various data
131
+ * [ProtonMail | Proton Docs](https://proton.me/drive/docs) - E2E encrypted
132
+ collaborative documents in Proton Drive.
133
+ * [Theneo](https://www.theneo.io/) - AI-powered API docs with live team collaboration.
134
+
135
+ ## Table of Contents
136
+
137
+ * [Overview](#overview)
138
+ * [Bindings](#bindings)
139
+ * [Providers](#providers)
140
+ * [Tooling](#tooling)
141
+ * [Ports](#ports)
142
+ * [Getting Started](#getting-started)
143
+ * [API](#api)
144
+ * [Shared Types](#shared-types)
145
+ * [Y.Doc](#ydoc)
146
+ * [Document Updates](#document-updates)
147
+ * [Relative Positions](#relative-positions)
148
+ * [Y.UndoManager](#yundomanager)
149
+ * [Yjs CRDT Algorithm](#yjs-crdt-algorithm)
150
+ * [License and Author](#license-and-author)
151
+
152
+ ## Overview
153
+
154
+ This repository contains a collection of shared types that can be observed for
155
+ changes and manipulated concurrently. Network functionality and two-way-bindings
156
+ are implemented in separate modules.
157
+
158
+ ### Bindings
159
+
160
+ | Name | Cursors | Binding | Demo |
161
+ |---|:-:|---|---|
162
+ | [ProseMirror](https://prosemirror.net/)                                                   | ✔ | [y-prosemirror](https://github.com/yjs/y-prosemirror) | [demo](https://demos.yjs.dev/prosemirror/prosemirror.html) |
163
+ | [Quill](https://quilljs.com/) | ✔ | [y-quill](https://github.com/yjs/y-quill) | [demo](https://demos.yjs.dev/quill/quill.html) |
164
+ | [CodeMirror](https://codemirror.net/) | ✔ | [y-codemirror](https://github.com/yjs/y-codemirror) | [demo](https://demos.yjs.dev/codemirror/codemirror.html) |
165
+ | [Monaco](https://microsoft.github.io/monaco-editor/) | ✔ | [y-monaco](https://github.com/yjs/y-monaco) | [demo](https://demos.yjs.dev/monaco/monaco.html) |
166
+ | [Ace](https://ace.c9.io/) | ✔ | [y-ace](https://github.com/bajrangCoder/y-ace) | |
167
+ | [Slate](https://github.com/ianstormtaylor/slate) | ✔ | [slate-yjs](https://github.com/bitphinix/slate-yjs) | [demo](https://bitphinix.github.io/slate-yjs-example) |
168
+ | [BlockSuite](https://github.com/toeverything/blocksuite) | ✔ | (native) | [demo](https://blocksuite-toeverything.vercel.app/?init) |
169
+ | [Lexical](https://lexical.dev/) | ✔ | (native) | [demo](https://lexical.dev/docs/collaboration/react#see-it-in-action) |
170
+ | [BlockNote](https://www.blocknotejs.org/docs/collaboration/real-time-collaboration) | ✔ | [y-prosemirror](https://github.com/yjs/y-prosemirror) | [demo](https://www.blocknotejs.org/docs/collaboration/real-time-collaboration) |
171
+ | [Tiptap](https://tiptap.dev/) | ✔ | [y-prosemirror](https://github.com/yjs/y-prosemirror) | [demo](https://template.tiptap.dev/preview/templates/simple) |
172
+ | [Milkdown](https://github.com/Milkdown/milkdown) | ✔ | [y-prosemirror](https://github.com/yjs/y-prosemirror) | [demo](https://milkdown.dev/playground) |
173
+ | [Superdoc](https://superdoc.dev/) | ✔ | (native) | [demo](https://superdoc.dev/) |
174
+ | [valtio](https://github.com/pmndrs/valtio) | | [valtio-yjs](https://github.com/dai-shi/valtio-yjs) | [demo](https://codesandbox.io/s/valtio-yjs-demo-ox3iy) |
175
+ | [immer](https://github.com/immerjs/immer) | | [immer-yjs](https://github.com/sep2/immer-yjs) | [demo](https://codesandbox.io/s/immer-yjs-demo-6e0znb) |
176
+ | React | | [react-yjs](https://github.com/nikgraf/react-yjs) | [demo](https://react-yjs-example.vercel.app/) |
177
+ | React / Vue / Svelte / MobX | | [SyncedStore](https://syncedstore.org) | [demo](https://syncedstore.org/docs/react) |
178
+ | [mobx-keystone](https://mobx-keystone.js.org/) | | [mobx-keystone-yjs](https://github.com/xaviergonz/mobx-keystone/tree/master/packages/mobx-keystone-yjs) | [demo](https://mobx-keystone.js.org/examples/yjs-binding) |
179
+ | [PSPDFKit](https://www.nutrient.io/) | | [yjs-pspdfkit](https://github.com/hoangqwe159/yjs-pspdfkit) | [demo](https://github.com/hoangqwe159/yjs-pspdfkit) |
180
+ | [Rows n'Columns](https://www.rowsncolumns.app/) | ✔ | [@rowsncolumns/y-spreadsheet](https://docs.rowsncolumns.app/collaboration/yjs-collaboration) | |
181
+
182
+ ### Utilities
183
+
184
+ Tools that extend the core functionality of Yjs.
185
+
186
+ <dl>
187
+ <dt><a href="https://github.com/yjs/y-utility">y-utility</a></dt>
188
+ <dd>
189
+ Library with <code>YMultiDocUndoManager</code> (undo/redo across Yjs docs) and
190
+ <code>YKeyValue</code> (optimized key-value store).
191
+ </dd>
192
+ <dt>
193
+ <a href="https://github.com/Tulip-Writer/yjs-orderedtree"> yjs-orderedtree </a> 🌳
194
+ </dt>
195
+ <dd>
196
+ Class for ordered trees via Y.Map. Handles <code>insert</code>,
197
+ <code>delete</code>, and <code>move</code> operations for folder-like
198
+ hierarchies.
199
+ </dd>
200
+
201
+ </dl>
202
+
203
+ ### Providers
204
+
205
+ Setting up the communication between clients, managing awareness information,
206
+ and storing shared data for offline usage is quite a hassle. **Providers**
207
+ manage all that for you and are the perfect starting point for your
208
+ collaborative app.
209
+
210
+ > This list of providers is incomplete. Please open PRs to add your providers to
211
+ > this list!
212
+
213
+ #### Connection Providers
214
+
215
+ <dl>
216
+ <dt><a href="https://github.com/yjs/y-websocket">y-websocket</a></dt>
217
+ <dd>
218
+ A module that contains a simple websocket backend and a websocket client that
219
+ connects to that backend. <a href="https://github.com/yjs/y-redis/"><b>y-redis</b></a>,
220
+ <b>y-sweet</b>, <b>ypy-websocket</b>, <b>yrs-warp</b> and <a href="https://tiptap.dev/docs/hocuspocus/introduction">
221
+ <b>Hocuspocus</b></a> (see below) are alternative
222
+ backends to y-websocket.
223
+ </dd>
224
+ <dt><a href="https://github.com/yjs/y-webrtc">y-webrtc</a></dt>
225
+ <dd>
226
+ Propagates document updates peer-to-peer using WebRTC. The peers exchange
227
+ signaling data over signaling servers. Publicly available signaling servers
228
+ are available. Communication over the signaling servers can be encrypted by
229
+ providing a shared secret, keeping the connection information and the shared
230
+ document private.
231
+ </dd>
232
+ <dt><a href="https://github.com/liveblocks/liveblocks">@liveblocks/yjs </a> 🌟</dt>
233
+ <dd>
234
+ <a href="https://liveblocks.io/document/yjs">Liveblocks Yjs</a> provides a fully
235
+ hosted WebSocket infrastructure and persisted data store for Yjs
236
+ documents. No configuration or maintenance is required. It also features
237
+ Yjs webhook events, REST API to read and update Yjs documents, and a
238
+ browser DevTools extension.
239
+ </dd>
240
+ <dt><a href="https://github.com/ueberdosis/hocuspocus">Hocuspocus</a> ⭐</dt>
241
+ <dd>
242
+ A standalone extensible yjs server with sqlite persistence, webhooks, auth and more.
243
+ </dd>
244
+ <dt><a href="https://github.com/nperez0111/teleportal">teleportal</a></dt>
245
+ <dd>
246
+ Build your own Y.js sync server: any storage, any JS runtime, any transport.
247
+ </dd>
248
+ <dt><a href="https://github.com/drifting-in-space/y-sweet">y-sweet</a></dt>
249
+ <dd>
250
+ A standalone yjs server with persistence to S3 or filesystem. They offer a
251
+ <a href="https://y-sweet.cloud">cloud service</a> as well.
252
+ </dd>
253
+ <dt><a href="https://docs.superviz.com/collaboration/integrations/YJS/overview">@superviz/yjs</a></dt>
254
+ <dd>
255
+ SuperViz Yjs Provider comes with a secure, scalable real-time infrastructure
256
+ for Yjs documents, fully compatible with a set of real-time
257
+ collaboration components offered by SuperViz. This solution ensures
258
+ synchronization, offline editing, and real-time updates, enabling
259
+ multiple users to collaborate effectively within shared workspaces.
260
+ </dd>
261
+ <dt><a href="https://docs.partykit.io/reference/y-partykit-api/">PartyKit</a></dt>
262
+ <dd>
263
+ Cloud service for building multiplayer apps.
264
+ </dd>
265
+ </dd>
266
+ <dt><a href="https://github.com/pluv-io/pluv">@pluv/crdt-yjs</a></dt>
267
+ <dd>
268
+ Use <a href="https://pluv.io/docs/storage/using-yjs">pluv.io</a> as a
269
+ full-featured backend for Yjs. pluv.io can either be be used on its
270
+ fully-managed WebSocket infrastructure, or self-hosted on Cloudflare Workers
271
+ and Node.js runtimes. Offers a typesafe API with authentication, webhooks,
272
+ rooms, and more.
273
+ <dt><a href="https://github.com/marcopolo/y-libp2p">y-libp2p</a></dt>
274
+ <dd>
275
+ Uses <a href="https://libp2p.io/">libp2p</a> to propagate updates via
276
+ <a href="https://github.com/libp2p/specs/tree/master/pubsub/gossipsub">GossipSub</a>.
277
+ Also includes a peer-sync mechanism to catch up on missed updates.
278
+ </dd>
279
+ <dt><a href="https://github.com/yjs/y-dat">y-dat</a></dt>
280
+ <dd>
281
+ [WIP] Write document updates efficiently to the dat network using
282
+ <a href="https://github.com/kappa-db/multifeed">multifeed</a>. Each client has
283
+ an append-only log of CRDT local updates (hypercore). Multifeed manages and sync
284
+ hypercores and y-dat listens to changes and applies them to the Yjs document.
285
+ </dd>
286
+ <dt><a href="https://github.com/yousefED/matrix-crdt">Matrix-CRDT</a></dt>
287
+ <dd>
288
+ Use <a href="https://www.matrix.org">Matrix</a> as an off-the-shelf backend for
289
+ Yjs by using the <a href="https://github.com/yousefED/matrix-crdt">MatrixProvider</a>.
290
+ Use Matrix as transport and storage of Yjs updates, so you can focus building
291
+ your client app and Matrix can provide powerful features like Authentication,
292
+ Authorization, Federation, hosting (self-hosting or SaaS) and even End-to-End
293
+ Encryption (E2EE).
294
+ </dd>
295
+ <dt><a href="https://github.com/y-crdt/yrb-actioncable">yrb-actioncable</a></dt>
296
+ <dd>
297
+ An ActionCable companion for Yjs clients. There is a fitting
298
+ <a href="https://github.com/y-crdt/yrb-redis">redis extension</a> as well.
299
+ </dd>
300
+ <dt><a href="https://github.com/y-crdt/ypy-websocket">ypy-websocket</a></dt>
301
+ <dd>
302
+ Websocket backend, written in Python.
303
+ </dd>
304
+ <dt><a href="https://tinybase.org/">Tinybase</a></dt>
305
+ <dd>
306
+ The reactive data store for local-first apps. They support multiple CRDTs and
307
+ different network technologies.
308
+ </dd>
309
+ <dt><a href="https://codeberg.org/webxdc/y-webxdc">y-webxdc</a></dt>
310
+ <dd>
311
+ Provider for sharing data in <a href="https://webxdc.org">webxdc chat apps</a>.
312
+ </dd>
313
+ <dt><a href="https://www.secsync.com/">secsync</a></dt>
314
+ <dd>
315
+ An architecture to relay end-to-end encrypted CRDTs over a central service.
316
+ </dd>
317
+ <dt><a href="https://www.npmjs.com/package/@electric-sql/y-electric">y-electric</a></dt>
318
+ <dd>
319
+ Sync Yjs over <a href="https://electric-sql.com/">ElectricSQL</a>.
320
+ </dd>
321
+ <dt><a href="https://github.com/TimoWilhelm/yjs-cf-ws-provider">yjs-cf-ws-provider</a></dt>
322
+ <dd>
323
+ Cloudflare provider for Yjs based on durable objects.
324
+ </dd>
325
+ <dt><a href="https://github.com/yousefED/nostr-crdt">nostr-crdt</a></dt>
326
+ <dd>
327
+ Sync Yjs over <a href="https://github.com/nostr-protocol/">nostr</a>.
328
+ </dd>
329
+ </dl>
330
+
331
+ #### Persistence Providers
332
+
333
+ <dl>
334
+ <dt><a href="https://github.com/yjs/y-indexeddb">y-indexeddb</a></dt>
335
+ <dd>
336
+ Efficiently persists document updates to the browsers indexeddb database.
337
+ The document is immediately available and only diffs need to be synced through the
338
+ network provider.
339
+ </dd>
340
+ <dt><a href="https://github.com/MaxNoetzold/y-mongodb-provider">y-mongodb-provider</a></dt>
341
+ <dd>
342
+ Adds persistent storage to a server with MongoDB. Can be used with the
343
+ y-websocket provider.
344
+ </dd>
345
+ <dt><a href="https://github.com/podraven/y-fire">y-fire</a></dt>
346
+ <dd>
347
+ A database and connection provider for Yjs based on Firestore.
348
+ </dd>
349
+ <dt><a href="https://github.com/malte-j/y-op-sqlite">y-op-sqlite</a></dt>
350
+ <dd>
351
+ Persist YJS updates in your React Native app using
352
+ <a href="https://github.com/OP-Engineering/op-sqlite">op-sqlite</a>
353
+ , the fastest SQLite library for React Native.
354
+ </dd>
355
+ <dt><a href="https://github.com/MaxNoetzold/y-postgresql">y-postgresql</a></dt>
356
+ <dd>
357
+ Provides persistent storage for a web server using PostgreSQL and
358
+ is easily compatible with y-websocket.
359
+ </dd>
360
+ <dt><a href="https://github.com/kapv89/k_yrs_go">k_yrs_go</a></dt>
361
+ <dd>
362
+ Golang database server for YJS CRDT using Postgres + Redis
363
+ </dd>
364
+ <dt><a href="https://github.com/malte-j/y-op-sqlite">y-op-sqlite</a></dt>
365
+ <dd>
366
+ Yjs persistence provider for op-sqlite
367
+ </dd>
368
+
369
+ </dl>
370
+
371
+ ### Tooling
372
+
373
+ * [y-sweet debugger](https://y-sweet.cloud/advanced/debugger)
374
+ * [liveblocks devtools](https://liveblocks.io/devtools)
375
+ * [Yjs inspector](https://inspector.yjs.dev)
376
+
377
+ ### Ports
378
+
379
+ There are several Yjs-compatible ports to other programming languages.
380
+
381
+ * [y-octo](https://github.com/toeverything/y-octo) - Rust implementation by
382
+ [AFFiNE](https://affine.pro)
383
+ * [y-crdt](https://github.com/y-crdt/y-crdt) - Rust implementation with multiple
384
+ language bindings to other languages
385
+ * [yrs](https://github.com/y-crdt/y-crdt/tree/main/yrs) - Rust interface
386
+ * [ypy](https://github.com/y-crdt/ypy) - Python binding
387
+ * [yrb](https://github.com/y-crdt/yrb) - Ruby binding
388
+ * [yswift](https://github.com/y-crdt/yswift) - Swift binding
389
+ * [yffi](https://github.com/y-crdt/y-crdt/tree/main/yffi) - C-FFI
390
+ * [ywasm](https://github.com/y-crdt/y-crdt/tree/main/ywasm) - WASM binding
391
+ * [y_ex](https://github.com/satoren/y_ex) - Elixir bindings
392
+ * [ycs](https://github.com/yjs/ycs) - .Net compatible C# implementation.
393
+
394
+ ## Getting Started
395
+
396
+ Install Yjs and a provider with your favorite package manager:
397
+
398
+ ```sh
399
+ npm i yjs y-websocket
400
+ ```
401
+
402
+ Start the y-websocket server:
403
+
404
+ ```sh
405
+ PORT=1234 node ./node_modules/y-websocket/bin/server.cjs
406
+ ```
407
+
408
+ ### Example: Observe types
409
+
410
+ ```js
411
+ import * as Y from 'yjs';
412
+
413
+ const doc = new Y.Doc();
414
+ const yarray = doc.getArray('my-array')
415
+ yarray.observe(event => {
416
+ console.log('yarray was modified')
417
+ })
418
+ // every time a local or remote client modifies yarray, the observer is called
419
+ yarray.insert(0, ['val']) // => "yarray was modified"
420
+ ```
421
+
422
+ ### Example: Nest types
423
+
424
+ Remember, shared types are just plain old data types. The only limitation is
425
+ that a shared type must exist only once in the shared document.
426
+
427
+ ```js
428
+ const ymap = doc.getMap('map')
429
+ const foodArray = new Y.Array()
430
+ foodArray.insert(0, ['apple', 'banana'])
431
+ ymap.set('food', foodArray)
432
+ ymap.get('food') === foodArray // => true
433
+ ymap.set('fruit', foodArray) // => Error! foodArray is already defined
434
+ ```
435
+
436
+ Now you understand how types are defined on a shared document. Next you can jump
437
+ to the [demo repository](https://github.com/yjs/yjs-demos) or continue reading
438
+ the API docs.
439
+
440
+ ### Example: Using and combining providers
441
+
442
+ Any of the Yjs providers can be combined with each other. So you can sync data
443
+ over different network technologies.
444
+
445
+ In most cases you want to use a network provider (like y-websocket or y-webrtc)
446
+ in combination with a persistence provider (y-indexeddb in the browser).
447
+ Persistence allows you to load the document faster and to persist data that is
448
+ created while offline.
449
+
450
+ For the sake of this demo we combine two different network providers with a
451
+ persistence provider.
452
+
453
+ ```js
454
+ import * as Y from 'yjs'
455
+ import { WebrtcProvider } from 'y-webrtc'
456
+ import { WebsocketProvider } from 'y-websocket'
457
+ import { IndexeddbPersistence } from 'y-indexeddb'
458
+
459
+ const ydoc = new Y.Doc()
460
+
461
+ // this allows you to instantly get the (cached) documents data
462
+ const indexeddbProvider = new IndexeddbPersistence('count-demo', ydoc)
463
+ indexeddbProvider.whenSynced.then(() => {
464
+ console.log('loaded data from indexed db')
465
+ })
466
+
467
+ // Sync clients with the y-webrtc provider.
468
+ const webrtcProvider = new WebrtcProvider('count-demo', ydoc)
469
+
470
+ // Sync clients with the y-websocket provider
471
+ const websocketProvider = new WebsocketProvider(
472
+ 'wss://demos.yjs.dev', 'count-demo', ydoc
473
+ )
474
+
475
+ // array of numbers which produce a sum
476
+ const yarray = ydoc.getArray('count')
477
+
478
+ // observe changes of the sum
479
+ yarray.observe(event => {
480
+ // print updates when the data changes
481
+ console.log('new sum: ' + yarray.toArray().reduce((a,b) => a + b))
482
+ })
483
+
484
+ // add 1 to the sum
485
+ yarray.push([1]) // => "new sum: 1"
486
+ ```
487
+
488
+ ## API
489
+
490
+ ```js
491
+ import * as Y from 'yjs'
492
+ ```
493
+
494
+ ### Shared Types
495
+
496
+ <details>
497
+ <summary><b>Y.Array</b></summary>
498
+ <br>
499
+ <p>
500
+ A shareable Array-like type that supports efficient insert/delete of elements
501
+ at any position. Internally it uses a linked list of Arrays that is split when
502
+ necessary.
503
+ </p>
504
+ <pre>const yarray = new Y.Array()</pre>
505
+ <dl>
506
+ <b><code>
507
+ Y.Array.from(Array&lt;object|boolean|Array|string|number|null|Uint8Array|Y.Type&gt;):
508
+ Y.Array
509
+ </code></b>
510
+ <dd>An alternative factory function to create a Y.Array based on existing content.</dd>
511
+ <b><code>parent:Y.AbstractType|null</code></b>
512
+ <dd></dd>
513
+ <b><code>insert(index:number, content:Array&lt;object|boolean|Array|string|number|null|Uint8Array|Y.Type&gt;)</code></b>
514
+ <dd>
515
+ Insert content at <var>index</var>. Note that content is an array of elements.
516
+ I.e. <code>array.insert(0, [1])</code> splices the list and inserts 1 at
517
+ position 0.
518
+ </dd>
519
+ <b><code>push(Array&lt;Object|boolean|Array|string|number|null|Uint8Array|Y.Type&gt;)</code></b>
520
+ <dd></dd>
521
+ <b><code>unshift(Array&lt;Object|boolean|Array|string|number|null|Uint8Array|Y.Type&gt;)</code></b>
522
+ <dd></dd>
523
+ <b><code>delete(index:number, length:number)</code></b>
524
+ <dd></dd>
525
+ <b><code>get(index:number)</code></b>
526
+ <dd></dd>
527
+ <b><code>slice(start:number, end:number):Array&lt;Object|boolean|Array|string|number|null|Uint8Array|Y.Type&gt;</code></b>
528
+ <dd>Retrieve a range of content</dd>
529
+ <b><code>length:number</code></b>
530
+ <dd></dd>
531
+ <b>
532
+ <code>
533
+ forEach(function(value:object|boolean|Array|string|number|null|Uint8Array|Y.Type,
534
+ index:number, array: Y.Array))
535
+ </code>
536
+ </b>
537
+ <dd></dd>
538
+ <b><code>map(function(T, number, YArray):M):Array&lt;M&gt;</code></b>
539
+ <dd></dd>
540
+ <b><code>clone(): Y.Array</code></b>
541
+ <dd>
542
+ Clone all values into a fresh Y.Array instance. The returned type can be
543
+ included into the Yjs document.
544
+ </dd>
545
+ <b><code>toArray():Array&lt;object|boolean|Array|string|number|null|Uint8Array|Y.Type&gt;</code></b>
546
+ <dd>Copies the content of this YArray to a new Array.</dd>
547
+ <b><code>toJSON():Array&lt;Object|boolean|Array|string|number|null&gt;</code></b>
548
+ <dd>
549
+ Copies the content of this YArray to a new Array. It transforms all child types
550
+ to JSON using their <code>toJSON</code> method.
551
+ </dd>
552
+ <b><code>[Symbol.Iterator]</code></b>
553
+ <dd>
554
+ Returns an YArray Iterator that contains the values for each index in the array.
555
+ <pre>for (let value of yarray) { .. }</pre>
556
+ </dd>
557
+ <b><code>observe(function(YArrayEvent, Transaction):void)</code></b>
558
+ <dd>
559
+ Adds an event listener to this type that will be called synchronously every time
560
+ this type is modified. In the case this type is modified in the event listener,
561
+ the event listener will be called again after the current event listener returns.
562
+ </dd>
563
+ <b><code>unobserve(function(YArrayEvent, Transaction):void)</code></b>
564
+ <dd>
565
+ Removes an <code>observe</code> event listener from this type.
566
+ </dd>
567
+ <b><code>observeDeep(function(Array&lt;YEvent&gt;, Transaction):void)</code></b>
568
+ <dd>
569
+ Adds an event listener to this type that will be called synchronously every time
570
+ this type or any of its children is modified. In the case this type is modified
571
+ in the event listener, the event listener will be called again after the current
572
+ event listener returns. The event listener receives all Events created by itself
573
+ or any of its children.
574
+ </dd>
575
+ <b><code>unobserveDeep(function(Array&lt;YEvent&gt;, Transaction):void)</code></b>
576
+ <dd>
577
+ Removes an <code>observeDeep</code> event listener from this type.
578
+ </dd>
579
+ </dl>
580
+ </details>
581
+ <details>
582
+ <summary><b>Y.Map</b></summary>
583
+ <br>
584
+ <p>
585
+ A shareable Map type.
586
+ </p>
587
+ <pre><code>const ymap = new Y.Map()</code></pre>
588
+ <dl>
589
+ <b><code>parent:Y.AbstractType|null</code></b>
590
+ <dd></dd>
591
+ <b><code>size: number</code></b>
592
+ <dd>Total number of key/value pairs.</dd>
593
+ <b><code>get(key:string):object|boolean|string|number|null|Uint8Array|Y.Type</code></b>
594
+ <dd></dd>
595
+ <b><code>set(key:string, value:object|boolean|string|number|null|Uint8Array|Y.Type)</code></b>
596
+ <dd></dd>
597
+ <b><code>delete(key:string)</code></b>
598
+ <dd></dd>
599
+ <b><code>has(key:string):boolean</code></b>
600
+ <dd></dd>
601
+ <b><code>clear()</code></b>
602
+ <dd>Removes all elements from this YMap.</dd>
603
+ <b><code>clone():Y.Map</code></b>
604
+ <dd>Clone this type into a fresh Yjs type.</dd>
605
+ <b><code>toJSON():Object&lt;string, Object|boolean|Array|string|number|null|Uint8Array&gt;</code></b>
606
+ <dd>
607
+ Copies the <code>[key,value]</code> pairs of this YMap to a new Object.It
608
+ transforms all child types to JSON using their <code>toJSON</code> method.
609
+ </dd>
610
+ <b><code>forEach(function(value:object|boolean|Array|string|number|null|Uint8Array|Y.Type,
611
+ key:string, map: Y.Map))</code></b>
612
+ <dd>
613
+ Execute the provided function once for every key-value pair.
614
+ </dd>
615
+ <b><code>[Symbol.Iterator]</code></b>
616
+ <dd>
617
+ Returns an Iterator of <code>[key, value]</code> pairs.
618
+ <pre>for (let [key, value] of ymap) { .. }</pre>
619
+ </dd>
620
+ <b><code>entries()</code></b>
621
+ <dd>
622
+ Returns an Iterator of <code>[key, value]</code> pairs.
623
+ </dd>
624
+ <b><code>values()</code></b>
625
+ <dd>
626
+ Returns an Iterator of all values.
627
+ </dd>
628
+ <b><code>keys()</code></b>
629
+ <dd>
630
+ Returns an Iterator of all keys.
631
+ </dd>
632
+ <b><code>observe(function(YMapEvent, Transaction):void)</code></b>
633
+ <dd>
634
+ Adds an event listener to this type that will be called synchronously every time
635
+ this type is modified. In the case this type is modified in the event listener,
636
+ the event listener will be called again after the current event listener returns.
637
+ </dd>
638
+ <b><code>unobserve(function(YMapEvent, Transaction):void)</code></b>
639
+ <dd>
640
+ Removes an <code>observe</code> event listener from this type.
641
+ </dd>
642
+ <b><code>observeDeep(function(Array&lt;YEvent&gt;, Transaction):void)</code></b>
643
+ <dd>
644
+ Adds an event listener to this type that will be called synchronously every time
645
+ this type or any of its children is modified. In the case this type is modified
646
+ in the event listener, the event listener will be called again after the current
647
+ event listener returns. The event listener receives all Events created by itself
648
+ or any of its children.
649
+ </dd>
650
+ <b><code>unobserveDeep(function(Array&lt;YEvent&gt;, Transaction):void)</code></b>
651
+ <dd>
652
+ Removes an <code>observeDeep</code> event listener from this type.
653
+ </dd>
654
+ </dl>
655
+ </details>
656
+
657
+ <details>
658
+ <summary><b>Y.Text</b></summary>
659
+ <br>
660
+ <p>
661
+ A shareable type that is optimized for shared editing on text. It allows to
662
+ assign properties to ranges in the text. This makes it possible to implement
663
+ rich-text bindings to this type.
664
+ </p>
665
+ <p>
666
+ This type can also be transformed to the
667
+ <a href="https://quilljs.com/docs/delta">delta format</a>. Similarly the
668
+ YTextEvents compute changes as deltas.
669
+ </p>
670
+ <pre>const ytext = new Y.Text()</pre>
671
+ <dl>
672
+ <b><code>parent:Y.AbstractType|null</code></b>
673
+ <dd></dd>
674
+ <b><code>insert(index:number, content:string, [formattingAttributes:Object&lt;string,string&gt;])</code></b>
675
+ <dd>
676
+ Insert a string at <var>index</var> and assign formatting attributes to it.
677
+ <pre>ytext.insert(0, 'bold text', { bold: true })</pre>
678
+ </dd>
679
+ <b><code>delete(index:number, length:number)</code></b>
680
+ <dd></dd>
681
+ <b><code>format(index:number, length:number, formattingAttributes:Object&lt;string,string&gt;)</code></b>
682
+ <dd>Assign formatting attributes to a range in the text</dd>
683
+ <b><code>applyDelta(delta: Delta, opts:Object&lt;string,any&gt;)</code></b>
684
+ <dd>
685
+ See <a href="https://quilljs.com/docs/delta/">Quill Delta</a>
686
+ Can set options for preventing remove ending newLines, default is true.
687
+ <pre>ytext.applyDelta(delta, { sanitize: false })</pre>
688
+ </dd>
689
+ <b><code>length:number</code></b>
690
+ <dd></dd>
691
+ <b><code>toString():string</code></b>
692
+ <dd>Transforms this type, without formatting options, into a string.</dd>
693
+ <b><code>toJSON():string</code></b>
694
+ <dd>See <code>toString</code></dd>
695
+ <b><code>toDelta():Delta</code></b>
696
+ <dd>
697
+ Transforms this type to a <a href="https://quilljs.com/docs/delta/">Quill Delta</a>
698
+ </dd>
699
+ <b><code>observe(function(YTextEvent, Transaction):void)</code></b>
700
+ <dd>
701
+ Adds an event listener to this type that will be called synchronously every time
702
+ this type is modified. In the case this type is modified in the event listener,
703
+ the event listener will be called again after the current event listener returns.
704
+ </dd>
705
+ <b><code>unobserve(function(YTextEvent, Transaction):void)</code></b>
706
+ <dd>
707
+ Removes an <code>observe</code> event listener from this type.
708
+ </dd>
709
+ <b><code>observeDeep(function(Array&lt;YEvent&gt;, Transaction):void)</code></b>
710
+ <dd>
711
+ Adds an event listener to this type that will be called synchronously every time
712
+ this type or any of its children is modified. In the case this type is modified
713
+ in the event listener, the event listener will be called again after the current
714
+ event listener returns. The event listener receives all Events created by itself
715
+ or any of its children.
716
+ </dd>
717
+ <b><code>unobserveDeep(function(Array&lt;YEvent&gt;, Transaction):void)</code></b>
718
+ <dd>
719
+ Removes an <code>observeDeep</code> event listener from this type.
720
+ </dd>
721
+ </dl>
722
+ </details>
723
+
724
+ <details>
725
+ <summary><b>Y.XmlFragment</b></summary>
726
+ <br>
727
+ <p>
728
+ A container that holds an Array of Y.XmlElements.
729
+ </p>
730
+ <pre><code>const yxml = new Y.XmlFragment()</code></pre>
731
+ <dl>
732
+ <b><code>parent:Y.AbstractType|null</code></b>
733
+ <dd></dd>
734
+ <b><code>firstChild:Y.XmlElement|Y.XmlText|null</code></b>
735
+ <dd></dd>
736
+ <b><code>insert(index:number, content:Array&lt;Y.XmlElement|Y.XmlText&gt;)</code></b>
737
+ <dd></dd>
738
+ <b><code>delete(index:number, length:number)</code></b>
739
+ <dd></dd>
740
+ <b><code>get(index:number)</code></b>
741
+ <dd></dd>
742
+ <b><code>slice(start:number, end:number):Array&lt;Y.XmlElement|Y.XmlText&gt;</code></b>
743
+ <dd>Retrieve a range of content</dd>
744
+ <b><code>length:number</code></b>
745
+ <dd></dd>
746
+ <b><code>clone():Y.XmlFragment</code></b>
747
+ <dd>Clone this type into a fresh Yjs type.</dd>
748
+ <b><code>toArray():Array&lt;Y.XmlElement|Y.XmlText&gt;</code></b>
749
+ <dd>Copies the children to a new Array.</dd>
750
+ <b><code>toString():string</code></b>
751
+ <dd>Get the XML serialization of all descendants.</dd>
752
+ <b><code>toJSON():string</code></b>
753
+ <dd>See <code>toString</code>.</dd>
754
+ <b><code>createTreeWalker(filter: function(AbstractType&lt;any&gt;):boolean):Iterable</code></b>
755
+ <dd>Create an Iterable that walks through the children.</dd>
756
+ <b><code>observe(function(YXmlEvent, Transaction):void)</code></b>
757
+ <dd>
758
+ Adds an event listener to this type that will be called synchronously every time
759
+ this type is modified. In the case this type is modified in the event listener,
760
+ the event listener will be called again after the current event listener returns.
761
+ </dd>
762
+ <b><code>unobserve(function(YXmlEvent, Transaction):void)</code></b>
763
+ <dd>
764
+ Removes an <code>observe</code> event listener from this type.
765
+ </dd>
766
+ <b><code>observeDeep(function(Array&lt;YEvent&gt;, Transaction):void)</code></b>
767
+ <dd>
768
+ Adds an event listener to this type that will be called synchronously every time
769
+ this type or any of its children is modified. In the case this type is modified
770
+ in the event listener, the event listener will be called again after the current
771
+ event listener returns. The event listener receives all Events created by itself
772
+ or any of its children.
773
+ </dd>
774
+ <b><code>unobserveDeep(function(Array&lt;YEvent&gt;, Transaction):void)</code></b>
775
+ <dd>
776
+ Removes an <code>observeDeep</code> event listener from this type.
777
+ </dd>
778
+ </dl>
779
+ </details>
780
+
781
+ <details>
782
+ <summary><b>Y.XmlElement</b></summary>
783
+ <br>
784
+ <p>
785
+ A shareable type that represents an XML Element. It has a <code>nodeName</code>,
786
+ attributes, and a list of children. But it makes no effort to validate its
787
+ content and be actually XML compliant.
788
+ </p>
789
+ <pre><code>const yxml = new Y.XmlElement()</code></pre>
790
+ <dl>
791
+ <b><code>parent:Y.AbstractType|null</code></b>
792
+ <dd></dd>
793
+ <b><code>firstChild:Y.XmlElement|Y.XmlText|null</code></b>
794
+ <dd></dd>
795
+ <b><code>nextSibling:Y.XmlElement|Y.XmlText|null</code></b>
796
+ <dd></dd>
797
+ <b><code>prevSibling:Y.XmlElement|Y.XmlText|null</code></b>
798
+ <dd></dd>
799
+ <b><code>insert(index:number, content:Array&lt;Y.XmlElement|Y.XmlText&gt;)</code></b>
800
+ <dd></dd>
801
+ <b><code>delete(index:number, length:number)</code></b>
802
+ <dd></dd>
803
+ <b><code>get(index:number)</code></b>
804
+ <dd></dd>
805
+ <b><code>length:number</code></b>
806
+ <dd></dd>
807
+ <b><code>setAttribute(attributeName:string, attributeValue:string)</code></b>
808
+ <dd></dd>
809
+ <b><code>removeAttribute(attributeName:string)</code></b>
810
+ <dd></dd>
811
+ <b><code>getAttribute(attributeName:string):string</code></b>
812
+ <dd></dd>
813
+ <b><code>getAttributes():Object&lt;string,string&gt;</code></b>
814
+ <dd></dd>
815
+ <b><code>get(i:number):Y.XmlElement|Y.XmlText</code></b>
816
+ <dd>Retrieve the i-th element.</dd>
817
+ <b><code>slice(start:number, end:number):Array&lt;Y.XmlElement|Y.XmlText&gt;</code></b>
818
+ <dd>Retrieve a range of content</dd>
819
+ <b><code>clone():Y.XmlElement</code></b>
820
+ <dd>Clone this type into a fresh Yjs type.</dd>
821
+ <b><code>toArray():Array&lt;Y.XmlElement|Y.XmlText&gt;</code></b>
822
+ <dd>Copies the children to a new Array.</dd>
823
+ <b><code>toString():string</code></b>
824
+ <dd>Get the XML serialization of all descendants.</dd>
825
+ <b><code>toJSON():string</code></b>
826
+ <dd>See <code>toString</code>.</dd>
827
+ <b><code>observe(function(YXmlEvent, Transaction):void)</code></b>
828
+ <dd>
829
+ Adds an event listener to this type that will be called synchronously every
830
+ time this type is modified. In the case this type is modified in the event
831
+ listener, the event listener will be called again after the current event
832
+ listener returns.
833
+ </dd>
834
+ <b><code>unobserve(function(YXmlEvent, Transaction):void)</code></b>
835
+ <dd>
836
+ Removes an <code>observe</code> event listener from this type.
837
+ </dd>
838
+ <b><code>observeDeep(function(Array&lt;YEvent&gt;, Transaction):void)</code></b>
839
+ <dd>
840
+ Adds an event listener to this type that will be called synchronously every time
841
+ this type or any of its children is modified. In the case this type is modified
842
+ in the event listener, the event listener will be called again after the current
843
+ event listener returns. The event listener receives all Events created by itself
844
+ or any of its children.
845
+ </dd>
846
+ <b><code>unobserveDeep(function(Array&lt;YEvent&gt;, Transaction):void)</code></b>
847
+ <dd>
848
+ Removes an <code>observeDeep</code> event listener from this type.
849
+ </dd>
850
+ </dl>
851
+ </details>
852
+
853
+ ### Y.Doc
854
+
855
+ ```js
856
+ const doc = new Y.Doc()
857
+ ```
858
+
859
+ <dl>
860
+ <b><code>clientID</code></b>
861
+ <dd>A unique id that identifies this client. (readonly)</dd>
862
+ <b><code>gc</code></b>
863
+ <dd>
864
+ Whether garbage collection is enabled on this doc instance. Set `doc.gc = false`
865
+ in order to disable gc and be able to restore old content. See https://github.com/yjs/yjs#yjs-crdt-algorithm
866
+ for more information about gc in Yjs.
867
+ </dd>
868
+ <b><code>transact(function(Transaction):void [, origin:any])</code></b>
869
+ <dd>
870
+ Every change on the shared document happens in a transaction. Observer calls and
871
+ the <code>update</code> event are called after each transaction. You should
872
+ <i>bundle</i> changes into a single transaction to reduce the amount of event
873
+ calls. I.e. <code>doc.transact(() => { yarray.insert(..); ymap.set(..) })</code>
874
+ triggers a single change event. <br>You can specify an optional <code>origin</code>
875
+ parameter that is stored on <code>transaction.origin</code> and
876
+ <code>on('update', (update, origin) => ..)</code>.
877
+ </dd>
878
+ <b><code>toJSON():any</code></b>
879
+ <dd>
880
+ Deprecated: It is recommended to call toJSON directly on the shared types.
881
+ Converts the entire document into a js object, recursively traversing each yjs
882
+ type. Doesn't log types that have not been defined (using
883
+ <code>ydoc.getType(..)</code>).
884
+ </dd>
885
+ <b><code>get(string, Y.[TypeClass]):[Type]</code></b>
886
+ <dd>Define a shared type.</dd>
887
+ <b><code>getArray(string):Y.Array</code></b>
888
+ <dd>Define a shared Y.Array type. Is equivalent to <code>y.get(string, Y.Array)</code>.</dd>
889
+ <b><code>getMap(string):Y.Map</code></b>
890
+ <dd>Define a shared Y.Map type. Is equivalent to <code>y.get(string, Y.Map)</code>.</dd>
891
+ <b><code>getText(string):Y.Text</code></b>
892
+ <dd>Define a shared Y.Text type. Is equivalent to <code>y.get(string, Y.Text)</code>.</dd>
893
+ <b><code>getXmlElement(string, string):Y.XmlElement</code></b>
894
+ <dd>Define a shared Y.XmlElement type. Is equivalent to <code>y.get(string, Y.XmlElement)</code>.</dd>
895
+ <b><code>getXmlFragment(string):Y.XmlFragment</code></b>
896
+ <dd>Define a shared Y.XmlFragment type. Is equivalent to <code>y.get(string, Y.XmlFragment)</code>.</dd>
897
+ <b><code>on(string, function)</code></b>
898
+ <dd>Register an event listener on the shared type</dd>
899
+ <b><code>off(string, function)</code></b>
900
+ <dd>Unregister an event listener from the shared type</dd>
901
+ </dl>
902
+
903
+ #### Y.Doc Events
904
+
905
+ <dl>
906
+ <b><code>on('update', function(updateMessage:Uint8Array, origin:any, Y.Doc):void)</code></b>
907
+ <dd>
908
+ Listen to document updates. Document updates must be transmitted to all other
909
+ peers. You can apply document updates in any order and multiple times. Use `updateV2`
910
+ to receive V2 events.
911
+ </dd>
912
+ <b><code>on('beforeTransaction', function(Y.Transaction, Y.Doc):void)</code></b>
913
+ <dd>Emitted before each transaction.</dd>
914
+ <b><code>on('afterTransaction', function(Y.Transaction, Y.Doc):void)</code></b>
915
+ <dd>Emitted after each transaction.</dd>
916
+ <b><code>on('beforeAllTransactions', function(Y.Doc):void)</code></b>
917
+ <dd>
918
+ Transactions can be nested (e.g. when an event within a transaction calls another
919
+ transaction). Emitted before the first transaction.
920
+ </dd>
921
+ <b><code>on('afterAllTransactions', function(Y.Doc, Array&lt;Y.Transaction&gt;):void)</code></b>
922
+ <dd>Emitted after the last transaction is cleaned up.</dd>
923
+ </dl>
924
+
925
+ ### Document Updates
926
+
927
+ Changes on the shared document are encoded into *document updates*. Document
928
+ updates are *commutative* and *idempotent*. This means that they can be applied
929
+ in any order and multiple times.
930
+
931
+ #### Example: Listen to update events and apply them on remote client
932
+
933
+ ```js
934
+ const doc1 = new Y.Doc()
935
+ const doc2 = new Y.Doc()
936
+
937
+ doc1.on('update', update => {
938
+ Y.applyUpdate(doc2, update)
939
+ })
940
+
941
+ doc2.on('update', update => {
942
+ Y.applyUpdate(doc1, update)
943
+ })
944
+
945
+ // All changes are also applied to the other document
946
+ doc1.getArray('myarray').insert(0, ['Hello doc2, you got this?'])
947
+ doc2.getArray('myarray').get(0) // => 'Hello doc2, you got this?'
948
+ ```
949
+
950
+ Yjs internally maintains a [state vector](#state-vector) that denotes the next
951
+ expected clock from each client. In a different interpretation it holds the
952
+ number of structs created by each client. When two clients sync, you can either
953
+ exchange the complete document structure or only the differences by sending the
954
+ state vector to compute the differences.
955
+
956
+ #### Example: Sync two clients by exchanging the complete document structure
957
+
958
+ ```js
959
+ const state1 = Y.encodeStateAsUpdate(ydoc1)
960
+ const state2 = Y.encodeStateAsUpdate(ydoc2)
961
+ Y.applyUpdate(ydoc1, state2)
962
+ Y.applyUpdate(ydoc2, state1)
963
+ ```
964
+
965
+ #### Example: Sync two clients by computing the differences
966
+
967
+ This example shows how to sync two clients with the minimal amount of exchanged
968
+ data by computing only the differences using the state vector of the remote
969
+ client. Syncing clients using the state vector requires another roundtrip, but
970
+ can save a lot of bandwidth.
971
+
972
+ ```js
973
+ const stateVector1 = Y.encodeStateVector(ydoc1)
974
+ const stateVector2 = Y.encodeStateVector(ydoc2)
975
+ const diff1 = Y.encodeStateAsUpdate(ydoc1, stateVector2)
976
+ const diff2 = Y.encodeStateAsUpdate(ydoc2, stateVector1)
977
+ Y.applyUpdate(ydoc1, diff2)
978
+ Y.applyUpdate(ydoc2, diff1)
979
+ ```
980
+
981
+ #### Example: Syncing clients without loading the Y.Doc
982
+
983
+ It is possible to sync clients and compute delta updates without loading the Yjs
984
+ document to memory. Yjs exposes an API to compute the differences directly on the
985
+ binary document updates.
986
+
987
+ ```js
988
+ // encode the current state as a binary buffer
989
+ let currentState1 = Y.encodeStateAsUpdate(ydoc1)
990
+ let currentState2 = Y.encodeStateAsUpdate(ydoc2)
991
+ // now we can continue syncing clients using state vectors without using the Y.Doc
992
+ ydoc1.destroy()
993
+ ydoc2.destroy()
994
+
995
+ const stateVector1 = Y.encodeStateVectorFromUpdate(currentState1)
996
+ const stateVector2 = Y.encodeStateVectorFromUpdate(currentState2)
997
+ const diff1 = Y.diffUpdate(currentState1, stateVector2)
998
+ const diff2 = Y.diffUpdate(currentState2, stateVector1)
999
+
1000
+ // sync clients
1001
+ currentState1 = Y.mergeUpdates([currentState1, diff2])
1002
+ currentState2 = Y.mergeUpdates([currentState2, diff1])
1003
+ ```
1004
+
1005
+ #### Obfuscating Updates
1006
+
1007
+ If one of your users runs into a weird bug (e.g. the rich-text editor throws
1008
+ error messages), then you don't have to request the full document from your
1009
+ user. Instead, they can obfuscate the document (i.e. replace the content with
1010
+ meaningless generated content) before sending it to you. Note that someone might
1011
+ still deduce the type of content by looking at the general structure of the
1012
+ document. But this is much better than requesting the original document.
1013
+
1014
+ Obfuscated updates contain all the CRDT-related data that is required for
1015
+ merging. So it is safe to merge obfuscated updates.
1016
+
1017
+ ```javascript
1018
+ const ydoc = new Y.Doc()
1019
+ // perform some changes..
1020
+ ydoc.getText().insert(0, 'hello world')
1021
+ const update = Y.encodeStateAsUpdate(ydoc)
1022
+ // the below update contains scrambled data
1023
+ const obfuscatedUpdate = Y.obfuscateUpdate(update)
1024
+ const ydoc2 = new Y.Doc()
1025
+ Y.applyUpdate(ydoc2, obfuscatedUpdate)
1026
+ ydoc2.getText().toString() // => "00000000000"
1027
+ ```
1028
+
1029
+ #### Using V2 update format
1030
+
1031
+ Yjs implements two update formats. By default you are using the V1 update format.
1032
+ You can opt-in into the V2 update format which provides much better compression.
1033
+ It is not yet used by all providers. However, you can already use it if
1034
+ you are building your own provider. All below functions are available with the
1035
+ suffix "V2". E.g. `Y.applyUpdate` ⇒ `Y.applyUpdateV2`. Also when listening to updates
1036
+ you need to specifically need listen for V2 events e.g. `yDoc.on('updateV2', …)`.
1037
+ We also support conversion functions between both formats:
1038
+ `Y.convertUpdateFormatV1ToV2` & `Y.convertUpdateFormatV2ToV1`.
1039
+
1040
+ #### Update API
1041
+
1042
+ <dl>
1043
+ <b><code>Y.applyUpdate(Y.Doc, update:Uint8Array, [transactionOrigin:any])</code></b>
1044
+ <dd>
1045
+ Apply a document update on the shared document. Optionally you can specify
1046
+ <code>transactionOrigin</code> that will be stored on
1047
+ <code>transaction.origin</code>
1048
+ and <code>ydoc.on('update', (update, origin) => ..)</code>.
1049
+ </dd>
1050
+ <b><code>Y.encodeStateAsUpdate(Y.Doc, [encodedTargetStateVector:Uint8Array]):Uint8Array</code></b>
1051
+ <dd>
1052
+ Encode the document state as a single update message that can be applied on the
1053
+ remote document. Optionally specify the target state vector to only write the
1054
+ differences to the update message.
1055
+ </dd>
1056
+ <b><code>Y.encodeStateVector(Y.Doc):Uint8Array</code></b>
1057
+ <dd>Computes the state vector and encodes it into an Uint8Array.</dd>
1058
+ <b><code>Y.mergeUpdates(Array&lt;Uint8Array&gt;)</code></b>
1059
+ <dd>
1060
+ Merge several document updates into a single document update while removing
1061
+ duplicate information. The merged document update is always smaller than
1062
+ the separate updates because of the compressed encoding.
1063
+ </dd>
1064
+ <b><code>Y.encodeStateVectorFromUpdate(Uint8Array): Uint8Array</code></b>
1065
+ <dd>
1066
+ Computes the state vector from a document update and encodes it into an Uint8Array.
1067
+ </dd>
1068
+ <b><code>Y.diffUpdate(update: Uint8Array, stateVector: Uint8Array): Uint8Array</code></b>
1069
+ <dd>
1070
+ Encode the missing differences to another update message. This function works
1071
+ similarly to <code>Y.encodeStateAsUpdate(ydoc, stateVector)</code> but works
1072
+ on updates instead.
1073
+ </dd>
1074
+ <b><code>convertUpdateFormatV1ToV2</code></b>
1075
+ <dd>
1076
+ Convert V1 update format to the V2 update format.
1077
+ </dd>
1078
+ <b><code>convertUpdateFormatV2ToV1</code></b>
1079
+ <dd>
1080
+ Convert V2 update format to the V1 update format.
1081
+ </dd>
1082
+ </dl>
1083
+
1084
+ ### Relative Positions
1085
+
1086
+ When working with collaborative documents, we often need to work with positions.
1087
+ Positions may represent cursor locations, selection ranges, or even assign a
1088
+ comment to a range of text. Normal index-positions (expressed as integers) are
1089
+ not convenient to use because the index-range is invalidated as soon as a remote
1090
+ change manipulates the document. Relative positions give you a powerful API to
1091
+ express positions.
1092
+
1093
+ A relative position is fixated to an element in the shared document and is not
1094
+ affected by remote changes. I.e. given the document `"a|c"`, the relative
1095
+ position is attached to `c`. When a remote user modifies the document by
1096
+ inserting a character before the cursor, the cursor will stay attached to the
1097
+ character `c`. `insert(1, 'x')("a|c") = "ax|c"`. When the relative position is
1098
+ set to the end of the document, it will stay attached to the end of the
1099
+ document.
1100
+
1101
+ #### Example: Transform to RelativePosition and back
1102
+
1103
+ ```js
1104
+ const relPos = Y.createRelativePositionFromTypeIndex(ytext, 2)
1105
+ const pos = Y.createAbsolutePositionFromRelativePosition(relPos, doc)
1106
+ pos.type === ytext // => true
1107
+ pos.index === 2 // => true
1108
+ ```
1109
+
1110
+ #### Example: Send relative position to remote client (json)
1111
+
1112
+ ```js
1113
+ const relPos = Y.createRelativePositionFromTypeIndex(ytext, 2)
1114
+ const encodedRelPos = JSON.stringify(relPos)
1115
+ // send encodedRelPos to remote client..
1116
+ const parsedRelPos = JSON.parse(encodedRelPos)
1117
+ const pos = Y.createAbsolutePositionFromRelativePosition(parsedRelPos, remoteDoc)
1118
+ pos.type === remoteytext // => true
1119
+ pos.index === 2 // => true
1120
+ ```
1121
+
1122
+ #### Example: Send relative position to remote client (Uint8Array)
1123
+
1124
+ ```js
1125
+ const relPos = Y.createRelativePositionFromTypeIndex(ytext, 2)
1126
+ const encodedRelPos = Y.encodeRelativePosition(relPos)
1127
+ // send encodedRelPos to remote client..
1128
+ const parsedRelPos = Y.decodeRelativePosition(encodedRelPos)
1129
+ const pos = Y.createAbsolutePositionFromRelativePosition(parsedRelPos, remoteDoc)
1130
+ pos.type === remoteytext // => true
1131
+ pos.index === 2 // => true
1132
+ ```
1133
+
1134
+ <dl>
1135
+ <b><code>
1136
+ Y.createRelativePositionFromTypeIndex(type:Uint8Array|Y.Type, index: number
1137
+ [, assoc=0])
1138
+ </code></b>
1139
+ <dd>
1140
+ Create a relative position fixated to the i-th element in any sequence-like
1141
+ shared type (if <code>assoc >= 0</code>). By default, the position associates
1142
+ with the character that comes after the specified index position. If
1143
+ <code>assoc < 0</code>, then the relative position associates with the character
1144
+ before the specified index position.
1145
+ </dd>
1146
+ <b><code>
1147
+ Y.createAbsolutePositionFromRelativePosition(RelativePosition, Y.Doc):
1148
+ { type: Y.AbstractType, index: number, assoc: number } | null
1149
+ </code></b>
1150
+ <dd>
1151
+ Create an absolute position from a relative position. If the relative position
1152
+ cannot be referenced, or the type is deleted, then the result is null.
1153
+ </dd>
1154
+ <b><code>
1155
+ Y.encodeRelativePosition(RelativePosition):Uint8Array
1156
+ </code></b>
1157
+ <dd>
1158
+ Encode a relative position to an Uint8Array. Binary data is the preferred
1159
+ encoding format for document updates. If you prefer JSON encoding, you can
1160
+ simply JSON.stringify / JSON.parse the relative position instead.
1161
+ </dd>
1162
+ <b><code>Y.decodeRelativePosition(Uint8Array):RelativePosition</code></b>
1163
+ <dd>Decode a binary-encoded relative position to a RelativePosition object.</dd>
1164
+ </dl>
1165
+
1166
+ ### Y.UndoManager
1167
+
1168
+ Yjs ships with an Undo/Redo manager for selective undo/redo of changes on a
1169
+ Yjs type. The changes can be optionally scoped to transaction origins.
1170
+
1171
+ ```js
1172
+ const ytext = doc.getText('text')
1173
+ const undoManager = new Y.UndoManager(ytext)
1174
+
1175
+ ytext.insert(0, 'abc')
1176
+ undoManager.undo()
1177
+ ytext.toString() // => ''
1178
+ undoManager.redo()
1179
+ ytext.toString() // => 'abc'
1180
+ ```
1181
+
1182
+ <dl>
1183
+ <b><code>constructor(scope:Y.AbstractType|Array&lt;Y.AbstractType&gt;
1184
+ [, {captureTimeout:number,trackedOrigins:Set&lt;any&gt;,deleteFilter:function(item):boolean}])</code></b>
1185
+ <dd>Accepts either single type as scope or an array of types.</dd>
1186
+ <b><code>undo()</code></b>
1187
+ <dd></dd>
1188
+ <b><code>redo()</code></b>
1189
+ <dd></dd>
1190
+ <b><code>stopCapturing()</code></b>
1191
+ <dd></dd>
1192
+ <b>
1193
+ <code>
1194
+ on('stack-item-added', { stackItem: { meta: Map&lt;any,any&gt; }, type: 'undo'
1195
+ | 'redo' })
1196
+ </code>
1197
+ </b>
1198
+ <dd>
1199
+ Register an event that is called when a <code>StackItem</code> is added to the
1200
+ undo- or the redo-stack.
1201
+ </dd>
1202
+ <b>
1203
+ <code>
1204
+ on('stack-item-updated', { stackItem: { meta: Map&lt;any,any&gt; }, type: 'undo'
1205
+ | 'redo' })
1206
+ </code>
1207
+ </b>
1208
+ <dd>
1209
+ Register an event that is called when an existing <code>StackItem</code> is updated.
1210
+ This happens when two changes happen within a "captureInterval".
1211
+ </dd>
1212
+ <b>
1213
+ <code>
1214
+ on('stack-item-popped', { stackItem: { meta: Map&lt;any,any&gt; }, type: 'undo'
1215
+ | 'redo' })
1216
+ </code>
1217
+ </b>
1218
+ <dd>
1219
+ Register an event that is called when a <code>StackItem</code> is popped from
1220
+ the undo- or the redo-stack.
1221
+ </dd>
1222
+ <b>
1223
+ <code>
1224
+ on('stack-cleared', { undoStackCleared: boolean, redoStackCleared: boolean })
1225
+ </code>
1226
+ </b>
1227
+ <dd>
1228
+ Register an event that is called when the undo- and/or the redo-stack is cleared.
1229
+ </dd>
1230
+ </dl>
1231
+
1232
+ #### Example: Stop Capturing
1233
+
1234
+ UndoManager merges Undo-StackItems if they are created within time-gap
1235
+ smaller than `options.captureTimeout`. Call `um.stopCapturing()` so that the next
1236
+ StackItem won't be merged.
1237
+
1238
+ ```js
1239
+ // without stopCapturing
1240
+ ytext.insert(0, 'a')
1241
+ ytext.insert(1, 'b')
1242
+ undoManager.undo()
1243
+ ytext.toString() // => '' (note that 'ab' was removed)
1244
+ // with stopCapturing
1245
+ ytext.insert(0, 'a')
1246
+ undoManager.stopCapturing()
1247
+ ytext.insert(0, 'b')
1248
+ undoManager.undo()
1249
+ ytext.toString() // => 'a' (note that only 'b' was removed)
1250
+ ```
1251
+
1252
+ #### Example: Specify tracked origins
1253
+
1254
+ Every change on the shared document has an origin. If no origin was specified,
1255
+ it defaults to `null`. By specifying `trackedOrigins` you can
1256
+ selectively specify which changes should be tracked by `UndoManager`. The
1257
+ UndoManager instance is always added to `trackedOrigins`.
1258
+
1259
+ ```js
1260
+ class CustomBinding {}
1261
+
1262
+ const ytext = doc.getText('text')
1263
+ const undoManager = new Y.UndoManager(ytext, {
1264
+ trackedOrigins: new Set([42, CustomBinding])
1265
+ })
1266
+
1267
+ ytext.insert(0, 'abc')
1268
+ undoManager.undo()
1269
+ ytext.toString() // => 'abc' (does not track because origin `null` and not part
1270
+ // of `trackedTransactionOrigins`)
1271
+ ytext.delete(0, 3) // revert change
1272
+
1273
+ doc.transact(() => {
1274
+ ytext.insert(0, 'abc')
1275
+ }, 42)
1276
+ undoManager.undo()
1277
+ ytext.toString() // => '' (tracked because origin is an instance of `trackedTransactionorigins`)
1278
+
1279
+ doc.transact(() => {
1280
+ ytext.insert(0, 'abc')
1281
+ }, 41)
1282
+ undoManager.undo()
1283
+ ytext.toString() // => 'abc' (not tracked because 41 is not an instance of
1284
+ // `trackedTransactionorigins`)
1285
+ ytext.delete(0, 3) // revert change
1286
+
1287
+ doc.transact(() => {
1288
+ ytext.insert(0, 'abc')
1289
+ }, new CustomBinding())
1290
+ undoManager.undo()
1291
+ ytext.toString() // => '' (tracked because origin is a `CustomBinding` and
1292
+ // `CustomBinding` is in `trackedTransactionorigins`)
1293
+ ```
1294
+
1295
+ #### Example: Add additional information to the StackItems
1296
+
1297
+ When undoing or redoing a previous action, it is often expected to restore
1298
+ additional meta information like the cursor location or the view on the
1299
+ document. You can assign meta-information to Undo-/Redo-StackItems.
1300
+
1301
+ ```js
1302
+ const ytext = doc.getText('text')
1303
+ const undoManager = new Y.UndoManager(ytext, {
1304
+ trackedOrigins: new Set([42, CustomBinding])
1305
+ })
1306
+
1307
+ undoManager.on('stack-item-added', event => {
1308
+ // save the current cursor location on the stack-item
1309
+ event.stackItem.meta.set('cursor-location', getRelativeCursorLocation())
1310
+ })
1311
+
1312
+ undoManager.on('stack-item-popped', event => {
1313
+ // restore the current cursor location on the stack-item
1314
+ restoreCursorLocation(event.stackItem.meta.get('cursor-location'))
1315
+ })
1316
+ ```
1317
+
1318
+ ## Yjs CRDT Algorithm
1319
+
1320
+ *Conflict-free replicated data types* (CRDT) for collaborative editing are an
1321
+ alternative approach to *operational transformation* (OT). A very simple
1322
+ differentiation between the two approaches is that OT attempts to transform
1323
+ index positions to ensure convergence (all clients end up with the same
1324
+ content), while CRDTs use mathematical models that usually do not involve index
1325
+ transformations, like linked lists. OT is currently the de-facto standard for
1326
+ shared editing on text. OT approaches that support shared editing without a
1327
+ central source of truth (a central server) require too much bookkeeping to be
1328
+ viable in practice. CRDTs are better suited for distributed systems, provide
1329
+ additional guarantees that the document can be synced with remote clients, and
1330
+ do not require a central source of truth.
1331
+
1332
+ Yjs implements a modified version of the algorithm described in [this
1333
+ paper](https://www.researchgate.net/publication/310212186_Near_Real-Time_Peer-to-Peer_Shared_Editing_on_Extensible_Data_Types).
1334
+ This [article](https://blog.kevinjahns.de/are-crdts-suitable-for-shared-editing/)
1335
+ explains a simple optimization on the CRDT model and
1336
+ gives more insight about the performance characteristics in Yjs.
1337
+ More information about the specific implementation is available in
1338
+ [INTERNALS.md](./INTERNALS.md) and in
1339
+ [this walkthrough of the Yjs codebase](https://youtu.be/0l5XgnQ6rB4).
1340
+
1341
+ CRDTs that are suitable for shared text editing suffer from the fact that they
1342
+ only grow in size. There are CRDTs that do not grow in size, but they do not
1343
+ have the characteristics that are beneficial for shared text editing (like
1344
+ intention preservation). Yjs implements many improvements to the original
1345
+ algorithm that diminish the trade-off that the document only grows in size. We
1346
+ can't garbage collect deleted structs (tombstones) while ensuring a unique
1347
+ order of the structs. But we can 1. merge preceding structs into a single
1348
+ struct to reduce the amount of meta information, 2. we can delete content from
1349
+ the struct if it is deleted, and 3. we can garbage collect tombstones if we
1350
+ don't care about the order of the structs anymore (e.g. if the parent was
1351
+ deleted).
1352
+
1353
+ **Examples:**
1354
+
1355
+ 1. If a user inserts elements in sequence, the struct will be merged into a
1356
+ single struct. E.g. `text.insert(0, 'a'), text.insert(1, 'b');` is
1357
+ first represented as two structs (`[{id: {client, clock: 0}, content: 'a'},
1358
+ {id: {client, clock: 1}, content: 'b'}`) and then merged into a single
1359
+ struct: `[{id: {client, clock: 0}, content: 'ab'}]`.
1360
+ 2. When a struct that contains content (e.g. `ItemString`) is deleted, the
1361
+ struct will be replaced with an `ItemDeleted` that does not contain content
1362
+ anymore.
1363
+ 3. When a type is deleted, all child elements are transformed to `GC` structs. A
1364
+ `GC` struct only denotes the existence of a struct and that it is deleted.
1365
+ `GC` structs can always be merged with other `GC` structs if the id's are
1366
+ adjacent.
1367
+
1368
+ Especially when working on structured content (e.g. shared editing on
1369
+ ProseMirror), these improvements yield very good results when
1370
+ [benchmarking](https://github.com/dmonad/crdt-benchmarks) random document edits.
1371
+ In practice they show even better results, because users usually edit text in
1372
+ sequence, resulting in structs that can easily be merged. The benchmarks show
1373
+ that even in the worst case scenario that a user edits text from right to left,
1374
+ Yjs achieves good performance even for huge documents.
1375
+
1376
+ ### State Vector
1377
+
1378
+ Yjs has the ability to exchange only the differences when syncing two clients.
1379
+ We use lamport timestamps to identify structs and to track in which order a
1380
+ client created them. Each struct has an `struct.id = { client: number, clock:
1381
+ number}` that uniquely identifies a struct. We define the next expected `clock`
1382
+ by each client as the *state vector*. This data structure is similar to the
1383
+ [version vectors](https://en.wikipedia.org/wiki/Version_vector) data structure.
1384
+ But we use state vectors only to describe the state of the local document, so we
1385
+ can compute the missing struct of the remote client. We do not use it to track
1386
+ causality.
1387
+
1388
+ ### Formal Proof
1389
+
1390
+ [lean-yjs](https://github.com/iasakura/lean-yjs) provides a formal verification
1391
+ of the YATA CRDT algorithm that Yjs implements, using the Lean theorem prover to
1392
+ mathematically prove correctness properties. While the CRDT algorithm itself is
1393
+ correct (currently proven for preservation and commutativity), the project
1394
+ reveals that [the pseudocode in the original YATA paper contains
1395
+ errors](https://discuss.yjs.dev/t/lean-yjs-formally-proving-the-yjs-conflict-resolution-algorithms/3875/2).
1396
+
1397
+ ## License and Author
1398
+
1399
+ Yjs and all related projects are [**MIT licensed**](./LICENSE).
1400
+
1401
+ Yjs is based on my research as a student at the [RWTH
1402
+ i5](http://dbis.rwth-aachen.de/). Now I am working on Yjs in my spare time.
1403
+
1404
+ Fund this project by donating on [GitHub Sponsors](https://github.com/sponsors/dmonad)
1405
+ or hiring [me](https://github.com/dmonad) as a contractor for your collaborative
1406
+ app.