Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d4f2e5435 | ||
|
|
2c0daeb071 | ||
|
|
013b2b6886 | ||
|
|
c2e7076400 | ||
|
|
289ff16f66 | ||
|
|
9f8c55885f | ||
|
|
5861876e6f | ||
|
|
37236fa31f | ||
|
|
b32f5434f1 | ||
|
|
61e84c5f99 | ||
|
|
b531438369 | ||
|
|
ac49dbcbd8 | ||
|
|
da8ca5168e | ||
|
|
a3d69bba72 | ||
|
|
e5f286cf89 | ||
|
|
c14a8d70f1 | ||
|
|
f52569b8fa | ||
|
|
25bef2308f | ||
|
|
e7572d61c6 | ||
|
|
e6afc51b84 | ||
|
|
171d801e0a | ||
|
|
9a7b659919 | ||
|
|
e0a9c0d9bb | ||
|
|
3a758f89a1 | ||
|
|
b5051e91ac | ||
|
|
7bdf94167a | ||
|
|
1ce1751432 |
8
.github/workflows/node.js.yml
vendored
8
.github/workflows/node.js.yml
vendored
@@ -16,16 +16,16 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [16.x, 18.x]
|
||||
node-version: [16.x, 20.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: npm ci
|
||||
- run: npm run lint
|
||||
- run: npm run test-extensive
|
||||
- run: npm run test
|
||||
env:
|
||||
CI: true
|
||||
|
||||
@@ -149,8 +149,8 @@ concepts that can be used to create a custom network protocol:
|
||||
|
||||
* `update`: The Yjs document can be encoded to an *update* object that can be
|
||||
parsed to reconstruct the document. Also every change on the document fires
|
||||
an incremental document updates that allows clients to sync with each other.
|
||||
The update object is an Uint8Array that efficiently encodes `Item` objects and
|
||||
an incremental document update that allows clients to sync with each other.
|
||||
The update object is a Uint8Array that efficiently encodes `Item` objects and
|
||||
the delete set.
|
||||
* `state vector`: A state vector defines the known state of each user (a set of
|
||||
tuples `(client, clock)`). This object is also efficiently encoded as a
|
||||
|
||||
4
LICENSE
4
LICENSE
@@ -1,7 +1,7 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014
|
||||
- Kevin Jahns <kevin.jahns@rwth-aachen.de>.
|
||||
Copyright (c) 2023
|
||||
- Kevin Jahns <kevin.jahns@protonmail.com>.
|
||||
- Chair of Computer Science 5 (Databases & Information Systems), RWTH Aachen University, Germany
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
|
||||
16
README.md
16
README.md
@@ -65,6 +65,7 @@ on Yjs. [ Collaborative campaign planner and
|
||||
worldbuilding app for tabletop RPGs.
|
||||
* [IllumiDesk](https://illumidesk.com/) Build courses and content with A.I.
|
||||
* [btw](https://www.btw.so) Open-source Medium alternative
|
||||
|
||||
## Table of Contents
|
||||
|
||||
@@ -183,7 +184,7 @@ language bindings to other languages
|
||||
* [yrs](https://github.com/y-crdt/y-crdt/tree/main/yrs) - Rust interface
|
||||
* [ypy](https://github.com/y-crdt/ypy) - Python binding
|
||||
* [yrb](https://github.com/y-crdt/yrb) - Ruby binding
|
||||
* [yrb](https://github.com/y-crdt/yswift) - Swift binding
|
||||
* [yswift](https://github.com/y-crdt/yswift) - Swift binding
|
||||
* [yffi](https://github.com/y-crdt/y-crdt/tree/main/yffi) - C-FFI
|
||||
* [ywasm](https://github.com/y-crdt/y-crdt/tree/main/ywasm) - WASM binding
|
||||
* [ycs](https://github.com/yjs/ycs) - .Net compatible C# implementation.
|
||||
@@ -697,7 +698,8 @@ type. Doesn't log types that have not been defined (using
|
||||
<b><code>on('update', function(updateMessage:Uint8Array, origin:any, Y.Doc):void)</code></b>
|
||||
<dd>
|
||||
Listen to document updates. Document updates must be transmitted to all other
|
||||
peers. You can apply document updates in any order and multiple times.
|
||||
peers. You can apply document updates in any order and multiple times. Use `updateV2`
|
||||
to receive V2 events.
|
||||
</dd>
|
||||
<b><code>on('beforeTransaction', function(Y.Transaction, Y.Doc):void)</code></b>
|
||||
<dd>Emitted before each transaction.</dd>
|
||||
@@ -789,7 +791,7 @@ const diff2 = Y.diffUpdate(currentState2, stateVector1)
|
||||
|
||||
// sync clients
|
||||
currentState1 = Y.mergeUpdates([currentState1, diff2])
|
||||
currentState1 = Y.mergeUpdates([currentState1, diff1])
|
||||
currentState2 = Y.mergeUpdates([currentState2, diff1])
|
||||
```
|
||||
|
||||
#### Obfuscating Updates
|
||||
@@ -822,8 +824,10 @@ Yjs implements two update formats. By default you are using the V1 update format
|
||||
You can opt-in into the V2 update format wich provides much better compression.
|
||||
It is not yet used by all providers. However, you can already use it if
|
||||
you are building your own provider. All below functions are available with the
|
||||
suffix "V2". E.g. `Y.applyUpdate` ⇒ `Y.applyUpdateV2`. We also support conversion
|
||||
functions between both formats: `Y.convertUpdateFormatV1ToV2` & `Y.convertUpdateFormatV2ToV1`.
|
||||
suffix "V2". E.g. `Y.applyUpdate` ⇒ `Y.applyUpdateV2`. Also when listening to updates
|
||||
you need to specifically need listen for V2 events e.g. `yDoc.on('updateV2', …)`.
|
||||
We also support conversion functions between both formats:
|
||||
`Y.convertUpdateFormatV1ToV2` & `Y.convertUpdateFormatV2ToV1`.
|
||||
|
||||
#### Update API
|
||||
|
||||
@@ -1068,7 +1072,7 @@ doc.transact(() => {
|
||||
ytext.insert(0, 'abc')
|
||||
}, 41)
|
||||
undoManager.undo()
|
||||
ytext.toString() // => '' (not tracked because 41 is not an instance of
|
||||
ytext.toString() // => 'abc' (not tracked because 41 is not an instance of
|
||||
// `trackedTransactionorigins`)
|
||||
ytext.delete(0, 3) // revert change
|
||||
|
||||
|
||||
14
package-lock.json
generated
14
package-lock.json
generated
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"name": "yjs",
|
||||
"version": "13.6.8",
|
||||
"version": "13.6.10",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "yjs",
|
||||
"version": "13.6.8",
|
||||
"version": "13.6.10",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lib0": "^0.2.74"
|
||||
"lib0": "^0.2.86"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^24.0.1",
|
||||
@@ -2481,9 +2481,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/lib0": {
|
||||
"version": "0.2.74",
|
||||
"resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.74.tgz",
|
||||
"integrity": "sha512-roj9i46/JwG5ik5KNTkxP2IytlnrssAkD/OhlAVtE+GqectrdkfR+pttszVLrOzMDeXNs1MPt6yo66MUolWSiA==",
|
||||
"version": "0.2.86",
|
||||
"resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.86.tgz",
|
||||
"integrity": "sha512-kxigQTM4Q7NwJkEgdqQvU21qiR37twcqqLmh+/SbiGbRLfPlLVbHyY9sWp7PwXh0Xus9ELDSjsUOwcrdt5yZ4w==",
|
||||
"dependencies": {
|
||||
"isomorphic.js": "^0.2.4"
|
||||
},
|
||||
@@ -2492,7 +2492,7 @@
|
||||
"0serve": "bin/0serve.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
"node": ">=16"
|
||||
},
|
||||
"funding": {
|
||||
"type": "GitHub Sponsors ❤",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "yjs",
|
||||
"version": "13.6.8",
|
||||
"version": "13.6.10",
|
||||
"description": "Shared Editing Library",
|
||||
"main": "./dist/yjs.cjs",
|
||||
"module": "./dist/yjs.mjs",
|
||||
@@ -75,7 +75,7 @@
|
||||
},
|
||||
"homepage": "https://docs.yjs.dev",
|
||||
"dependencies": {
|
||||
"lib0": "^0.2.74"
|
||||
"lib0": "^0.2.86"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-commonjs": "^24.0.1",
|
||||
|
||||
@@ -18,8 +18,10 @@ export {
|
||||
Item,
|
||||
AbstractStruct,
|
||||
GC,
|
||||
Skip,
|
||||
ContentBinary,
|
||||
ContentDeleted,
|
||||
ContentDoc,
|
||||
ContentEmbed,
|
||||
ContentFormat,
|
||||
ContentJSON,
|
||||
@@ -50,6 +52,7 @@ export {
|
||||
getItem,
|
||||
typeListToArraySnapshot,
|
||||
typeMapGetSnapshot,
|
||||
typeMapGetAllSnapshot,
|
||||
createDocFromSnapshot,
|
||||
iterateDeletedStructs,
|
||||
applyUpdate,
|
||||
@@ -93,6 +96,9 @@ export {
|
||||
obfuscateUpdate,
|
||||
obfuscateUpdateV2,
|
||||
UpdateEncoderV1,
|
||||
UpdateEncoderV2,
|
||||
UpdateDecoderV1,
|
||||
UpdateDecoderV2,
|
||||
equalDeleteSets,
|
||||
snapshotContainsUpdate
|
||||
} from './internals.js'
|
||||
|
||||
@@ -925,6 +925,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>>}
|
||||
|
||||
@@ -41,7 +41,7 @@ export class YMapEvent extends YEvent {
|
||||
* A shared Map implementation.
|
||||
*
|
||||
* @extends AbstractType<YMapEvent<MapType>>
|
||||
* @implements {Iterable<MapType>}
|
||||
* @implements {Iterable<[string, MapType]>}
|
||||
*/
|
||||
export class YMap extends AbstractType {
|
||||
/**
|
||||
@@ -152,7 +152,7 @@ export class YMap extends AbstractType {
|
||||
/**
|
||||
* Returns the values for each element in the YMap Type.
|
||||
*
|
||||
* @return {IterableIterator<any>}
|
||||
* @return {IterableIterator<MapType>}
|
||||
*/
|
||||
values () {
|
||||
return iterator.iteratorMap(createMapIterator(this._map), /** @param {any} v */ v => v[1].content.getContent()[v[1].length - 1])
|
||||
@@ -161,10 +161,10 @@ export class YMap extends AbstractType {
|
||||
/**
|
||||
* Returns an Iterator of [key, value] pairs
|
||||
*
|
||||
* @return {IterableIterator<any>}
|
||||
* @return {IterableIterator<[string, MapType]>}
|
||||
*/
|
||||
entries () {
|
||||
return iterator.iteratorMap(createMapIterator(this._map), /** @param {any} v */ v => [v[0], v[1].content.getContent()[v[1].length - 1]])
|
||||
return iterator.iteratorMap(createMapIterator(this._map), /** @param {any} v */ v => /** @type {any} */ ([v[0], v[1].content.getContent()[v[1].length - 1]]))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -183,7 +183,7 @@ export class YMap extends AbstractType {
|
||||
/**
|
||||
* Returns an Iterator of [key, value] pairs
|
||||
*
|
||||
* @return {IterableIterator<any>}
|
||||
* @return {IterableIterator<[string, MapType]>}
|
||||
*/
|
||||
[Symbol.iterator] () {
|
||||
return this.entries()
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -113,7 +113,7 @@ export class Doc extends Observable {
|
||||
this.whenSynced = provideSyncedPromise()
|
||||
}
|
||||
this.isSynced = isSynced === undefined || isSynced === true
|
||||
if (!this.isLoaded) {
|
||||
if (this.isSynced && !this.isLoaded) {
|
||||
this.emit('load', [])
|
||||
}
|
||||
})
|
||||
|
||||
@@ -251,7 +251,7 @@ const integrateStructs = (transaction, store, clientsStructRefs) => {
|
||||
return nextStructsTarget
|
||||
}
|
||||
let curStructsTarget = getNextStructTarget()
|
||||
if (curStructsTarget === null && stack.length === 0) {
|
||||
if (curStructsTarget === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -8,6 +8,31 @@ import * as Y from '../src/index.js'
|
||||
import * as t from 'lib0/testing'
|
||||
import * as prng from 'lib0/prng'
|
||||
|
||||
/**
|
||||
* @param {t.TestCase} _tc
|
||||
*/
|
||||
export const testIterators = _tc => {
|
||||
const ydoc = new Y.Doc()
|
||||
/**
|
||||
* @type {Y.Map<number>}
|
||||
*/
|
||||
const ymap = ydoc.getMap()
|
||||
// we are only checking if the type assumptions are correct
|
||||
/**
|
||||
* @type {Array<number>}
|
||||
*/
|
||||
const vals = Array.from(ymap.values())
|
||||
/**
|
||||
* @type {Array<[string,number]>}
|
||||
*/
|
||||
const entries = Array.from(ymap.entries())
|
||||
/**
|
||||
* @type {Array<string>}
|
||||
*/
|
||||
const keys = Array.from(ymap.keys())
|
||||
console.log(vals, entries, keys)
|
||||
}
|
||||
|
||||
/**
|
||||
* Computing event changes after transaction should result in an error. See yjs#539
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user