Compare commits

...

35 Commits

Author SHA1 Message Date
Kevin Jahns
6208b82872 13.5.42 2022-10-18 16:52:38 +02:00
Kevin Jahns
12a9134b09 lint 2022-10-18 16:51:07 +02:00
Kevin Jahns
7395229086 Port test from @PatrickShaw #432. Allow infinite captureTimeout in UndoManager #431. Closes #432 2022-10-18 16:45:30 +02:00
Kevin Jahns
8fb73edd97 Merge pull request #453 from Cargo/main
Allow updating captureTimeout on UndoManager instances
2022-10-03 11:58:03 +02:00
Kevin Jahns
f1ad5686c1 Add Hyperquery to -Who Is Using Yjs- 2022-10-02 17:10:14 +02:00
Kevin Jahns
ed9236bdc7 Merge pull request #464 from MaxNoetzold/main
Add y-mongodb-provider to provider list in README
2022-09-26 22:08:43 +02:00
Kevin Jahns
5405fd2d7c Merge pull request #465 from neo/patch-1
Remove unused return in `forEach` of `YMap`
2022-09-20 13:07:23 +02:00
Wenchen Li
12667f6b66 Remove unused return in forEach of YMap
If the idea is to keep the API as close to the JS Map as possible, maybe we should consider not returning here.

Ref: https://github.com/microsoft/TypeScript/blob/v4.8.3/lib/lib.es2015.collection.d.ts#L28-L31
2022-09-19 18:19:41 -04:00
MaxNoetzold
3d7ef7e28b add y-mongodb-provider to provider list in README 2022-09-18 14:17:52 +02:00
Kevin Jahns
56267e0a7d Merge pull request #455 from scenaristeur/patch-1
Update INTERNALS.md
2022-09-16 23:26:04 +02:00
Kevin Jahns
da71f6fa45 Merge pull request #463 from doodlewind/patch-1
add AFFiNE to sponsor list
2022-09-14 17:43:04 +02:00
Kevin Jahns
588788fbef fix snapshot diff calculation naming bug 2022-09-14 00:37:06 +02:00
Yifeng Wang
fb9df6efe2 add AFFiNE to sponsor list 2022-09-13 21:31:10 +08:00
Kevin Jahns
a69ecb0287 Merge pull request #460 from regischen/fix-typo
fix typo
2022-08-25 12:03:42 +02:00
regischen
923fc6e06e fix typo 2022-08-25 17:35:24 +08:00
David
0fdfd93e4b Update INTERNALS.md
Typo
2022-08-14 10:10:14 +02:00
Aart Rost
e0e5f8d2ea Allow updating captureTimeout on UndoManager instances
Used to pause the undoManager by toggling the timeout with `yUndoManager.captureTimeout = Number.MAX_VALUE`
2022-08-10 14:07:40 -07:00
Kevin Jahns
daf034cf75 Merge pull request #452 from aryzing/patch-1
Update README.md
2022-08-08 10:57:13 +02:00
Eduard Bardají Puig
2157ebb4d0 Update README.md
Add missing `.getText()` method to docs.
2022-08-07 20:52:16 +02:00
Kevin Jahns
97ef4ae1e0 13.5.41 2022-07-28 14:14:27 +02:00
Kevin Jahns
df2d59e2fb UndoManager: fix special deletion case. closes #447 closes #443 2022-07-28 14:12:21 +02:00
Kevin Jahns
7a61c90261 13.5.40 2022-07-22 14:24:44 +02:00
Kevin Jahns
6fa8778fc7 add doc parameter to UndoManager 2022-07-22 14:22:46 +02:00
Kevin Jahns
1bc9308566 improve already-imported message further 2022-06-16 12:57:04 +02:00
Kevin Jahns
a5e0448a92 Merge pull request #434 from PatrickShaw/globalThis-add
Prefer globalThis over window and global
2022-06-15 12:04:09 +02:00
Kevin Jahns
c0c2b3347b 13.5.39 2022-06-15 10:49:48 +02:00
Kevin Jahns
6258ba1ce9 Merge pull request #435 from yogas/export_UpdateEncoderV1
export UpdateEncoderV1
2022-06-15 10:47:04 +02:00
Vladimir Shapovalov
5a7ee74f68 export UpdateEncoderV1 2022-06-14 13:16:35 +07:00
Kevin Jahns
29fb4a0aab improve double-import error message 2022-06-13 10:27:04 +02:00
Patrick Shaw
8937494bdd Added globalThis 2022-06-01 13:36:32 +10:00
Kevin Jahns
4504196d5c add gitter & discord link 2022-05-19 22:15:10 +02:00
Kevin Jahns
0c8d29bfff Merge pull request #424 from KentoMoriwaki/patch-1
Add support for esm in node.js
2022-05-17 14:38:25 +02:00
Kento Moriwaki
43384e4148 Add support for esm in node.js 2022-05-17 21:18:04 +09:00
Kevin Jahns
a2b62b0a58 13.5.38 2022-05-14 18:11:57 +02:00
Kevin Jahns
6febf51b1a fix captureTransaction 2022-05-14 18:10:19 +02:00
10 changed files with 99 additions and 33 deletions

