Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
31b4ab8d0c | ||
|
|
ab978b2003 | ||
|
|
afc6728c9e | ||
|
|
0ef5bd42fe | ||
|
|
3ece681758 | ||
|
|
cac9407185 | ||
|
|
7ea8ffebae | ||
|
|
d7751c16fd | ||
|
|
a64c51ec06 | ||
|
|
7405057037 |
23
README.md
23
README.md
@@ -34,6 +34,8 @@ on Yjs. [ A learning platform with collaborative text
|
||||||
|
editing powered by Yjs.
|
||||||
* [AFFiNE](https://affine.pro/) A local-first, privacy-first, open source
|
* [AFFiNE](https://affine.pro/) A local-first, privacy-first, open source
|
||||||
knowledge base. 🏅
|
knowledge base. 🏅
|
||||||
* [Dynaboard](https://dynaboard.com/) Build web apps collaboratively. :star2:
|
* [Dynaboard](https://dynaboard.com/) Build web apps collaboratively. :star2:
|
||||||
@@ -1059,16 +1061,17 @@ More information about the specific implementation is available in
|
|||||||
[INTERNALS.md](./INTERNALS.md) and in
|
[INTERNALS.md](./INTERNALS.md) and in
|
||||||
[this walkthrough of the Yjs codebase](https://youtu.be/0l5XgnQ6rB4).
|
[this walkthrough of the Yjs codebase](https://youtu.be/0l5XgnQ6rB4).
|
||||||
|
|
||||||
CRDTs that suitable for shared text editing suffer from the fact that they only grow
|
CRDTs that are suitable for shared text editing suffer from the fact that they
|
||||||
in size. There are CRDTs that do not grow in size, but they do not have the
|
only grow in size. There are CRDTs that do not grow in size, but they do not
|
||||||
characteristics that are benificial for shared text editing (like intention
|
have the characteristics that are benificial for shared text editing (like
|
||||||
preservation). Yjs implements many improvements to the original algorithm that
|
intention preservation). Yjs implements many improvements to the original
|
||||||
diminish the trade-off that the document only grows in size. We can't garbage
|
algorithm that diminish the trade-off that the document only grows in size. We
|
||||||
collect deleted structs (tombstones) while ensuring a unique order of the
|
can't garbage collect deleted structs (tombstones) while ensuring a unique
|
||||||
structs. But we can 1. merge preceeding structs into a single struct to reduce
|
order of the structs. But we can 1. merge preceeding structs into a single
|
||||||
the amount of meta information, 2. we can delete content from the struct if it
|
struct to reduce the amount of meta information, 2. we can delete content from
|
||||||
is deleted, and 3. we can garbage collect tombstones if we don't care about the
|
the struct if it is deleted, and 3. we can garbage collect tombstones if we
|
||||||
order of the structs anymore (e.g. if the parent was deleted).
|
don't care about the order of the structs anymore (e.g. if the parent was
|
||||||
|
deleted).
|
||||||
|
|
||||||
**Examples:**
|
**Examples:**
|
||||||
|
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "yjs",
|
"name": "yjs",
|
||||||
"version": "13.5.42",
|
"version": "13.5.44",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "yjs",
|
"name": "yjs",
|
||||||
"version": "13.5.42",
|
"version": "13.5.44",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lib0": "^0.2.49"
|
"lib0": "^0.2.49"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "yjs",
|
"name": "yjs",
|
||||||
"version": "13.5.42",
|
"version": "13.5.44",
|
||||||
"description": "Shared Editing Library",
|
"description": "Shared Editing Library",
|
||||||
"main": "./dist/yjs.cjs",
|
"main": "./dist/yjs.cjs",
|
||||||
"module": "./dist/yjs.mjs",
|
"module": "./dist/yjs.mjs",
|
||||||
@@ -27,6 +27,7 @@
|
|||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"types": "./dist/src/index.d.ts",
|
"types": "./dist/src/index.d.ts",
|
||||||
|
"module": "./dist/yjs.mjs",
|
||||||
"import": "./dist/yjs.mjs",
|
"import": "./dist/yjs.mjs",
|
||||||
"require": "./dist/yjs.cjs"
|
"require": "./dist/yjs.cjs"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1064,7 +1064,7 @@ export class YText extends AbstractType {
|
|||||||
n = n.right
|
n = n.right
|
||||||
}
|
}
|
||||||
packStr()
|
packStr()
|
||||||
}, splitSnapshotAffectedStructs)
|
}, 'cleanup')
|
||||||
return ops
|
return ops
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -176,12 +176,11 @@ export class YXmlElement extends YXmlFragment {
|
|||||||
/**
|
/**
|
||||||
* Returns all attribute name/value pairs in a JSON Object.
|
* Returns all attribute name/value pairs in a JSON Object.
|
||||||
*
|
*
|
||||||
* @param {Snapshot} [snapshot]
|
|
||||||
* @return {Object<string, any>} A JSON Object that describes the attributes.
|
* @return {Object<string, any>} A JSON Object that describes the attributes.
|
||||||
*
|
*
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
getAttributes (snapshot) {
|
getAttributes () {
|
||||||
return typeMapGetAll(this)
|
return typeMapGetAll(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -251,7 +251,6 @@ const cleanupTransactions = (transactionCleanups, i) => {
|
|||||||
try {
|
try {
|
||||||
sortAndMergeDeleteSet(ds)
|
sortAndMergeDeleteSet(ds)
|
||||||
transaction.afterState = getStateVector(transaction.doc.store)
|
transaction.afterState = getStateVector(transaction.doc.store)
|
||||||
doc._transaction = null
|
|
||||||
doc.emit('beforeObserverCalls', [transaction, doc])
|
doc.emit('beforeObserverCalls', [transaction, doc])
|
||||||
/**
|
/**
|
||||||
* An array of event callbacks.
|
* An array of event callbacks.
|
||||||
@@ -398,16 +397,20 @@ export const transact = (doc, f, origin = null, local = true) => {
|
|||||||
try {
|
try {
|
||||||
f(doc._transaction)
|
f(doc._transaction)
|
||||||
} finally {
|
} finally {
|
||||||
if (initialCall && transactionCleanups[0] === doc._transaction) {
|
if (initialCall) {
|
||||||
// The first transaction ended, now process observer calls.
|
const finishCleanup = doc._transaction === transactionCleanups[0]
|
||||||
// Observer call may create new transactions for which we need to call the observers and do cleanup.
|
doc._transaction = null
|
||||||
// We don't want to nest these calls, so we execute these calls one after
|
if (finishCleanup) {
|
||||||
// another.
|
// The first transaction ended, now process observer calls.
|
||||||
// Also we need to ensure that all cleanups are called, even if the
|
// Observer call may create new transactions for which we need to call the observers and do cleanup.
|
||||||
// observes throw errors.
|
// We don't want to nest these calls, so we execute these calls one after
|
||||||
// This file is full of hacky try {} finally {} blocks to ensure that an
|
// another.
|
||||||
// event can throw errors and also that the cleanup is called.
|
// Also we need to ensure that all cleanups are called, even if the
|
||||||
cleanupTransactions(transactionCleanups, 0)
|
// observes throw errors.
|
||||||
|
// This file is full of hacky try {} finally {} blocks to ensure that an
|
||||||
|
// event can throw errors and also that the cleanup is called.
|
||||||
|
cleanupTransactions(transactionCleanups, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,31 @@
|
|||||||
import * as Y from '../src/index.js'
|
import * as Y from '../src/index.js'
|
||||||
import * as t from 'lib0/testing'
|
import * as t from 'lib0/testing'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {t.TestCase} _tc
|
||||||
|
*/
|
||||||
|
export const testOriginInTransaction = _tc => {
|
||||||
|
const doc = new Y.Doc()
|
||||||
|
const ytext = doc.getText()
|
||||||
|
/**
|
||||||
|
* @type {Array<string>}
|
||||||
|
*/
|
||||||
|
const origins = []
|
||||||
|
doc.on('afterTransaction', (tr) => {
|
||||||
|
origins.push(tr.origin)
|
||||||
|
if (origins.length <= 1) {
|
||||||
|
ytext.toDelta()
|
||||||
|
doc.transact(() => {
|
||||||
|
ytext.insert(0, 'a')
|
||||||
|
}, 'nested')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
doc.transact(() => {
|
||||||
|
ytext.insert(0, '0')
|
||||||
|
}, 'first')
|
||||||
|
t.compareArrays(origins, ['first', 'cleanup', 'nested'])
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Client id should be changed when an instance receives updates from another client using the same client id.
|
* Client id should be changed when an instance receives updates from another client using the same client id.
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user