From 9f5bc9ddfe9741f863124a96e9afa903f483a2b3 Mon Sep 17 00:00:00 2001 From: Kevin Jahns Date: Sun, 3 May 2020 16:10:58 +0200 Subject: [PATCH] change client id when duplicate content is detected --- src/index.js | 1 + src/utils/Doc.js | 4 +++- src/utils/Transaction.js | 6 ++++++ tests/consistency.tests.js | 19 +++++++++++++++++++ tests/index.js | 3 ++- 5 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 tests/consistency.tests.js diff --git a/src/index.js b/src/index.js index 2973dfb8..8fd0e0d4 100644 --- a/src/index.js +++ b/src/index.js @@ -47,6 +47,7 @@ export { typeMapGetSnapshot, iterateDeletedStructs, applyUpdate, + readUpdate, encodeStateAsUpdate, encodeStateVector, UndoManager, diff --git a/src/utils/Doc.js b/src/utils/Doc.js index 58c76467..4eab58f7 100644 --- a/src/utils/Doc.js +++ b/src/utils/Doc.js @@ -17,6 +17,8 @@ import { Observable } from 'lib0/observable.js' import * as random from 'lib0/random.js' import * as map from 'lib0/map.js' +export const generateNewClientId = random.uint32 + /** * A Yjs instance handles the state of shared data. * @extends Observable @@ -31,7 +33,7 @@ export class Doc extends Observable { super() this.gc = gc this.gcFilter = gcFilter - this.clientID = random.uint32() + this.clientID = generateNewClientId() /** * @type {Map>} */ diff --git a/src/utils/Transaction.js b/src/utils/Transaction.js index 68459398..2fd0d598 100644 --- a/src/utils/Transaction.js +++ b/src/utils/Transaction.js @@ -10,6 +10,7 @@ import { findIndexSS, callEventHandlerListeners, Item, + generateNewClientId, StructStore, ID, AbstractType, AbstractStruct, YEvent, Doc // eslint-disable-line } from '../internals.js' @@ -17,6 +18,7 @@ import * as encoding from 'lib0/encoding.js' import * as map from 'lib0/map.js' import * as math from 'lib0/math.js' import * as set from 'lib0/set.js' +import * as logging from 'lib0/logging.js' import { callAll } from 'lib0/function.js' /** @@ -313,6 +315,10 @@ const cleanupTransactions = (transactionCleanups, i) => { tryToMergeWithLeft(structs, replacedStructPos) } } + if (!transaction.local && transaction.afterState.get(doc.clientID) !== transaction.beforeState.get(doc.clientID)) { + doc.clientID = generateNewClientId() + logging.print(logging.ORANGE, logging.BOLD, '[yjs] ', logging.UNBOLD, logging.RED, 'Changed the client-id because another client seems to be using it.') + } // @todo Merge all the transactions into one and provide send the data as a single update message doc.emit('afterTransactionCleanup', [transaction, doc]) if (doc._observers.has('update')) { diff --git a/tests/consistency.tests.js b/tests/consistency.tests.js new file mode 100644 index 00000000..4743ec74 --- /dev/null +++ b/tests/consistency.tests.js @@ -0,0 +1,19 @@ + +import * as Y from '../src/index.js' +import * as t from 'lib0/testing.js' + +/** + * Client id should be changed when an instance receives updates from another client using the same client id. + * + * @param {t.TestCase} tc + */ +export const testClientIdDuplicateChange = tc => { + const doc1 = new Y.Doc() + doc1.clientID = 0 + const doc2 = new Y.Doc() + doc2.clientID = 0 + t.assert(doc2.clientID === doc1.clientID) + doc1.getArray('a').insert(0, [1, 2]) + Y.applyUpdate(doc2, Y.encodeStateAsUpdate(doc1)) + t.assert(doc2.clientID !== doc1.clientID) +} diff --git a/tests/index.js b/tests/index.js index 7d60fa26..279c4d53 100644 --- a/tests/index.js +++ b/tests/index.js @@ -5,6 +5,7 @@ import * as text from './y-text.tests.js' import * as xml from './y-xml.tests.js' import * as encoding from './encoding.tests.js' import * as undoredo from './undo-redo.tests.js' +import * as consistency from './consistency.tests.js' import { runTests } from 'lib0/testing.js' import { isBrowser, isNode } from 'lib0/environment.js' @@ -14,7 +15,7 @@ if (isBrowser) { log.createVConsole(document.body) } runTests({ - map, array, text, xml, encoding, undoredo + map, array, text, xml, consistency, encoding, undoredo }).then(success => { /* istanbul ignore next */ if (isNode) {