View File

@@ -66,7 +66,7 @@ fields, respectively. These are used when peers concurrently insert at the same
location in a document. Though quite rare in practice, Yjs needs to make sure
the list items always resolve to the same order on all peers. The actual logic
is relatively simple - its only a couple dozen lines of code and it lives in
the `Item#integrate()` method. The YATA paper has much more detail on the this
the `Item#integrate()` method. The YATA paper has much more detail on this
algorithm.
### Item Storage

View File

@@ -15,6 +15,7 @@ suited for even large documents.
* Demos: [https://github.com/yjs/yjs-demos](https://github.com/yjs/yjs-demos)
* Discuss: [https://discuss.yjs.dev](https://discuss.yjs.dev)
* Chat: [Gitter](https://gitter.im/Yjs/community) | [Discord](https://discord.gg/T3nqMT6qbM)
* Benchmark Yjs vs. Automerge:
[https://github.com/dmonad/crdt-benchmarks](https://github.com/dmonad/crdt-benchmarks)
* 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)
@@ -33,6 +34,8 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2
## 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:
* [Relm](https://www.relm.us/) A collaborative gameworld for teamwork and
community. :star:
@@ -51,6 +54,8 @@ on Yjs. [![Become a Sponsor](https://img.shields.io/static/v1?label=Become%20a%2
* [Slidebeamer](https://slidebeamer.com/) Presentation app.
* [BlockSurvey](https://blocksurvey.io) End-to-end encryption for your forms/surveys.
* [Skiff](https://skiff.org/) Private, decentralized workspace.
* [Hyperquery](https://hyperquery.ai/) A collaborative data workspace for
sharing analyses, documentation, spreadsheets, and dashboards.
## Table of Contents
@@ -135,6 +140,11 @@ 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>
<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>
</dl>
@@ -628,6 +638,8 @@ type. Doesn't log types that have not been defined (using
<dd>Define a shared Y.Array type. Is equivalent to <code>y.get(string, Y.Array)</code>.</dd>
<b><code>getMap(string):Y.Map</code></b>
<dd>Define a shared Y.Map type. Is equivalent to <code>y.get(string, Y.Map)</code>.</dd>
<b><code>getText(string):Y.Text</code></b>
<dd>Define a shared Y.Text type. Is equivalent to <code>y.get(string, Y.Text)</code>.</dd>
<b><code>getXmlFragment(string):Y.XmlFragment</code></b>
<dd>Define a shared Y.XmlFragment type. Is equivalent to <code>y.get(string, Y.XmlFragment)</code>.</dd>
<b><code>on(string, function)</code></b>

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "yjs",
"version": "13.5.37",
"version": "13.5.42",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "yjs",
"version": "13.5.37",
"version": "13.5.42",
"license": "MIT",
"dependencies": {
"lib0": "^0.2.49"

View File

@@ -1,6 +1,6 @@
{
"name": "yjs",
"version": "13.5.37",
"version": "13.5.42",
"description": "Shared Editing Library",
"main": "./dist/yjs.cjs",
"module": "./dist/yjs.mjs",
@@ -26,6 +26,7 @@
},
"exports": {
".": {
"types": "./dist/src/index.d.ts",
"import": "./dist/yjs.mjs",
"require": "./dist/yjs.cjs"
},

View File

@@ -89,21 +89,24 @@ export {
diffUpdate,
diffUpdateV2,
convertUpdateFormatV1ToV2,
convertUpdateFormatV2ToV1
convertUpdateFormatV2ToV1,
UpdateEncoderV1
} from './internals.js'
const glo = /** @type {any} */ (typeof window !== 'undefined'
? window
// @ts-ignore
: typeof global !== 'undefined' ? global : {})
const glo = /** @type {any} */ (typeof globalThis !== 'undefined'
? globalThis
: typeof window !== 'undefined'
? window
// @ts-ignore
: typeof global !== 'undefined' ? global : {})
const importIdentifier = '__ $YJS$ __'
if (glo[importIdentifier] === true) {
/**
* Dear reader of this warning message. Please take this seriously.
* Dear reader of this message. Please take this seriously.
*
* If you see this message, please make sure that you only import one version of Yjs. In many cases,
* If you see this message, make sure that you only import one version of Yjs. In many cases,
* your package manager installs two versions of Yjs that are used by different packages within your project.
* Another reason for this message is that some parts of your project use the commonjs version of Yjs
* and others use the EcmaScript version of Yjs.
@@ -111,7 +114,9 @@ if (glo[importIdentifier] === true) {
* This often leads to issues that are hard to debug. We often need to perform constructor checks,
* e.g. `struct instanceof GC`. If you imported different versions of Yjs, it is impossible for us to
* do the constructor checks anymore - which might break the CRDT algorithm.
*
* https://github.com/yjs/yjs/issues/438
*/
console.warn('Yjs was already imported. Importing different versions of Yjs often leads to issues.')
console.error('Yjs was already imported. This breaks constructor checks and will lead to issues! - https://github.com/yjs/yjs/issues/438')
}
glo[importIdentifier] = true

View File

@@ -211,11 +211,6 @@ export const redoItem = (transaction, item, redoitems, itemsToDelete, ignoreRemo
while (left !== null && left.redone !== null) {
left = getItemCleanStart(transaction, left.redone)
}
// check wether we were allowed to follow right (indicating that originally this op was replaced by another item)
if (left === null || /** @type {AbstractType<any>} */ (left.parent)._item !== parentItem) {
// invalid parent; should never happen
return null
}
if (left && left.right !== null) {
// It is not possible to redo this item because it conflicts with a
// change from another client

View File

@@ -167,16 +167,11 @@ export class YMap extends AbstractType {
* @param {function(MapType,string,YMap<MapType>):void} f A function to execute on every element of this YArray.
*/
forEach (f) {
/**
* @type {Object<string,MapType>}
*/
const map = {}
this._map.forEach((item, key) => {
if (!item.deleted) {
f(item.content.getContent()[item.length - 1], key, this)
}
})
return map
}
/**

View File

@@ -1018,12 +1018,12 @@ export class YText extends AbstractType {
case ContentString: {
const cur = currentAttributes.get('ychange')
if (snapshot !== undefined && !isVisible(n, snapshot)) {
if (cur === undefined || cur.user !== n.id.client || cur.state !== 'removed') {
if (cur === undefined || cur.user !== n.id.client || cur.type !== 'removed') {
packStr()
currentAttributes.set('ychange', computeYChange ? computeYChange('removed', n.id) : { type: 'removed' })
}
} else if (prevSnapshot !== undefined && !isVisible(n, prevSnapshot)) {
if (cur === undefined || cur.user !== n.id.client || cur.state !== 'added') {
if (cur === undefined || cur.user !== n.id.client || cur.type !== 'added') {
packStr()
currentAttributes.set('ychange', computeYChange ? computeYChange('added', n.id) : { type: 'added' })
}

View File

@@ -134,11 +134,12 @@ const popStackItem = (undoManager, stack, eventType) => {
* @property {number} [UndoManagerOptions.captureTimeout=500]
* @property {function(Transaction):boolean} [UndoManagerOptions.captureTransaction] Do not capture changes of a Transaction if result false.
* @property {function(Item):boolean} [UndoManagerOptions.deleteFilter=()=>true] Sometimes
* it is necessary to filter whan an Undo/Redo operation can delete. If this
* it is necessary to filter what an Undo/Redo operation can delete. If this
* filter returns false, the type/item won't be deleted even it is in the
* undo/redo scope.
* @property {Set<any>} [UndoManagerOptions.trackedOrigins=new Set([null])]
* @property {boolean} [ignoreRemoteMapChanges] Experimental. By default, the UndoManager will never overwrite remote changes. Enable this property to enable overwriting remote changes on key-value changes (Y.Map, properties on Y.Xml, etc..).
* @property {Doc} [doc] The document that this UndoManager operates on. Only needed if typeScope is empty.
*/
/**
@@ -160,7 +161,8 @@ export class UndoManager extends Observable {
captureTransaction = tr => true,
deleteFilter = () => true,
trackedOrigins = new Set([null]),
ignoreRemoteMapChanges = false
ignoreRemoteMapChanges = false,
doc = /** @type {Doc} */ (array.isArray(typeScope) ? typeScope[0].doc : typeScope.doc)
} = {}) {
super()
/**
@@ -187,20 +189,20 @@ export class UndoManager extends Observable {
*/
this.undoing = false
this.redoing = false
this.doc = /** @type {Doc} */ (this.scope[0].doc)
this.doc = doc
this.lastChange = 0
this.ignoreRemoteMapChanges = ignoreRemoteMapChanges
this.captureTimeout = captureTimeout
/**
* @param {Transaction} transaction
*/
this.afterTransactionHandler = transaction => {
// Only track certain transactions
if (this.captureTransaction(transaction) && (
if (
!this.captureTransaction(transaction) ||
!this.scope.some(type => transaction.changedParentTypes.has(type)) ||
(
!this.trackedOrigins.has(transaction.origin) && (!transaction.origin || !this.trackedOrigins.has(transaction.origin.constructor))
)
)) {
(!this.trackedOrigins.has(transaction.origin) && (!transaction.origin || !this.trackedOrigins.has(transaction.origin.constructor)))
) {
return
}
const undoing = this.undoing
@@ -222,7 +224,7 @@ export class UndoManager extends Observable {
})
const now = time.getUnixTime()
let didAdd = false
if (now - this.lastChange < captureTimeout && stack.length > 0 && !undoing && !redoing) {
if (this.lastChange > 0 && now - this.lastChange < this.captureTimeout && stack.length > 0 && !undoing && !redoing) {
// append change to last stack op
const lastOp = stack[stack.length - 1]
lastOp.deletions = mergeDeleteSets([lastOp.deletions, transaction.deleteSet])

View File

@@ -3,6 +3,19 @@ import { init, compare, applyRandomTests, Doc } from './testHelper.js' // eslint
import * as Y from '../src/index.js'
import * as t from 'lib0/testing'
/**
* @param {t.TestCase} tc
*/
export const testInfiniteCaptureTimeout = tc => {
const { array0 } = init(tc, { users: 3 })
const undoManager = new Y.UndoManager(array0, { captureTimeout: Number.MAX_VALUE })
array0.push([1, 2, 3])
undoManager.stopCapturing()
array0.push([4, 5, 6])
undoManager.undo()
t.compare(array0.toArray(), [1, 2, 3])
}
/**
* @param {t.TestCase} tc
*/
@@ -49,6 +62,20 @@ export const testUndoText = tc => {
t.compare(text0.toDelta(), [{ insert: 'b' }, { insert: 'cxy', attributes: { bold: true } }, { insert: 'z' }])
}
/**
* Test case to fix #241
* @param {t.TestCase} tc
*/
export const testEmptyTypeScope = tc => {
const ydoc = new Y.Doc()
const um = new Y.UndoManager([], { doc: ydoc })
const yarray = ydoc.getArray()
um.addToScope(yarray)
yarray.insert(0, [1])
um.undo()
t.assert(yarray.length === 0)
}
/**
* Test case to fix #241
* @param {t.TestCase} tc
@@ -588,3 +615,32 @@ export const testBehaviorOfIgnoreremotemapchangesProperty = tc => {
t.assert(map1.get('x') === 2)
t.assert(map2.get('x') === 2)
}
/**
* Special deletion case.
*
* @see https://github.com/yjs/yjs/issues/447
* @param {t.TestCase} tc
*/
export const testSpecialDeletionCase = tc => {
const origin = 'undoable'
const doc = new Y.Doc()
const fragment = doc.getXmlFragment()
const undoManager = new Y.UndoManager(fragment, { trackedOrigins: new Set([origin]) })
doc.transact(() => {
const e = new Y.XmlElement('test')
e.setAttribute('a', '1')
e.setAttribute('b', '2')
fragment.insert(0, [e])
})
t.compareStrings(fragment.toString(), '<test a="1" b="2"></test>')
doc.transact(() => {
// change attribute "b" and delete test-node
const e = fragment.get(0)
e.setAttribute('b', '3')
fragment.delete(0)
}, origin)
t.compareStrings(fragment.toString(), '')
undoManager.undo()
t.compareStrings(fragment.toString(), '<test a="1" b="2"></test>')
}