From d1f5ff0f59e5845e2110bd9872f0cc3ef358b6de Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Tue, 17 Sep 2019 18:53:59 +0200 Subject: [PATCH] implement PermanentUserData storage prototype --- package-lock.json | 6 +- package.json | 2 +- src/index.js | 3 +- src/internals.js | 7 +- src/structs/Item.js | 10 +-- src/types/AbstractType.js | 6 +- src/types/YText.js | 31 ++++---- src/utils/DeleteSet.js | 37 +++++---- src/utils/PermanentUserData.js | 134 +++++++++++++++++++++++++++++++++ src/utils/Snapshot.js | 4 +- src/utils/StructStore.js | 7 +- src/utils/Transaction.js | 14 +++- src/utils/UndoManager.js | 10 +-- src/utils/encoding.js | 5 +- tests/y-array.tests.js | 5 +- tests/y-text.tests.js | 2 +- 16 files changed, 215 insertions(+), 68 deletions(-) create mode 100644 src/utils/PermanentUserData.js diff --git a/package-lock.json b/package-lock.json index 896b5cb7..3180ad9b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2596,9 +2596,9 @@ } }, "lib0": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.1.0.tgz", - "integrity": "sha512-pkpnv2IJEOb6iwpcJ6BVQu9GkZ9VINKeQ/0BcArHpozqaGQYWe+ychf2p9wHKToHUnivPoGZZ7rFqrxNXjqFBg==" + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.1.1.tgz", + "integrity": "sha512-ghjoI4xL/xzVR1fRLYEOnJjYMguoI2dnDUf5HYOpTfD6R5GPKLml6xNKl4ZfBVmczkIOQPNthhukp6nlgbmDLw==" }, "linkify-it": { "version": "2.2.0", diff --git a/package.json b/package.json index 18c1f157..502e605e 100644 --- a/package.json +++ b/package.json @@ -51,7 +51,7 @@ }, "homepage": "https://yjs.dev", "dependencies": { - "lib0": "^0.1.0" + "lib0": "^0.1.1" }, "devDependencies": { "concurrently": "^3.6.1", diff --git a/src/index.js b/src/index.js index 9f63300c..7ddcf46e 100644 --- a/src/index.js +++ b/src/index.js @@ -53,5 +53,6 @@ export { decodeSnapshot, encodeSnapshot, isDeleted, - equalSnapshots + equalSnapshots, + PermanentUserData // @TODO experimental } from './internals.js' diff --git a/src/internals.js b/src/internals.js index d5ac7848..288cb404 100644 --- a/src/internals.js +++ b/src/internals.js @@ -1,13 +1,16 @@ + export * from './utils/DeleteSet.js' +export * from './utils/Doc.js' +export * from './utils/encoding.js' export * from './utils/EventHandler.js' export * from './utils/ID.js' export * from './utils/isParentOf.js' +export * from './utils/PermanentUserData.js' export * from './utils/RelativePosition.js' export * from './utils/Snapshot.js' export * from './utils/StructStore.js' export * from './utils/Transaction.js' export * from './utils/UndoManager.js' -export * from './utils/Doc.js' export * from './utils/YEvent.js' export * from './types/AbstractType.js' @@ -31,5 +34,3 @@ export * from './structs/ContentAny.js' export * from './structs/ContentString.js' export * from './structs/ContentType.js' export * from './structs/Item.js' - -export * from './utils/encoding.js' diff --git a/src/structs/Item.js b/src/structs/Item.js index 95530b6e..3abfa325 100644 --- a/src/structs/Item.js +++ b/src/structs/Item.js @@ -135,7 +135,7 @@ export const splitItem = (transaction, leftItem, diff) => { */ export const redoItem = (transaction, item, redoitems) => { if (item.redone !== null) { - return getItemCleanStart(transaction, transaction.doc.store, item.redone) + return getItemCleanStart(transaction, item.redone) } let parentItem = item.parent._item /** @@ -175,7 +175,7 @@ export const redoItem = (transaction, item, redoitems) => { } if (parentItem !== null && parentItem.redone !== null) { while (parentItem.redone !== null) { - parentItem = getItemCleanStart(transaction, transaction.doc.store, parentItem.redone) + parentItem = getItemCleanStart(transaction, parentItem.redone) } // find next cloned_redo items while (left !== null) { @@ -185,7 +185,7 @@ export const redoItem = (transaction, item, redoitems) => { let leftTrace = left // trace redone until parent matches while (leftTrace !== null && leftTrace.parent._item !== parentItem) { - leftTrace = leftTrace.redone === null ? null : getItemCleanStart(transaction, transaction.doc.store, leftTrace.redone) + leftTrace = leftTrace.redone === null ? null : getItemCleanStart(transaction, leftTrace.redone) } if (leftTrace !== null && leftTrace.parent._item === parentItem) { left = leftTrace @@ -200,7 +200,7 @@ export const redoItem = (transaction, item, redoitems) => { let rightTrace = right // trace redone until parent matches while (rightTrace !== null && rightTrace.parent._item !== parentItem) { - rightTrace = rightTrace.redone === null ? null : getItemCleanStart(transaction, transaction.doc.store, rightTrace.redone) + rightTrace = rightTrace.redone === null ? null : getItemCleanStart(transaction, rightTrace.redone) } if (rightTrace !== null && rightTrace.parent._item === parentItem) { right = rightTrace @@ -726,7 +726,7 @@ export class ItemRef extends AbstractStructRef { } const left = this.left === null ? null : getItemCleanEnd(transaction, store, this.left) - const right = this.right === null ? null : getItemCleanStart(transaction, store, this.right) + const right = this.right === null ? null : getItemCleanStart(transaction, this.right) let parent = null let parentSub = this.parentSub if (this.parent !== null) { diff --git a/src/types/AbstractType.js b/src/types/AbstractType.js index 82aa8fc8..731e12d4 100644 --- a/src/types/AbstractType.js +++ b/src/types/AbstractType.js @@ -428,7 +428,7 @@ export const typeListInsertGenerics = (transaction, parent, index, content) => { if (index <= n.length) { if (index < n.length) { // insert in-between - getItemCleanStart(transaction, transaction.doc.store, createID(n.id.client, n.id.clock + index)) + getItemCleanStart(transaction, createID(n.id.client, n.id.clock + index)) } break } @@ -454,7 +454,7 @@ export const typeListDelete = (transaction, parent, index, length) => { for (; n !== null && index > 0; n = n.right) { if (!n.deleted && n.countable) { if (index < n.length) { - getItemCleanStart(transaction, transaction.doc.store, createID(n.id.client, n.id.clock + index)) + getItemCleanStart(transaction, createID(n.id.client, n.id.clock + index)) } index -= n.length } @@ -463,7 +463,7 @@ export const typeListDelete = (transaction, parent, index, length) => { while (length > 0 && n !== null) { if (!n.deleted) { if (length < n.length) { - getItemCleanStart(transaction, transaction.doc.store, createID(n.id.client, n.id.clock + length)) + getItemCleanStart(transaction, createID(n.id.client, n.id.clock + length)) } n.delete(transaction) length -= n.length diff --git a/src/types/YText.js b/src/types/YText.js index 01f82ace..2520f3ee 100644 --- a/src/types/YText.js +++ b/src/types/YText.js @@ -17,7 +17,7 @@ import { ContentFormat, ContentString, splitSnapshotAffectedStructs, - Doc, Item, Snapshot, StructStore, Transaction // eslint-disable-line + ID, Doc, Item, Snapshot, Transaction // eslint-disable-line } from '../internals.js' import * as decoding from 'lib0/decoding.js' // eslint-disable-line @@ -68,7 +68,6 @@ export class ItemInsertionResult extends ItemListPosition { /** * @param {Transaction} transaction - * @param {StructStore} store * @param {Map} currentAttributes * @param {Item|null} left * @param {Item|null} right @@ -78,7 +77,7 @@ export class ItemInsertionResult extends ItemListPosition { * @private * @function */ -const findNextPosition = (transaction, store, currentAttributes, left, right, count) => { +const findNextPosition = (transaction, currentAttributes, left, right, count) => { while (right !== null && count > 0) { switch (right.content.constructor) { case ContentEmbed: @@ -86,7 +85,7 @@ const findNextPosition = (transaction, store, currentAttributes, left, right, co if (!right.deleted) { if (count < right.length) { // split right - getItemCleanStart(transaction, store, createID(right.id.client, right.id.clock + count)) + getItemCleanStart(transaction, createID(right.id.client, right.id.clock + count)) } count -= right.length } @@ -105,7 +104,6 @@ const findNextPosition = (transaction, store, currentAttributes, left, right, co /** * @param {Transaction} transaction - * @param {StructStore} store * @param {AbstractType} parent * @param {number} index * @return {ItemTextListPosition} @@ -113,11 +111,11 @@ const findNextPosition = (transaction, store, currentAttributes, left, right, co * @private * @function */ -const findPosition = (transaction, store, parent, index) => { +const findPosition = (transaction, parent, index) => { let currentAttributes = new Map() let left = null let right = parent._start - return findNextPosition(transaction, store, currentAttributes, left, right, index) + return findNextPosition(transaction, currentAttributes, left, right, index) } /** @@ -299,7 +297,7 @@ const formatText = (transaction, parent, left, right, currentAttributes, length, case ContentEmbed: case ContentString: if (length < right.length) { - getItemCleanStart(transaction, transaction.doc.store, createID(right.id.client, right.id.clock + length)) + getItemCleanStart(transaction, createID(right.id.client, right.id.clock + length)) } length -= right.length break @@ -343,7 +341,7 @@ const deleteText = (transaction, left, right, currentAttributes, length) => { case ContentEmbed: case ContentString: if (length < right.length) { - getItemCleanStart(transaction, transaction.doc.store, createID(right.id.client, right.id.clock + length)) + getItemCleanStart(transaction, createID(right.id.client, right.id.clock + length)) } length -= right.length right.delete(transaction) @@ -714,11 +712,12 @@ export class YText extends AbstractType { * * @param {Snapshot} [snapshot] * @param {Snapshot} [prevSnapshot] + * @param {function('removed' | 'added', ID):any} [computeYChange] * @return {any} The Delta representation of this type. * * @public */ - toDelta (snapshot, prevSnapshot) { + toDelta (snapshot, prevSnapshot, computeYChange) { /** * @type{Array} */ @@ -767,12 +766,12 @@ export class YText extends AbstractType { if (snapshot !== undefined && !isVisible(n, snapshot)) { if (cur === undefined || cur.user !== n.id.client || cur.state !== 'removed') { packStr() - currentAttributes.set('ychange', { user: n.id.client, state: 'removed' }) + 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') { packStr() - currentAttributes.set('ychange', { user: n.id.client, state: 'added' }) + currentAttributes.set('ychange', computeYChange ? computeYChange('added', n.id) : { type: 'added' }) } } else if (cur !== undefined) { packStr() @@ -818,7 +817,7 @@ export class YText extends AbstractType { const y = this.doc if (y !== null) { transact(y, transaction => { - const { left, right, currentAttributes } = findPosition(transaction, y.store, this, index) + const { left, right, currentAttributes } = findPosition(transaction, this, index) if (!attributes) { attributes = {} currentAttributes.forEach((v, k) => { attributes[k] = v }) @@ -847,7 +846,7 @@ export class YText extends AbstractType { const y = this.doc if (y !== null) { transact(y, transaction => { - const { left, right, currentAttributes } = findPosition(transaction, y.store, this, index) + const { left, right, currentAttributes } = findPosition(transaction, this, index) insertText(transaction, this, left, right, currentAttributes, embed, attributes) }) } else { @@ -870,7 +869,7 @@ export class YText extends AbstractType { const y = this.doc if (y !== null) { transact(y, transaction => { - const { left, right, currentAttributes } = findPosition(transaction, y.store, this, index) + const { left, right, currentAttributes } = findPosition(transaction, this, index) deleteText(transaction, left, right, currentAttributes, length) }) } else { @@ -892,7 +891,7 @@ export class YText extends AbstractType { const y = this.doc if (y !== null) { transact(y, transaction => { - let { left, right, currentAttributes } = findPosition(transaction, y.store, this, index) + let { left, right, currentAttributes } = findPosition(transaction, this, index) if (right === null) { return } diff --git a/src/utils/DeleteSet.js b/src/utils/DeleteSet.js index ea3b7b59..16c4a2ba 100644 --- a/src/utils/DeleteSet.js +++ b/src/utils/DeleteSet.js @@ -8,6 +8,7 @@ import { Item, GC, StructStore, Transaction, ID // eslint-disable-line } from '../internals.js' +import * as array from 'lib0/array.js' import * as math from 'lib0/math.js' import * as map from 'lib0/map.js' import * as encoding from 'lib0/encoding.js' @@ -52,14 +53,13 @@ export class DeleteSet { * * @param {Transaction} transaction * @param {DeleteSet} ds - * @param {StructStore} store * @param {function(GC|Item):void} f * * @function */ -export const iterateDeletedStructs = (transaction, ds, store, f) => +export const iterateDeletedStructs = (transaction, ds, f) => ds.clients.forEach((deletes, clientid) => { - const structs = /** @type {Array} */ (store.clients.get(clientid)) + const structs = /** @type {Array} */ (transaction.doc.store.clients.get(clientid)) for (let i = 0; i < deletes.length; i++) { const del = deletes[i] iterateStructs(transaction, structs, del.clock, del.len, f) @@ -137,22 +137,27 @@ export const sortAndMergeDeleteSet = ds => { } /** - * @param {DeleteSet} ds1 - * @param {DeleteSet} ds2 + * @param {Array} dss * @return {DeleteSet} A fresh DeleteSet */ -export const mergeDeleteSets = (ds1, ds2) => { +export const mergeDeleteSets = dss => { const merged = new DeleteSet() - // Write all keys from ds1 to merged. If ds2 has the same key, combine the sets. - ds1.clients.forEach((dels1, client) => - merged.clients.set(client, dels1.concat(ds2.clients.get(client) || [])) - ) - // Write all missing keys from ds2 to merged. - ds2.clients.forEach((dels2, client) => { - if (!merged.clients.has(client)) { - merged.clients.set(client, dels2) - } - }) + for (let dssI = 0; dssI < dss.length; dssI++) { + dss[dssI].clients.forEach((delsLeft, client) => { + if (!merged.clients.has(client)) { + // Write all missing keys from current ds and all following. + // If merged already contains `client` current ds has already been added. + /** + * @type {Array} + */ + const dels = delsLeft.slice() + for (let i = dssI + 1; i < dss.length; i++) { + array.appendTo(dels, dss[i].clients.get(client) || []) + } + merged.clients.set(client, dels) + } + }) + } sortAndMergeDeleteSet(merged) return merged } diff --git a/src/utils/PermanentUserData.js b/src/utils/PermanentUserData.js new file mode 100644 index 00000000..289a3755 --- /dev/null +++ b/src/utils/PermanentUserData.js @@ -0,0 +1,134 @@ + +import { + YArray, + YMap, + readDeleteSet, + writeDeleteSet, + createDeleteSet, + ID, DeleteSet, YArrayEvent, Transaction, Doc // eslint-disable-line +} from '../internals.js' + +import * as decoding from 'lib0/decoding.js' +import * as encoding from 'lib0/encoding.js' +import { mergeDeleteSets, isDeleted } from './DeleteSet.js' + +export class PermanentUserData { + /** + * @param {Doc} doc + * @param {string} key + */ + constructor (doc, key = 'users') { + const users = doc.getMap(key) + /** + * @type {Map} + */ + const dss = new Map() + this.yusers = users + this.doc = doc + /** + * Maps from clientid to userDescription + * + * @type {Map} + */ + this.clients = new Map() + this.dss = dss + /** + * @param {YMap} user + * @param {string} userDescription + */ + const initUser = (user, userDescription) => { + /** + * @type {YArray} + */ + const ds = user.get('ds') + const ids = user.get('ids') + const addClientId = /** @param {number} clientid */ clientid => this.clients.set(clientid, userDescription) + ds.observe(/** @param {YArrayEvent} event */ event => { + event.changes.added.forEach(item => { + item.content.getContent().forEach(encodedDs => { + if (encodedDs instanceof Uint8Array) { + this.dss.set(userDescription, mergeDeleteSets([this.dss.get(userDescription) || createDeleteSet(), readDeleteSet(decoding.createDecoder(encodedDs))])) + } + }) + }) + }) + this.dss.set(userDescription, mergeDeleteSets(ds.map(encodedDs => readDeleteSet(decoding.createDecoder(encodedDs))))) + ids.observe(/** @param {YArrayEvent} event */ event => + event.changes.added.forEach(item => item.content.getContent().forEach(addClientId)) + ) + ids.forEach(addClientId) + } + // observe users + users.observe(event => { + event.keysChanged.forEach(userDescription => + initUser(users.get(userDescription), userDescription) + ) + }) + // add intial data + users.forEach(initUser) + } + /** + * @param {Doc} doc + * @param {number} clientid + * @param {string} userDescription + */ + setUserMapping (doc, clientid, userDescription) { + const users = this.yusers + let user = users.get(userDescription) + if (!user) { + user = new YMap() + user.set('ids', new YArray()) + user.set('ds', new YArray()) + users.set(userDescription, user) + } + user.get('ids').push([clientid]) + users.observe(event => { + const userOverwrite = users.get(userDescription) + if (userOverwrite !== user) { + // user was overwritten, port all data over to the next user object + // @todo Experiment with Y.Sets here + user = userOverwrite + // @todo iterate over old type + this.clients.forEach((_userDescription, clientid) => { + if (userDescription === _userDescription) { + user.get('ids').push([clientid]) + } + }) + const encoder = encoding.createEncoder() + const ds = this.dss.get(userDescription) + if (ds) { + writeDeleteSet(encoder, ds) + user.get('ds').push([encoding.toUint8Array(encoder)]) + } + } + }) + doc.on('afterTransaction', /** @param {Transaction} transaction */ transaction => { + const yds = user.get('ds') + const ds = transaction.deleteSet + if (transaction.local && ds.clients.size > 0) { + const encoder = encoding.createEncoder() + writeDeleteSet(encoder, ds) + yds.push([encoding.toUint8Array(encoder)]) + } + }) + } + /** + * @param {number} clientid + * @return {any} + */ + getUserByClientId (clientid) { + return this.clients.get(clientid) || null + } + /** + * @param {ID} id + * @return {string | null} + */ + getUserByDeletedId (id) { + for (const [userDescription, ds] of this.dss) { + if (isDeleted(ds, id)) { + return userDescription + } + } + return null + } +} diff --git a/src/utils/Snapshot.js b/src/utils/Snapshot.js index f9d2714e..716ddeed 100644 --- a/src/utils/Snapshot.js +++ b/src/utils/Snapshot.js @@ -131,10 +131,10 @@ export const splitSnapshotAffectedStructs = (transaction, snapshot) => { if (!meta.has(snapshot)) { snapshot.sv.forEach((clock, client) => { if (clock < getState(store, client)) { - getItemCleanStart(transaction, store, createID(client, clock)) + getItemCleanStart(transaction, createID(client, clock)) } }) - iterateDeletedStructs(transaction, snapshot.ds, store, item => {}) + iterateDeletedStructs(transaction, snapshot.ds, item => {}) meta.add(snapshot) } } diff --git a/src/utils/StructStore.js b/src/utils/StructStore.js index 84dd4b93..969acfa4 100644 --- a/src/utils/StructStore.js +++ b/src/utils/StructStore.js @@ -197,16 +197,15 @@ export const findIndexCleanStart = (transaction, structs, clock) => { * Expects that id is actually in store. This function throws or is an infinite loop otherwise. * * @param {Transaction} transaction - * @param {StructStore} store * @param {ID} id * @return {Item} * * @private * @function */ -export const getItemCleanStart = (transaction, store, id) => { - const structs = /** @type {Array} */ (store.clients.get(id.client)) - return /** @type {Item} */ (structs[findIndexCleanStart(transaction, structs, id.clock)]) +export const getItemCleanStart = (transaction, id) => { + const structs = /** @type {Array} */ (transaction.doc.store.clients.get(id.client)) + return structs[findIndexCleanStart(transaction, structs, id.clock)] } /** diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index 503bccbe..abaa547d 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -46,8 +46,9 @@ export class Transaction { /** * @param {Doc} doc * @param {any} origin + * @param {boolean} local */ - constructor (doc, origin) { + constructor (doc, origin, local) { /** * The Yjs instance. * @type {Doc} @@ -95,6 +96,11 @@ export class Transaction { * @type {Map} */ this.meta = new Map() + /** + * Whether this change originates from this doc. + * @type {boolean} + */ + this.local = local } } @@ -143,17 +149,17 @@ export const addChangedTypeToTransaction = (transaction, type, parentSub) => { * * @param {Doc} doc * @param {function(Transaction):void} f - * @param {any} [origin] + * @param {any} [origin=true] * * @private * @function */ -export const transact = (doc, f, origin = null) => { +export const transact = (doc, f, origin = null, local = true) => { const transactionCleanups = doc._transactionCleanups let initialCall = false if (doc._transaction === null) { initialCall = true - doc._transaction = new Transaction(doc, origin) + doc._transaction = new Transaction(doc, origin, local) transactionCleanups.push(doc._transaction) doc.emit('beforeTransaction', [doc._transaction, doc]) } diff --git a/src/utils/UndoManager.js b/src/utils/UndoManager.js index 789321a2..2066fa9e 100644 --- a/src/utils/UndoManager.js +++ b/src/utils/UndoManager.js @@ -52,7 +52,7 @@ const popStackItem = (undoManager, stack, eventType) => { const stackItem = /** @type {StackItem} */ (stack.pop()) const itemsToRedo = new Set() let performedChange = false - iterateDeletedStructs(transaction, stackItem.ds, store, struct => { + iterateDeletedStructs(transaction, stackItem.ds, struct => { if (struct instanceof Item && scope.some(type => isParentOf(type, struct))) { itemsToRedo.add(struct) } @@ -70,10 +70,10 @@ const popStackItem = (undoManager, stack, eventType) => { if (struct.redone !== null) { let { item, diff } = followRedone(store, struct.id) if (diff > 0) { - item = getItemCleanStart(transaction, store, createID(item.id.client, item.id.clock + diff)) + item = getItemCleanStart(transaction, createID(item.id.client, item.id.clock + diff)) } if (item.length > stackItem.len) { - getItemCleanStart(transaction, store, createID(item.id.client, item.id.clock + stackItem.len)) + getItemCleanStart(transaction, createID(item.id.client, item.id.clock + stackItem.len)) } struct = item } @@ -168,7 +168,7 @@ export class UndoManager extends Observable { if (now - this.lastChange < captureTimeout && stack.length > 0 && !undoing && !redoing) { // append change to last stack op const lastOp = stack[stack.length - 1] - lastOp.ds = mergeDeleteSets(lastOp.ds, transaction.deleteSet) + lastOp.ds = mergeDeleteSets([lastOp.ds, transaction.deleteSet]) lastOp.len = afterState - lastOp.start } else { // create a new stack op @@ -178,7 +178,7 @@ export class UndoManager extends Observable { this.lastChange = now } // make sure that deleted structs are not gc'd - iterateDeletedStructs(transaction, transaction.deleteSet, transaction.doc.store, /** @param {Item|GC} item */ item => { + iterateDeletedStructs(transaction, transaction.deleteSet, /** @param {Item|GC} item */ item => { if (item instanceof Item && this.scope.some(type => isParentOf(type, item))) { keepItem(item) } diff --git a/src/utils/encoding.js b/src/utils/encoding.js index a0ebf23f..00aee8ac 100644 --- a/src/utils/encoding.js +++ b/src/utils/encoding.js @@ -26,6 +26,7 @@ import { readAndApplyDeleteSet, writeDeleteSet, createDeleteSetFromStructStore, + transact, Doc, Transaction, AbstractStruct, StructStore, ID // eslint-disable-line } from '../internals.js' @@ -299,10 +300,10 @@ export const readStructs = (decoder, transaction, store) => { * @function */ export const readUpdate = (decoder, ydoc, transactionOrigin) => - ydoc.transact(transaction => { + transact(ydoc, transaction => { readStructs(decoder, transaction, ydoc.store) readAndApplyDeleteSet(decoder, transaction, ydoc.store) - }, transactionOrigin) + }, transactionOrigin, false) /** * Apply a document update created by, for example, `y.on('update', update => ..)` or `update = encodeStateAsUpdate()`. diff --git a/tests/y-array.tests.js b/tests/y-array.tests.js index 36804780..27a7f147 100644 --- a/tests/y-array.tests.js +++ b/tests/y-array.tests.js @@ -3,6 +3,7 @@ import { init, compare, applyRandomTests, Doc } from './testHelper.js' // eslint import * as Y from '../src/index.js' import * as t from 'lib0/testing.js' import * as prng from 'lib0/prng.js' +import * as math from 'lib0/math.js' /** * @param {t.TestCase} tc @@ -362,12 +363,12 @@ const arrayTransactions = [ var length = yarray.length if (length > 0) { var somePos = prng.int31(gen, 0, length - 1) - var delLength = prng.int31(gen, 1, Math.min(2, length - somePos)) + var delLength = prng.int31(gen, 1, math.min(2, length - somePos)) if (prng.bool(gen)) { var type = yarray.get(somePos) if (type.length > 0) { somePos = prng.int31(gen, 0, type.length - 1) - delLength = prng.int31(gen, 0, Math.min(2, type.length - somePos)) + delLength = prng.int31(gen, 0, math.min(2, type.length - somePos)) type.delete(somePos, delLength) } } else { diff --git a/tests/y-text.tests.js b/tests/y-text.tests.js index f422ed45..f76ea659 100644 --- a/tests/y-text.tests.js +++ b/tests/y-text.tests.js @@ -127,7 +127,7 @@ export const testSnapshot = tc => { delete v.attributes.ychange.user } }) - t.compare(state2Diff, [{insert: 'a'}, {insert: 'x', attributes: {ychange: { state: 'added' }}}, {insert: 'b', attributes: {ychange: { state: 'removed' }}}, { insert: 'cd' }]) + t.compare(state2Diff, [{insert: 'a'}, {insert: 'x', attributes: {ychange: { type: 'added' }}}, {insert: 'b', attributes: {ychange: { type: 'removed' }}}, { insert: 'cd' }]) } /**