Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
415a645874 | ||
|
|
1cb52dc863 | ||
|
|
7a8ca6eaa5 | ||
|
|
e348255bb1 | ||
|
|
79c095d4dc | ||
|
|
0241fd3c40 | ||
|
|
cf78ce12b2 | ||
|
|
77bd74127d | ||
|
|
fe36ffd122 | ||
|
|
28ccd5e0dd | ||
|
|
1d4f2e5435 | ||
|
|
2c0daeb071 |
99
README.md
99
README.md
@@ -32,13 +32,30 @@ Otherwise you can find help on our community [discussion board](https://discuss.
|
||||
Please contribute to the project financially - especially if your company relies
|
||||
on Yjs. [](https://github.com/sponsors/dmonad)
|
||||
|
||||
## Professional Support
|
||||
|
||||
* [Support Contract with the Maintainer](https://github.com/sponsors/dmonad) -
|
||||
By contributing financially to the open-source Yjs project, you can receive
|
||||
professional support directly from the author. This includes the opportunity for
|
||||
weekly video calls to discuss your specific challenges.
|
||||
* [Synergy Codes](https://synergycodes.com/yjs-services/) - Specializing in
|
||||
consulting and developing real-time collaborative editing solutions for visual
|
||||
apps, Synergy Codes focuses on interactive diagrams, complex graphs, charts, and
|
||||
various data visualization types. Their expertise empowers developers to build
|
||||
engaging and interactive visual experiences leveraging the power of Yjs. See
|
||||
their work in action at [Visual Collaboration
|
||||
Showcase](https://yjs-diagram.synergy.codes/).
|
||||
|
||||
## Who is using Yjs
|
||||
|
||||
* [AFFiNE](https://affine.pro/) A local-first, privacy-first, open source
|
||||
knowledge base. 🏅
|
||||
* [Dynaboard](https://dynaboard.com/) Build web apps collaboratively. :star2:
|
||||
* [Sana](https://sanalabs.com/) A learning platform with collaborative text
|
||||
editing powered by Yjs.
|
||||
* [Cargo](https://cargo.site/) Site builder for designers and artists :star2:
|
||||
* [Gitbook](https://gitbook.com) Knowledge management for technical teams :star2:
|
||||
* [Evernote](https://evernote.com) Note-taking app :star2:
|
||||
* [Lessonspace](https://thelessonspace.com) Enterprise platform for virtual
|
||||
classrooms and online training :star2:
|
||||
* [Dynaboard](https://dynaboard.com/) Build web apps collaboratively. :star:
|
||||
* [Relm](https://www.relm.us/) A collaborative gameworld for teamwork and
|
||||
community. :star:
|
||||
* [Room.sh](https://room.sh/) A meeting application with integrated
|
||||
@@ -47,6 +64,8 @@ on Yjs. [ A web-based app to
|
||||
collaboratively organize radio broadcasts. :star:
|
||||
* [Sana](https://sanalabs.com/) A learning platform with collaborative text
|
||||
editing powered by Yjs.
|
||||
* [Serenity Notes](https://www.serenity.re/en/notes) End-to-end encrypted
|
||||
collaborative notes app.
|
||||
* [PRSM](https://prsm.uk/) Collaborative mind-mapping and system visualisation. *[(source)](https://github.com/micrology/prsm)*
|
||||
@@ -56,6 +75,9 @@ on Yjs. [ Presentation app.
|
||||
* [BlockSurvey](https://blocksurvey.io) End-to-end encryption for your forms/surveys.
|
||||
* [Skiff](https://skiff.org/) Private, decentralized workspace.
|
||||
* [JupyterLab](https://jupyter.org/) Collaborative computational Notebooks
|
||||
* [JupyterCad](https://jupytercad.readthedocs.io/en/latest/) Extension to
|
||||
JupyterLab that enables collaborative editing of 3d FreeCAD Models.
|
||||
* [Hyperquery](https://hyperquery.ai/) A collaborative data workspace for
|
||||
sharing analyses, documentation, spreadsheets, and dashboards.
|
||||
* [Nosgestesclimat](https://nosgestesclimat.fr/groupe) The french carbon
|
||||
@@ -66,6 +88,9 @@ on Yjs. [ Build courses and content with A.I.
|
||||
* [btw](https://www.btw.so) Open-source Medium alternative
|
||||
* [AWS SageMaker](https://aws.amazon.com/sagemaker/) Tools for building Machine
|
||||
Learning Models
|
||||
* [linear](https://linear.app) Streamline issues, projects, and product roadmaps.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@@ -102,6 +127,7 @@ are implemented in separate modules.
|
||||
| [valtio](https://github.com/pmndrs/valtio) | | [valtio-yjs](https://github.com/dai-shi/valtio-yjs) | [demo](https://codesandbox.io/s/valtio-yjs-demo-ox3iy) |
|
||||
| [immer](https://github.com/immerjs/immer) | | [immer-yjs](https://github.com/sep2/immer-yjs) | [demo](https://codesandbox.io/s/immer-yjs-demo-6e0znb) |
|
||||
| React / Vue / Svelte / MobX | | [SyncedStore](https://syncedstore.org) | [demo](https://syncedstore.org/docs/react) |
|
||||
| [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) |
|
||||
|
||||
### Providers
|
||||
|
||||
@@ -110,7 +136,19 @@ and storing shared data for offline usage is quite a hassle. **Providers**
|
||||
manage all that for you and are the perfect starting point for your
|
||||
collaborative app.
|
||||
|
||||
> This list of providers is incomplete. Please open PRs to add your providers to
|
||||
> this list!
|
||||
|
||||
#### Connection Providers
|
||||
|
||||
<dl>
|
||||
<dt><a href="https://github.com/yjs/y-websocket">y-websocket</a></dt>
|
||||
<dd>
|
||||
A module that contains a simple websocket backend and a websocket client that
|
||||
connects to that backend. The backend can be extended to persist updates in a
|
||||
leveldb database. <b>y-sweet</b> and <b>ypy-websocket</b> (see below) are
|
||||
compatible to the y-wesocket protocol.
|
||||
</dd>
|
||||
<dt><a href="https://github.com/yjs/y-webrtc">y-webrtc</a></dt>
|
||||
<dd>
|
||||
Propagates document updates peer-to-peer using WebRTC. The peers exchange
|
||||
@@ -119,17 +157,22 @@ are available. Communication over the signaling servers can be encrypted by
|
||||
providing a shared secret, keeping the connection information and the shared
|
||||
document private.
|
||||
</dd>
|
||||
<dt><a href="https://github.com/yjs/y-websocket">y-websocket</a></dt>
|
||||
<dt><a href="https://github.com/liveblocks/liveblocks">@liveblocks/yjs</a></dt>
|
||||
<dd>
|
||||
A module that contains a simple websocket backend and a websocket client that
|
||||
connects to that backend. The backend can be extended to persist updates in a
|
||||
leveldb database.
|
||||
<a href="https://liveblocks.io/document/yjs">Liveblocks Yjs</a> provides a fully
|
||||
hosted WebSocket infrastructure and persisted data store for Yjs
|
||||
documents. No configuration or maintenance is required. It also features
|
||||
Yjs webhook events, REST API to read and update Yjs documents, and a
|
||||
browser DevTools extension.
|
||||
</dd>
|
||||
<dt><a href="https://github.com/yjs/y-indexeddb">y-indexeddb</a></dt>
|
||||
<dt><a href="https://github.com/drifting-in-space/y-sweet">y-sweet</a></dt>
|
||||
<dd>
|
||||
Efficiently persists document updates to the browsers indexeddb database.
|
||||
The document is immediately available and only diffs need to be synced through the
|
||||
network provider.
|
||||
A standalone yjs server with persistence to S3 or filesystem. They offer a
|
||||
<a href="https://y-sweet.cloud">cloud service</a> as well.
|
||||
</dd>
|
||||
<dt><a href="https://docs.partykit.io/reference/y-partykit-api/">PartyKit</a></dt>
|
||||
<dd>
|
||||
Cloud service for building multiplayer apps.
|
||||
</dd>
|
||||
<dt><a href="https://github.com/marcopolo/y-libp2p">y-libp2p</a></dt>
|
||||
<dd>
|
||||
@@ -144,14 +187,6 @@ Also includes a peer-sync mechanism to catch up on missed updates.
|
||||
an append-only log of CRDT local updates (hypercore). Multifeed manages and sync
|
||||
hypercores and y-dat listens to changes and applies them to the Yjs document.
|
||||
</dd>
|
||||
<dt><a href="https://github.com/liveblocks/liveblocks">@liveblocks/yjs</a></dt>
|
||||
<dd>
|
||||
<a href="https://liveblocks.io/document/yjs">Liveblocks Yjs</a> provides a fully
|
||||
hosted WebSocket infrastructure and persisted data store for Yjs
|
||||
documents. No configuration or maintenance is required. It also features
|
||||
Yjs webhook events, REST API to read and update Yjs documents, and a
|
||||
browser DevTools extension.
|
||||
</dd>
|
||||
<dt><a href="https://github.com/yousefED/matrix-crdt">Matrix-CRDT</a></dt>
|
||||
<dd>
|
||||
Use <a href="https://www.matrix.org">Matrix</a> as an off-the-shelf backend for
|
||||
@@ -160,17 +195,37 @@ Use Matrix as transport and storage of Yjs updates, so you can focus building
|
||||
your client app and Matrix can provide powerful features like Authentication,
|
||||
Authorization, Federation, hosting (self-hosting or SaaS) and even End-to-End
|
||||
Encryption (E2EE).
|
||||
</dd>
|
||||
</dd>
|
||||
<dt><a href="https://github.com/y-crdt/yrb-actioncable">yrb-actioncable</a></dt>
|
||||
<dd>
|
||||
An ActionCable companion for Yjs clients. There is a fitting
|
||||
<a href="https://github.com/y-crdt/yrb-redis">redis extension</a> as well.
|
||||
</dd>
|
||||
<dt><a href="https://github.com/y-crdt/ypy-websocket">ypy-websocket</a></dt>
|
||||
<dd>
|
||||
Websocket backend, written in Python.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
#### Persistence Providers
|
||||
|
||||
<dl>
|
||||
<dt><a href="https://github.com/yjs/y-indexeddb">y-indexeddb</a></dt>
|
||||
<dd>
|
||||
Efficiently persists document updates to the browsers indexeddb database.
|
||||
The document is immediately available and only diffs need to be synced through the
|
||||
network provider.
|
||||
</dd>
|
||||
<dt><a href="https://github.com/MaxNoetzold/y-mongodb-provider">y-mongodb-provider</a></dt>
|
||||
<dd>
|
||||
Adds persistent storage to a server with MongoDB. Can be used with the
|
||||
y-websocket provider.
|
||||
</dd>
|
||||
</dd>
|
||||
<dt><a href="https://github.com/toeverything/AFFiNE/tree/master/packages/y-indexeddb">
|
||||
@toeverything/y-indexeddb</a></dt>
|
||||
<dd>
|
||||
Like y-indexeddb, but with sub-documents support and fully TypeScript.
|
||||
</dd>
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
# Ports
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "yjs",
|
||||
"version": "13.6.9",
|
||||
"version": "13.6.11",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "yjs",
|
||||
"version": "13.6.9",
|
||||
"version": "13.6.11",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lib0": "^0.2.86"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "yjs",
|
||||
"version": "13.6.9",
|
||||
"version": "13.6.11",
|
||||
"description": "Shared Editing Library",
|
||||
"main": "./dist/yjs.cjs",
|
||||
"module": "./dist/yjs.mjs",
|
||||
|
||||
@@ -52,6 +52,7 @@ export {
|
||||
getItem,
|
||||
typeListToArraySnapshot,
|
||||
typeMapGetSnapshot,
|
||||
typeMapGetAllSnapshot,
|
||||
createDocFromSnapshot,
|
||||
iterateDeletedStructs,
|
||||
applyUpdate,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
export * from './utils/AbstractConnector.js'
|
||||
export * from './utils/DeleteSet.js'
|
||||
export * from './utils/Doc.js'
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import {
|
||||
UpdateEncoderV1, UpdateEncoderV2, ID, Transaction // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import {
|
||||
addToDeleteSet,
|
||||
UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, StructStore, Item, Transaction // eslint-disable-line
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import {
|
||||
Doc, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction, Item // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import {
|
||||
UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, StructStore, Item, Transaction // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import {
|
||||
YText, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Item, StructStore, Transaction // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import {
|
||||
readYArray,
|
||||
readYMap,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import {
|
||||
AbstractStruct,
|
||||
addStruct,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import {
|
||||
GC,
|
||||
getState,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import {
|
||||
AbstractStruct,
|
||||
UpdateEncoderV1, UpdateEncoderV2, StructStore, Transaction, ID // eslint-disable-line
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import {
|
||||
removeEventHandlerListener,
|
||||
callEventHandlerListeners,
|
||||
@@ -925,6 +924,34 @@ export const typeMapGetSnapshot = (parent, key, snapshot) => {
|
||||
return v !== null && isVisible(v, snapshot) ? v.content.getContent()[v.length - 1] : undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {AbstractType<any>} parent
|
||||
* @param {Snapshot} snapshot
|
||||
* @return {Object<string,Object<string,any>|number|null|Array<any>|string|Uint8Array|AbstractType<any>|undefined>}
|
||||
*
|
||||
* @private
|
||||
* @function
|
||||
*/
|
||||
export const typeMapGetAllSnapshot = (parent, snapshot) => {
|
||||
/**
|
||||
* @type {Object<string,any>}
|
||||
*/
|
||||
const res = {}
|
||||
parent._map.forEach((value, key) => {
|
||||
/**
|
||||
* @type {Item|null}
|
||||
*/
|
||||
let v = value
|
||||
while (v !== null && (!snapshot.sv.has(v.id.client) || v.id.clock >= (snapshot.sv.get(v.id.client) || 0))) {
|
||||
v = v.left
|
||||
}
|
||||
if (v !== null && isVisible(v, snapshot)) {
|
||||
res[key] = v.content.getContent()[v.length - 1]
|
||||
}
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Map<string,Item>} map
|
||||
* @return {IterableIterator<Array<any>>}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @module YMap
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @module YText
|
||||
*/
|
||||
@@ -118,14 +117,15 @@ const findNextPosition = (transaction, pos, count) => {
|
||||
* @param {Transaction} transaction
|
||||
* @param {AbstractType<any>} parent
|
||||
* @param {number} index
|
||||
* @param {boolean} useSearchMarker
|
||||
* @return {ItemTextListPosition}
|
||||
*
|
||||
* @private
|
||||
* @function
|
||||
*/
|
||||
const findPosition = (transaction, parent, index) => {
|
||||
const findPosition = (transaction, parent, index, useSearchMarker) => {
|
||||
const currentAttributes = new Map()
|
||||
const marker = findMarker(parent, index)
|
||||
const marker = useSearchMarker ? findMarker(parent, index) : null
|
||||
if (marker) {
|
||||
const pos = new ItemTextListPosition(marker.p.left, marker.p, marker.index, currentAttributes)
|
||||
return findNextPosition(transaction, pos, index - marker.index)
|
||||
@@ -1120,7 +1120,7 @@ export class YText extends AbstractType {
|
||||
const y = this.doc
|
||||
if (y !== null) {
|
||||
transact(y, transaction => {
|
||||
const pos = findPosition(transaction, this, index)
|
||||
const pos = findPosition(transaction, this, index, !attributes)
|
||||
if (!attributes) {
|
||||
attributes = {}
|
||||
// @ts-ignore
|
||||
@@ -1138,20 +1138,20 @@ export class YText extends AbstractType {
|
||||
*
|
||||
* @param {number} index The index to insert the embed at.
|
||||
* @param {Object | AbstractType<any>} embed The Object that represents the embed.
|
||||
* @param {TextAttributes} attributes Attribute information to apply on the
|
||||
* @param {TextAttributes} [attributes] Attribute information to apply on the
|
||||
* embed
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
insertEmbed (index, embed, attributes = {}) {
|
||||
insertEmbed (index, embed, attributes) {
|
||||
const y = this.doc
|
||||
if (y !== null) {
|
||||
transact(y, transaction => {
|
||||
const pos = findPosition(transaction, this, index)
|
||||
insertText(transaction, this, pos, embed, attributes)
|
||||
const pos = findPosition(transaction, this, index, !attributes)
|
||||
insertText(transaction, this, pos, embed, attributes || {})
|
||||
})
|
||||
} else {
|
||||
/** @type {Array<function>} */ (this._pending).push(() => this.insertEmbed(index, embed, attributes))
|
||||
/** @type {Array<function>} */ (this._pending).push(() => this.insertEmbed(index, embed, attributes || {}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1170,7 +1170,7 @@ export class YText extends AbstractType {
|
||||
const y = this.doc
|
||||
if (y !== null) {
|
||||
transact(y, transaction => {
|
||||
deleteText(transaction, findPosition(transaction, this, index), length)
|
||||
deleteText(transaction, findPosition(transaction, this, index, true), length)
|
||||
})
|
||||
} else {
|
||||
/** @type {Array<function>} */ (this._pending).push(() => this.delete(index, length))
|
||||
@@ -1194,7 +1194,7 @@ export class YText extends AbstractType {
|
||||
const y = this.doc
|
||||
if (y !== null) {
|
||||
transact(y, transaction => {
|
||||
const pos = findPosition(transaction, this, index)
|
||||
const pos = findPosition(transaction, this, index, false)
|
||||
if (pos.right === null) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -8,9 +8,10 @@ import {
|
||||
typeMapSet,
|
||||
typeMapGet,
|
||||
typeMapGetAll,
|
||||
typeMapGetAllSnapshot,
|
||||
typeListForEach,
|
||||
YXmlElementRefID,
|
||||
YXmlText, ContentType, AbstractType, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item // eslint-disable-line
|
||||
Snapshot, YXmlText, ContentType, AbstractType, UpdateDecoderV1, UpdateDecoderV2, UpdateEncoderV1, UpdateEncoderV2, Doc, Item // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
/**
|
||||
@@ -192,12 +193,13 @@ export class YXmlElement extends YXmlFragment {
|
||||
/**
|
||||
* Returns all attribute name/value pairs in a JSON Object.
|
||||
*
|
||||
* @param {Snapshot} [snapshot]
|
||||
* @return {{ [Key in Extract<keyof KV,string>]?: KV[Key]}} A JSON Object that describes the attributes.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
getAttributes () {
|
||||
return /** @type {any} */ (typeMapGetAll(this))
|
||||
getAttributes (snapshot) {
|
||||
return /** @type {any} */ (snapshot ? typeMapGetAllSnapshot(this, snapshot) : typeMapGetAll(this))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import {
|
||||
YEvent,
|
||||
YXmlText, YXmlElement, YXmlFragment, Transaction // eslint-disable-line
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import {
|
||||
YMap,
|
||||
YXmlHookRefID,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import {
|
||||
YText,
|
||||
YXmlTextRefID,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import { Observable } from 'lib0/observable'
|
||||
|
||||
import {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import {
|
||||
findIndexSS,
|
||||
getState,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import { AbstractType } from '../internals.js' // eslint-disable-line
|
||||
|
||||
import * as decoding from 'lib0/decoding'
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import {
|
||||
YArray,
|
||||
YMap,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import {
|
||||
writeID,
|
||||
readID,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import {
|
||||
isDeleted,
|
||||
createDeleteSetFromStructStore,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import {
|
||||
GC,
|
||||
splitItem,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import {
|
||||
getState,
|
||||
writeStructsFromTransaction,
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import * as error from 'lib0/error'
|
||||
import * as encoding from 'lib0/encoding'
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import {
|
||||
isDeleted,
|
||||
Item, AbstractType, Transaction, AbstractStruct // eslint-disable-line
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* @module encoding
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import { AbstractType, Item } from '../internals.js' // eslint-disable-line
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import {
|
||||
AbstractType // eslint-disable-line
|
||||
} from '../internals.js'
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import * as binary from 'lib0/binary'
|
||||
import * as decoding from 'lib0/decoding'
|
||||
import * as encoding from 'lib0/encoding'
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* Testing if encoding/decoding compatibility and integration compatiblity is given.
|
||||
* We expect that the document always looks the same, even if we upgrade the integration algorithm, or add additional encoding approaches.
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import * as Y from '../src/index.js'
|
||||
import * as t from 'lib0/testing'
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import * as Y from '../src/index.js'
|
||||
import * as t from 'lib0/testing'
|
||||
|
||||
|
||||
@@ -14,6 +14,21 @@ export const testBasic = _tc => {
|
||||
t.assert(restored.getText().toString() === 'world!')
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} _tc
|
||||
*/
|
||||
export const testBasicXmlAttributes = _tc => {
|
||||
const ydoc = new Y.Doc({ gc: false })
|
||||
const yxml = ydoc.getMap().set('el', new Y.XmlElement('div'))
|
||||
const snapshot1 = Y.snapshot(ydoc)
|
||||
yxml.setAttribute('a', '1')
|
||||
const snapshot2 = Y.snapshot(ydoc)
|
||||
yxml.setAttribute('a', '2')
|
||||
t.compare(yxml.getAttributes(), { a: '2' })
|
||||
t.compare(yxml.getAttributes(snapshot2), { a: '1' })
|
||||
t.compare(yxml.getAttributes(snapshot1), {})
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} _tc
|
||||
*/
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
import * as t from 'lib0/testing'
|
||||
import * as prng from 'lib0/prng'
|
||||
import * as encoding from 'lib0/encoding'
|
||||
|
||||
@@ -3,6 +3,46 @@ import { init } from './testHelper.js' // eslint-disable-line
|
||||
import * as Y from '../src/index.js'
|
||||
import * as t from 'lib0/testing'
|
||||
|
||||
export const testInconsistentFormat = () => {
|
||||
/**
|
||||
* @param {Y.Doc} ydoc
|
||||
*/
|
||||
const testYjsMerge = ydoc => {
|
||||
const content = /** @type {Y.XmlText} */ (ydoc.get('text', Y.XmlText))
|
||||
content.format(0, 6, { bold: null })
|
||||
content.format(6, 4, { type: 'text' })
|
||||
t.compare(content.toDelta(), [
|
||||
{
|
||||
attributes: { type: 'text' },
|
||||
insert: 'Merge Test'
|
||||
},
|
||||
{
|
||||
attributes: { type: 'text', italic: true },
|
||||
insert: ' After'
|
||||
}
|
||||
])
|
||||
}
|
||||
const initializeYDoc = () => {
|
||||
const yDoc = new Y.Doc({ gc: false })
|
||||
|
||||
const content = /** @type {Y.XmlText} */ (yDoc.get('text', Y.XmlText))
|
||||
content.insert(0, ' After', { type: 'text', italic: true })
|
||||
content.insert(0, 'Test', { type: 'text' })
|
||||
content.insert(0, 'Merge ', { type: 'text', bold: true })
|
||||
return yDoc
|
||||
}
|
||||
{
|
||||
const yDoc = initializeYDoc()
|
||||
testYjsMerge(yDoc)
|
||||
}
|
||||
{
|
||||
const initialYDoc = initializeYDoc()
|
||||
const yDoc = new Y.Doc({ gc: false })
|
||||
Y.applyUpdate(yDoc, Y.encodeStateAsUpdate(initialYDoc))
|
||||
testYjsMerge(yDoc)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} tc
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user