Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fab14a09de | ||
|
|
710b4ba145 | ||
|
|
34091ae614 | ||
|
|
feb8ec1afc | ||
|
|
ce9139c9f4 | ||
|
|
e2e5d0870c | ||
|
|
04cff60931 | ||
|
|
5dfe4e8af2 |
53
README.md
53
README.md
@@ -97,6 +97,7 @@ are implemented in separate modules.
|
||||
| [Quill](https://quilljs.com/) | ✔ | [y-quill](https://github.com/yjs/y-quill) | [demo](https://demos.yjs.dev/quill/quill.html) |
|
||||
| [CodeMirror](https://codemirror.net/) | ✔ | [y-codemirror](https://github.com/yjs/y-codemirror) | [demo](https://demos.yjs.dev/codemirror/codemirror.html) |
|
||||
| [Monaco](https://microsoft.github.io/monaco-editor/) | ✔ | [y-monaco](https://github.com/yjs/y-monaco) | [demo](https://demos.yjs.dev/monaco/monaco.html) |
|
||||
| [Slate](https://github.com/ianstormtaylor/slate) | ✔ | [slate-yjs](https://github.com/bitphinix/slate-yjs) | [demo](https://bitphinix.github.io/slate-yjs-example) |
|
||||
|
||||
### Providers
|
||||
|
||||
@@ -701,6 +702,30 @@ Y.applyUpdate(ydoc1, diff2)
|
||||
Y.applyUpdate(ydoc2, diff1)
|
||||
```
|
||||
|
||||
### Example: Syncing clients without loading the Y.Doc
|
||||
|
||||
It is possible to sync clients and compute delta updates without loading the Yjs
|
||||
document to memory. Yjs exposes an API to compute the differences directly on the
|
||||
binary document updates.
|
||||
|
||||
```js
|
||||
// encode the current state as a binary buffer
|
||||
let currentState1 = Y.encodeStateAsUpdate(ydoc1)
|
||||
let currentState2 = Y.encodeStateAsUpdate(ydoc2)
|
||||
// now we can continue syncing clients using state vectors without using the Y.Doc
|
||||
ydoc1.destroy()
|
||||
ydoc2.destroy()
|
||||
|
||||
const stateVector1 = Y.encodeStateVectorFromUpdate(currentState1)
|
||||
const stateVector2 = Y.encodeStateVectorFromUpdate(currentState2)
|
||||
const diff1 = Y.diffUpdate(currentState1, stateVector2)
|
||||
const diff2 = Y.diffUpdate(currentState2, stateVector1)
|
||||
|
||||
// sync clients
|
||||
currentState1 = Y.mergeUpdates([currentState1, diff2])
|
||||
currentState1 = Y.mergeUpdates([currentState1, diff1])
|
||||
```
|
||||
|
||||
<dl>
|
||||
<b><code>Y.applyUpdate(Y.Doc, update:Uint8Array, [transactionOrigin:any])</code></b>
|
||||
<dd>
|
||||
@@ -717,22 +742,26 @@ differences to the update message.
|
||||
</dd>
|
||||
<b><code>Y.encodeStateVector(Y.Doc):Uint8Array</code></b>
|
||||
<dd>Computes the state vector and encodes it into an Uint8Array.</dd>
|
||||
<b><code>Y.mergeUpdates(Array<Uint8Array>)</code></b>
|
||||
<dd>
|
||||
Merge several document updates into a single document update while removing
|
||||
duplicate information. The merged document update is always smaller than
|
||||
the separate updates because of the compressed encoding.
|
||||
</dd>
|
||||
<b><code>Y.encodeStateVectorFromUpdate(Uint8Array): Uint8Array</code></b>
|
||||
<dd>
|
||||
Computes the state vector from a document update and encodes it into an Uint8Array.
|
||||
</dd>
|
||||
<b><code>Y.diffUpdate(update: Uint8Array, stateVector: Uint8Array): Uint8Array</code></b>
|
||||
<dd>
|
||||
Encode the missing differences to another update message. This function works
|
||||
similarly to <code>Y.encodeStateAsUpdate(ydoc, stateVector)</code> but works
|
||||
on updates instead.
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
### Relative Positions
|
||||
|
||||
> This API is not stable yet
|
||||
|
||||
This feature is intended for managing selections / cursors. When working with
|
||||
other users that manipulate the shared document, you can't trust that an index
|
||||
position (an integer) will stay at the intended location. A *relative position*
|
||||
is fixated to an element in the shared document and is not affected by remote
|
||||
changes. I.e. given the document `"a|c"`, the relative position is attached to
|
||||
`c`. When a remote user modifies the document by inserting a character before
|
||||
the cursor, the cursor will stay attached to the character `c`. `insert(1,
|
||||
'x')("a|c") = "ax|c"`. When the *relative position* is set to the end of the
|
||||
document, it will stay attached to the end of the document.
|
||||
|
||||
#### Example: Transform to RelativePosition and back
|
||||
|
||||
```js
|
||||
|
||||
54
package-lock.json
generated
54
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "yjs",
|
||||
"version": "13.5.0",
|
||||
"version": "13.5.1",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -78,9 +78,9 @@
|
||||
}
|
||||
},
|
||||
"@rollup/plugin-node-resolve": {
|
||||
"version": "11.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.1.1.tgz",
|
||||
"integrity": "sha512-zlBXR4eRS+2m79TsUZWhsd0slrHUYdRx4JF+aVQm+MI0wsKdlpC2vlDVjmlGvtZY1vsefOT9w3JxvmWSBei+Lg==",
|
||||
"version": "11.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.2.0.tgz",
|
||||
"integrity": "sha512-qHjNIKYt5pCcn+5RUBQxK8krhRvf1HnyVgUCcFFcweDS7fhkOLZeYh0mhHK6Ery8/bb9tvN/ubPzmfF0qjDCTA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@rollup/pluginutils": "^3.1.0",
|
||||
@@ -92,12 +92,12 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"resolve": {
|
||||
"version": "1.19.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz",
|
||||
"integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==",
|
||||
"version": "1.20.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz",
|
||||
"integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-core-module": "^2.1.0",
|
||||
"is-core-module": "^2.2.0",
|
||||
"path-parse": "^1.0.6"
|
||||
}
|
||||
}
|
||||
@@ -135,9 +135,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "14.14.25",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.25.tgz",
|
||||
"integrity": "sha512-EPpXLOVqDvisVxtlbvzfyqSsFeQxltFbluZNRndIb8tr9KiBnYNLzrc1N3pyKUCww2RNrfHDViqDWWE1LCJQtQ==",
|
||||
"version": "14.14.31",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.31.tgz",
|
||||
"integrity": "sha512-vFHy/ezP5qI0rFgJ7aQnjDXwAMrG0KqqIH7tQG5PPv3BWBayOPIQNBjVc/P6hhdZfMx51REc6tfDNXHUio893g==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/resolve": {
|
||||
@@ -1479,9 +1479,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"isomorphic.js": {
|
||||
"version": "0.1.5",
|
||||
"resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.1.5.tgz",
|
||||
"integrity": "sha512-MkX5lLQApx/8IAIU31PKvpAZosnu2Jqcj1rM8TzxyA4CR96tv3SgMKQNTCxL58G7696Q57zd7ubHV/hTg+5fNA=="
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.0.tgz",
|
||||
"integrity": "sha512-U7JlVUbmVYvV1ddGLhc7pvoMNp+1uGwE3sCIGHEh9I4ldSJJ9LtLog5/H8GzZkwLjmbeEvIbQb50nsre57nDuw=="
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "4.0.0",
|
||||
@@ -1598,11 +1598,11 @@
|
||||
}
|
||||
},
|
||||
"lib0": {
|
||||
"version": "0.2.35",
|
||||
"resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.35.tgz",
|
||||
"integrity": "sha512-drVD3EscB3TIxiFzceuZg7oF5Z6I8a0KX+7FowNcAXOEsTej/hlHB+ElJ8Pa/Ge73Gy3fklSJtPxpNd2PajdWg==",
|
||||
"version": "0.2.38",
|
||||
"resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.38.tgz",
|
||||
"integrity": "sha512-ZxnX62R5weebi8bH/Ipc6JBiQIsiQ1D7p3r96zulSSu1byW6DDWSBeI8WC/W5UGtkZ80GktX3JNY2pqhNiXWGA==",
|
||||
"requires": {
|
||||
"isomorphic.js": "^0.1.3"
|
||||
"isomorphic.js": "^0.2.0"
|
||||
}
|
||||
},
|
||||
"linkify-it": {
|
||||
@@ -2430,9 +2430,9 @@
|
||||
}
|
||||
},
|
||||
"rollup": {
|
||||
"version": "2.38.5",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.38.5.tgz",
|
||||
"integrity": "sha512-VoWt8DysFGDVRGWuHTqZzT02J0ASgjVq/hPs9QcBOGMd7B+jfTr/iqMVEyOi901rE3xq+Deq66GzIT1yt7sGwQ==",
|
||||
"version": "2.39.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.39.0.tgz",
|
||||
"integrity": "sha512-+WR3bttcq7zE+BntH09UxaW3bQo3vItuYeLsyk4dL2tuwbeSKJuvwiawyhEnvRdRgrII0Uzk00FpctHO/zB1kw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fsevents": "~2.3.1"
|
||||
@@ -2828,9 +2828,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
"version": "3.9.7",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz",
|
||||
"integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==",
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.5.tgz",
|
||||
"integrity": "sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA==",
|
||||
"dev": true
|
||||
},
|
||||
"uc.micro": {
|
||||
@@ -2940,9 +2940,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"y-protocols": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.3.tgz",
|
||||
"integrity": "sha512-2hSl0dqrD8Kph0SpvyakVYpKEnTLOLGIf7yvwmloQ4qS6RSvl6fUYHy6YocCvTvcd9MBuNeO4EqlmBcONJsvtw==",
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.4.tgz",
|
||||
"integrity": "sha512-5/Hd6DJ5Y2SlbqLIKq86BictdOS0iAcWJZCVop8MKqx0XWwA+BbMn4538n4Z0CGjFMUGnG1kGzagk3BKGz5SvQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lib0": "^0.2.35"
|
||||
|
||||
20
package.json
20
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "yjs",
|
||||
"version": "13.5.0",
|
||||
"version": "13.5.1",
|
||||
"description": "Shared Editing Library",
|
||||
"main": "./dist/yjs.cjs",
|
||||
"module": "./dist/yjs.mjs",
|
||||
@@ -25,6 +25,14 @@
|
||||
"trace-opt": "clear && rollup -c && node --trace-opt dist/test.cjs",
|
||||
"postinstall": "node ./sponsor-y.js"
|
||||
},
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/yjs.mjs",
|
||||
"require": "./dist/yjs.cjs"
|
||||
},
|
||||
"./src/index.js": "./src/index.js",
|
||||
"./tests/testHelper.js": "./tests/testHelper.js"
|
||||
},
|
||||
"files": [
|
||||
"dist/yjs.*",
|
||||
"dist/src",
|
||||
@@ -62,19 +70,19 @@
|
||||
},
|
||||
"homepage": "https://docs.yjs.dev",
|
||||
"dependencies": {
|
||||
"lib0": "^0.2.35"
|
||||
"lib0": "^0.2.38"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^17.0.0",
|
||||
"@rollup/plugin-node-resolve": "^11.0.1",
|
||||
"@rollup/plugin-node-resolve": "^11.2.0",
|
||||
"concurrently": "^3.6.1",
|
||||
"http-server": "^0.12.3",
|
||||
"jsdoc": "^3.6.6",
|
||||
"markdownlint-cli": "^0.23.2",
|
||||
"rollup": "^2.36.1",
|
||||
"rollup": "^2.39.0",
|
||||
"standard": "^14.3.4",
|
||||
"tui-jsdoc-template": "^1.2.2",
|
||||
"typescript": "^3.9.7",
|
||||
"y-protocols": "^1.0.2"
|
||||
"typescript": "^4.1.5",
|
||||
"y-protocols": "^1.0.4"
|
||||
}
|
||||
}
|
||||
|
||||
18
sponsor-y.js
18
sponsor-y.js
@@ -1,10 +1,12 @@
|
||||
|
||||
const log = require('lib0/dist/logging.cjs')
|
||||
try {
|
||||
const log = require('lib0/dist/logging.cjs')
|
||||
|
||||
log.print()
|
||||
log.print(log.BOLD, log.GREEN, log.BOLD, 'Thank you for using Yjs ', log.RED, '❤\n')
|
||||
log.print(
|
||||
log.GREY,
|
||||
'The project has grown considerably in the past year. Too much for me to maintain\nin my spare time. Several companies built their products with Yjs.\nYet, this project receives very little funding. Yjs is far from done. I want to\ncreate more awesome extensions and work on the growing number of open issues.\n', log.BOLD, 'Dear user, the future of this project entirely depends on you.\n')
|
||||
log.print(log.BLUE, log.BOLD, 'Please start funding the project now: https://github.com/sponsors/dmonad \n')
|
||||
log.print(log.GREY, '(This message will be removed when I achieved my funding goal)\n\n')
|
||||
log.print()
|
||||
log.print(log.BOLD, log.GREEN, log.BOLD, 'Thank you for using Yjs ', log.RED, '❤\n')
|
||||
log.print(
|
||||
log.GREY,
|
||||
'The project has grown considerably in the past year. Too much for me to maintain\nin my spare time. Several companies built their products with Yjs.\nYet, this project receives very little funding. Yjs is far from done. I want to\ncreate more awesome extensions and work on the growing number of open issues.\n', log.BOLD, 'Dear user, the future of this project entirely depends on you.\n')
|
||||
log.print(log.BLUE, log.BOLD, 'Please start funding the project now: https://github.com/sponsors/dmonad \n')
|
||||
log.print(log.GREY, '(This message will be removed when I achieved my funding goal)\n\n')
|
||||
} catch (e) { }
|
||||
|
||||
@@ -368,6 +368,42 @@ export const testLargeFragmentedDocument = tc => {
|
||||
})()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} tc
|
||||
*/
|
||||
export const testIncrementalUpdatesPerformanceOnLargeFragmentedDocument = tc => {
|
||||
const itemsToInsert = largeDocumentSize
|
||||
const updates = /** @type {Array<Uint8Array>} */ ([])
|
||||
;(() => {
|
||||
const doc1 = new Y.Doc()
|
||||
doc1.on('update', update => {
|
||||
updates.push(update)
|
||||
})
|
||||
const text0 = doc1.getText('txt')
|
||||
tryGc()
|
||||
t.measureTime(`time to insert ${itemsToInsert} items`, () => {
|
||||
doc1.transact(() => {
|
||||
for (let i = 0; i < itemsToInsert; i++) {
|
||||
text0.insert(0, '0')
|
||||
}
|
||||
})
|
||||
})
|
||||
tryGc()
|
||||
})()
|
||||
;(() => {
|
||||
t.measureTime(`time to merge ${itemsToInsert} updates (differential updates)`, () => {
|
||||
Y.mergeUpdates(updates)
|
||||
})
|
||||
tryGc()
|
||||
t.measureTime(`time to merge ${itemsToInsert} updates (ydoc updates)`, () => {
|
||||
const ydoc = new Y.Doc()
|
||||
updates.forEach(update => {
|
||||
Y.applyUpdate(ydoc, update)
|
||||
})
|
||||
})
|
||||
})()
|
||||
}
|
||||
|
||||
/**
|
||||
* Splitting surrogates can lead to invalid encoded documents.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user