diff --git a/.flowconfig b/.flowconfig index 54306bd6..0dbc21fb 100644 --- a/.flowconfig +++ b/.flowconfig @@ -5,6 +5,8 @@ [include] ./src/ +./tests-lib/ +./test/ [libs] ./declarations/ diff --git a/package.json b/package.json index 23f711f2..40cd2b1f 100644 --- a/package.json +++ b/package.json @@ -64,7 +64,6 @@ "tag-dist-files": "^0.1.6" }, "dependencies": { - "debug": "^2.6.8", - "utf-8": "^1.0.0" + "debug": "^2.6.8" } } diff --git a/rollup.browser.js b/rollup.browser.js index f43af28e..78694858 100644 --- a/rollup.browser.js +++ b/rollup.browser.js @@ -5,7 +5,7 @@ import commonjs from 'rollup-plugin-commonjs' var pkg = require('./package.json') export default { - entry: 'src/y.js', + entry: 'src/Y.js', moduleName: 'Y', format: 'umd', plugins: [ diff --git a/rollup.node.js b/rollup.node.js index ede44376..ec5d4375 100644 --- a/rollup.node.js +++ b/rollup.node.js @@ -3,7 +3,7 @@ import commonjs from 'rollup-plugin-commonjs' var pkg = require('./package.json') export default { - entry: 'src/y.js', + entry: 'src/Y.js', moduleName: 'Y', format: 'umd', plugins: [ diff --git a/src/Binary/Decoder.js b/src/Binary/Decoder.js index 8bd5fe83..6cb11d1d 100644 --- a/src/Binary/Decoder.js +++ b/src/Binary/Decoder.js @@ -1,4 +1,4 @@ -import utf8 from 'utf-8' +import '../../node_modules/utf8/utf8.js' export default class BinaryDecoder { constructor (buffer) { @@ -11,25 +11,36 @@ export default class BinaryDecoder { } this.pos = 0 } - + /** + * Clone this decoder instance + * Optionally set a new position parameter + */ clone (newPos = this.pos) { let decoder = new BinaryDecoder(this.uint8arr) decoder.pos = newPos return decoder } - + /** + * Number of bytes + */ get length () { return this.uint8arr.length } - + /** + * Skip one byte, jump to the next position + */ skip8 () { this.pos++ } - + /** + * Read one byte as unsigned integer + */ readUint8 () { return this.uint8arr[this.pos++] } - + /** + * Read 4 bytes as unsigned integer + */ readUint32 () { let uint = this.uint8arr[this.pos] + @@ -39,11 +50,20 @@ export default class BinaryDecoder { this.pos += 4 return uint } - + /** + * Look ahead without incrementing position + * to the next byte and read it as unsigned integer + */ peekUint8 () { return this.uint8arr[this.pos] } - + /** + * Read unsigned integer (32bit) with variable length + * 1/8th of the storage is used as encoding overhead + * - numbers < 2^7 is stored in one byte + * - numbers < 2^14 is stored in two bytes + * .. + */ readVarUint () { let num = 0 let len = 0 @@ -59,7 +79,10 @@ export default class BinaryDecoder { } } } - + /** + * Read string of variable length + * - varUint is used to store the length of the string + */ readVarString () { let len = this.readVarUint() let bytes = new Array(len) @@ -68,20 +91,26 @@ export default class BinaryDecoder { } return utf8.getStringFromBytes(bytes) } - + /** + * Look ahead and read varString without incrementing position + */ peekVarString () { let pos = this.pos let s = this.readVarString() this.pos = pos return s } - - readOpID () { + /** + * Read ID + * - If first varUint read is 0xFFFFFF a RootID is returned + * - Otherwise an ID is returned + */ + readID () { let user = this.readVarUint() - if (user !== 0xFFFFFF) { - return [user, this.readVarUint()] - } else { - return [user, this.readVarString()] + if (user === 0xFFFFFF) { + // read property name and type id + return new RootID(this.readVarString(), this.readVarUint()) } + return new ID(user, this.readVarUint()) } } diff --git a/src/Binary/Encoder.js b/src/Binary/Encoder.js index dfa62a5f..00fe7cca 100644 --- a/src/Binary/Encoder.js +++ b/src/Binary/Encoder.js @@ -1,4 +1,4 @@ -import utf8 from 'utf-8' +import '../../node_modules/utf8/utf8.js' const bits7 = 0b1111111 const bits8 = 0b11111111 diff --git a/src/Connector.js b/src/Connector.js index 85ffa927..c22cea6f 100644 --- a/src/Connector.js +++ b/src/Connector.js @@ -1,10 +1,11 @@ -import { BinaryEncoder, BinaryDecoder } from './Encoding.js' +import BinaryEncoder from './Binary/Encoder.js' +import BinaryDecoder from './Binary/Decoder.js' -import { sendSyncStep1, readSyncStep1 } from './MessageHandler/syncStep1' -import { readSyncStep2 } from './MessageHandler/syncStep2' +import { sendSyncStep1, readSyncStep1 } from './MessageHandler/syncStep1.js' +import { readSyncStep2 } from './MessageHandler/syncStep2.js' import { readUpdate } from './MessageHandler/update.js' -import debug from 'debug' +import { debug } from './Y.js' export default class AbstractConnector { constructor (y, opts) { diff --git a/src/MessageHandler/integrateRemoteStructs.js b/src/MessageHandler/integrateRemoteStructs.js index 13749ee7..c5b33e22 100644 --- a/src/MessageHandler/integrateRemoteStructs.js +++ b/src/MessageHandler/integrateRemoteStructs.js @@ -1,5 +1,5 @@ -import { getStruct } from '../Util/StructReferences' -import BinaryDecoder from '../Util/Binary/Decoder' +import { getStruct } from '../Util/StructReferences.js' +import BinaryDecoder from '../Binary/Decoder.js' class MissingEntry { constructor (decoder, missing, struct) { @@ -39,7 +39,7 @@ function _integrateRemoteStructHelper (y, struct) { } } -export default function integrateRemoteStructs (decoder, encoder, y) { +export function integrateRemoteStructs (decoder, encoder, y) { while (decoder.length !== decoder.pos) { let decoderPos = decoder.pos let reference = decoder.readVarUint() diff --git a/src/MessageHandler/messageToString.js b/src/MessageHandler/messageToString.js index c6521f2a..405feeb9 100644 --- a/src/MessageHandler/messageToString.js +++ b/src/MessageHandler/messageToString.js @@ -1,7 +1,7 @@ -import BinaryDecoder from '../Utily/Binary/Decoder' -import { stringifyUpdate } from './update' -import { stringifySyncStep1 } from './syncStep1' -import { stringifySyncStep2 } from './syncStep2' +import BinaryDecoder from '../Binary/Decoder.js' +import { stringifyUpdate } from './update.js' +import { stringifySyncStep1 } from './syncStep1.js' +import { stringifySyncStep2 } from './syncStep2.js' export function messageToString (buffer) { let decoder = new BinaryDecoder(buffer) diff --git a/src/MessageHandler/syncStep1.js b/src/MessageHandler/syncStep1.js index b9e95193..a4f53680 100644 --- a/src/MessageHandler/syncStep1.js +++ b/src/MessageHandler/syncStep1.js @@ -1,4 +1,4 @@ -import BinaryEncoder from './Util/Binary/Encoder.js' +import BinaryEncoder from '../Binary/Encoder.js' export function stringifySyncStep1 (decoder, strBuilder) { let auth = decoder.readVarString() diff --git a/src/MessageHandler/syncStep2.js b/src/MessageHandler/syncStep2.js index 11a98262..3b0c6b70 100644 --- a/src/MessageHandler/syncStep2.js +++ b/src/MessageHandler/syncStep2.js @@ -1,6 +1,6 @@ -import integrateRemoteStructs from './integrateRemoteStructs' +import { integrateRemoteStructs } from './integrateRemoteStructs.js' import { stringifyUpdate } from './update.js' -import ID from '../Util/ID' +import ID from '../Util/ID.js' export function stringifySyncStep2 (decoder, strBuilder) { strBuilder.push(' - auth: ' + decoder.readVarString() + '\n') diff --git a/src/MessageHandler/update.js b/src/MessageHandler/update.js index 36121c58..0f66a7df 100644 --- a/src/MessageHandler/update.js +++ b/src/MessageHandler/update.js @@ -1,5 +1,5 @@ -import { getStruct } from '../Util/StructReferences' +import { getStruct } from '../Util/StructReferences.js' export function stringifyUpdate (decoder, strBuilder) { while (decoder.length !== decoder.pos) { @@ -16,4 +16,4 @@ export function stringifyUpdate (decoder, strBuilder) { } } -export { integrateRemoteStructs as readUpdate } from './integrateRemoteStructs' +export { integrateRemoteStructs as readUpdate } from './integrateRemoteStructs.js' diff --git a/src/Persistence.js b/src/Persistence.js index 7c961ed2..1cdb40ce 100644 --- a/src/Persistence.js +++ b/src/Persistence.js @@ -1,4 +1,4 @@ -import { BinaryEncoder } from './Encoding.js' +import BinaryEncoder from './Binary/Encoder.js' export default function extendPersistence (Y) { class AbstractPersistence { diff --git a/src/Store/DeleteStore.js b/src/Store/DeleteStore.js index 546dec21..7477ab54 100644 --- a/src/Store/DeleteStore.js +++ b/src/Store/DeleteStore.js @@ -1,5 +1,5 @@ -import Tree from '../Util/Tree' -import ID from '../Util/ID' +import Tree from '../Util/Tree.js' +import ID from '../Util/ID.js' class DSNode { constructor (id, len, gc) { diff --git a/src/Store/OperationStore.js b/src/Store/OperationStore.js index 49fe52ed..a3e03e8a 100644 --- a/src/Store/OperationStore.js +++ b/src/Store/OperationStore.js @@ -1,11 +1,8 @@ -import Tree from '../Util/Tree' -import RootID from '../Util/ID' -import { getStruct } from '../Util/structReferences' +import Tree from '../Util/Tree.js' +import RootID from '../Util/ID.js' +import { getStruct } from '../Util/structReferences.js' export default class OperationStore extends Tree { - constructor () { - - } get (id) { let struct = this.find(id) if (struct === null && id instanceof RootID) { @@ -27,7 +24,7 @@ export default class OperationStore extends Tree { } else { return null } - + } // Return an insertion such that id is the first element of content // This function manipulates an operation, if necessary diff --git a/src/Store/StateStore.js b/src/Store/StateStore.js index b12f8d5d..fe726f22 100644 --- a/src/Store/StateStore.js +++ b/src/Store/StateStore.js @@ -1,4 +1,4 @@ -import ID from '../Util/ID' +import ID from '../Util/ID.js' export default class StateStore { constructor (y) { diff --git a/src/Struct/Delete.js b/src/Struct/Delete.js index 922a3552..4b9bd850 100644 --- a/src/Struct/Delete.js +++ b/src/Struct/Delete.js @@ -1,4 +1,4 @@ -import StructManager from '../Util/StructManager' +import StructManager from '../Util/StructManager.js' export default class Delete { constructor () { diff --git a/src/Struct/Item.js b/src/Struct/Item.js index 56953b23..2b1ab0eb 100644 --- a/src/Struct/Item.js +++ b/src/Struct/Item.js @@ -1,4 +1,3 @@ -import StructManager from '../Util/StructManager' export default class Item { constructor () { diff --git a/src/Struct/ItemJSON.js b/src/Struct/ItemJSON.js index 3d0ade7e..91fe1f86 100644 --- a/src/Struct/ItemJSON.js +++ b/src/Struct/ItemJSON.js @@ -1,4 +1,4 @@ -import Item from './Item' +import Item from './Item.js' export default class ItemJSON extends Item { constructor () { diff --git a/src/Struct/ItemString.js b/src/Struct/ItemString.js index 8bf9b59d..cfe5c2a5 100644 --- a/src/Struct/ItemString.js +++ b/src/Struct/ItemString.js @@ -1,4 +1,4 @@ -import Item from './Item' +import Item from './Item.js' export default class ItemString extends Item { constructor () { diff --git a/src/Struct/Type.js b/src/Struct/Type.js index 5776c9b0..bd498bf1 100644 --- a/src/Struct/Type.js +++ b/src/Struct/Type.js @@ -1,4 +1,4 @@ -import Item from './Item' +import Item from './Item.js' export default class Type extends Item { constructor () { diff --git a/src/Type/YArray.js b/src/Type/YArray.js index 9894ca33..11f07d25 100644 --- a/src/Type/YArray.js +++ b/src/Type/YArray.js @@ -1,5 +1,5 @@ -import Type from '../Struct/Type' -import ItemJSON from '../Struct/ItemJSON' +import Type from '../Struct/Type.js' +import ItemJSON from '../Struct/ItemJSON.js' export default class YArray extends Type { forEach (f) { diff --git a/src/Type/YMap.js b/src/Type/YMap.js index 4e170d92..819e9d98 100644 --- a/src/Type/YMap.js +++ b/src/Type/YMap.js @@ -1,6 +1,6 @@ -import Type from '../Struct/Type' -import Item from '../Struct/Item' -import ItemJSON from '../Struct/ItemJSON' +import Type from '../Struct/Type.js' +import Item from '../Struct/Item.js' +import ItemJSON from '../Struct/ItemJSON.js' export default class YMap extends Type { set (key, value) { diff --git a/src/Type/YText.js b/src/Type/YText.js index e69de29b..ce3744ce 100644 --- a/src/Type/YText.js +++ b/src/Type/YText.js @@ -0,0 +1,4 @@ +import YArray from './YArray.js' + +export default class YText extends YArray { +} diff --git a/src/Type/YXml.js b/src/Type/YXml.js index e69de29b..3bf9ac0a 100644 --- a/src/Type/YXml.js +++ b/src/Type/YXml.js @@ -0,0 +1,4 @@ +import YArray from './YArray.js' + +export default class YXml extends YArray { +} diff --git a/src/Util/ID.js b/src/Util/ID.js index 17a68571..2de00f6d 100644 --- a/src/Util/ID.js +++ b/src/Util/ID.js @@ -1,7 +1,7 @@ -import StructManager from './StructManager' +import { getReference } from './structReferences.js' -export class ID { +export default class ID { constructor (user, clock) { this.user = user this.clock = clock @@ -16,17 +16,3 @@ export class ID { return this.user < id.user || (this.user === id.user && this.clock < id.clock) } } - -export class RootID { - constructor (name, typeConstructor) { - this.user = -1 - this.name = name - this.type = StructManager.getReference(typeConstructor) - } - equals (id) { - return id !== null && id.user === this.user && id.name === this.name && id.type === this.type - } - lessThan (id) { - return this.user < id.user || (this.user === id.user && (this.name < id.name || (this.name === id.name && this.type < id.type))) - } -} diff --git a/src/Util/RootID.js b/src/Util/RootID.js new file mode 100644 index 00000000..e3498e56 --- /dev/null +++ b/src/Util/RootID.js @@ -0,0 +1,14 @@ + +export default class RootID { + constructor (name, typeConstructor) { + this.user = -1 + this.name = name + this.type = StructManager.getReference(typeConstructor) + } + equals (id) { + return id !== null && id.user === this.user && id.name === this.name && id.type === this.type + } + lessThan (id) { + return this.user < id.user || (this.user === id.user && (this.name < id.name || (this.name === id.name && this.type < id.type))) + } +} diff --git a/src/Util/deleteItemRange.js b/src/Util/deleteItemRange.js index 804f7958..1969a0d5 100644 --- a/src/Util/deleteItemRange.js +++ b/src/Util/deleteItemRange.js @@ -1,7 +1,7 @@ import Delete from '../Struct/Delete' import ID from './ID' -export default function deleteItemRange (y, user, clock, length) { +export function deleteItemRange (y, user, clock, length) { let del = new Delete() del._target = new ID(user, clock) del._length = length diff --git a/src/Util/generateUserID.js b/src/Util/generateUserID.js index e0bc4993..d6f18701 100644 --- a/src/Util/generateUserID.js +++ b/src/Util/generateUserID.js @@ -1,6 +1,6 @@ /* global crypto */ -export default function generateUserID () { +export function generateUserID () { if (typeof crypto !== 'undefined' && crypto.getRandomValue != null) { // browser let arr = new Uint32Array(1) diff --git a/src/Util/structReferences.js b/src/Util/structReferences.js index 4396234e..df09d2f0 100644 --- a/src/Util/structReferences.js +++ b/src/Util/structReferences.js @@ -1,10 +1,10 @@ -import YArray from '../Type/YArray' -import YMap from '../Type/YMap' -import YText from '../Type/YText' -import YXml from '../Type/YXml' +import YArray from '../Type/YArray.js' +import YMap from '../Type/YMap.js' +import YText from '../Type/YText.js' +import YXml from '../Type/YXml.js' -import ItemJSON from '../Struct/ItemJSON' -import ItemString from '../Struct/ItemString' +import ItemJSON from '../Struct/ItemJSON.js' +import ItemString from '../Struct/ItemString.js' const structs = new Map() const references = new Map() diff --git a/src/Y.js b/src/Y.js index f5b6316e..1a859d88 100644 --- a/src/Y.js +++ b/src/Y.js @@ -1,20 +1,25 @@ -import debug from 'debug' +// import debug from 'debug' +export function debug (namespace) { + return function log (message) { + console.log(namespace, message) + } +} -import DeleteStore from './Store/DeleteStore' -import OperationStore from './Store/OperationStore' -import StateStore from './Store/StateStore' -import generateUserID from './Function/generateUserID' -import { RootID } from './Util/ID.js' +import DeleteStore from './Store/DeleteStore.js' +import OperationStore from './Store/OperationStore.js' +import StateStore from './Store/StateStore.js' +import { generateUserID } from './Util/generateUserID.js' +import RootID from './Util/RootID.js' -import { formatYjsMessage, formatYjsMessageType } from './MessageHandler' +import { messageToString, messageToRoomname } from './MessageHandler/messageToString.js' -import Connector from './Connector' -import Persistence from './Persistence' -import YArray from './Type/YArray' -import YMap from './Type/YMap' -import YText from './Type/YText' -import YXml from './Type/YXml' +import Connector from './Connector.js' +import Persistence from './Persistence.js' +import YArray from './Type/YArray.js' +import YMap from './Type/YMap.js' +import YText from './Type/YText.js' +import YXml from './Type/YXml.js' export default class Y { constructor (opts) { @@ -97,5 +102,5 @@ Y.Text = YText Y.Xml = YXml Y.debug = debug -debug.formatters.Y = formatYjsMessage -debug.formatters.y = formatYjsMessageType +debug.formatters.Y = messageToString +debug.formatters.y = messageToRoomname diff --git a/test/encode-decode.js b/test/encode-decode.js index bace3a09..8e9b2ac2 100644 --- a/test/encode-decode.js +++ b/test/encode-decode.js @@ -1,7 +1,9 @@ -import { test } from 'cutest' -import Chance from 'chance' +import { test } from '../node_modules/cutest/cutest.js' +import '../node_modules/chance/chance.js' import Y from '../src/y.js' -import { BinaryEncoder, BinaryDecoder } from '../src/Encoding.js' +import BinaryEncoder from '../src/Binary/Encoder.js' +import BinaryDecoder from '../src/Binary/Decoder.js' +import { generateUserID } from '../src/Util/generateUserID.js' function testEncoding (t, write, read, val) { let encoder = new BinaryEncoder() @@ -42,7 +44,7 @@ test('varUint random', async function varUintRandom (t) { test('varUint random user id', async function varUintRandomUserId (t) { t.getSeed() // enforces that this test is repeated - testEncoding(t, writeVarUint, readVarUint, Y.utils.generateUserId()) + testEncoding(t, writeVarUint, readVarUint, generateUserID()) }) const writeVarString = (encoder, val) => encoder.writeVarString(val) @@ -59,122 +61,3 @@ test('varString random', async function varStringRandom (t) { const chance = new Chance(t.getSeed() * 1000000000) testEncoding(t, writeVarString, readVarString, chance.string()) }) - -const writeDelete = Y.Struct.Delete.binaryEncode -const readDelete = Y.Struct.Delete.binaryDecode - -test('encode/decode Delete operation', async function binDelete (t) { - let op = { - target: [10, 3000], - length: 40000, - struct: 'Delete' - } - testEncoding(t, writeDelete, readDelete, op) -}) - -const writeInsert = Y.Struct.Insert.binaryEncode -const readInsert = Y.Struct.Insert.binaryDecode - -test('encode/decode Insert operations', async function binInsert (t) { - testEncoding(t, writeInsert, readInsert, { - id: [1, 2], - right: [5, 6], - left: [3, 4], - origin: [7, 8], - parent: [9, 10], - struct: 'Insert', - content: ['a'] - }) - - t.log('left === origin') - testEncoding(t, writeInsert, readInsert, { - id: [1, 2], - right: [5, 6], - left: [3, 4], - origin: [3, 4], - parent: [9, 10], - struct: 'Insert', - content: ['a'] - }) - - t.log('parentsub') - testEncoding(t, writeInsert, readInsert, { - id: [1, 2], - right: [5, 6], - left: [3, 4], - origin: [3, 4], - parent: [9, 10], - parentSub: 'sub', - struct: 'Insert', - content: ['a'] - }) - - t.log('opContent') - testEncoding(t, writeInsert, readInsert, { - id: [1, 2], - right: [5, 6], - left: [3, 4], - origin: [3, 4], - parent: [9, 10], - struct: 'Insert', - opContent: [1000, 10000] - }) - - t.log('mixed content') - testEncoding(t, writeInsert, readInsert, { - id: [1, 2], - right: [5, 6], - left: [3, 4], - origin: [3, 4], - parent: [9, 10], - struct: 'Insert', - content: ['a', 1] - }) - - t.log('origin is null') - testEncoding(t, writeInsert, readInsert, { - id: [1, 2], - right: [5, 6], - left: [3, 4], - origin: null, - parent: [9, 10], - struct: 'Insert', - content: ['a'] - }) - - t.log('left = origin = right = null') - testEncoding(t, writeInsert, readInsert, { - id: [1, 2], - right: null, - left: null, - origin: null, - parent: [9, 10], - struct: 'Insert', - content: ['a'] - }) -}) - -const writeList = Y.Struct.List.binaryEncode -const readList = Y.Struct.List.binaryDecode - -test('encode/decode List operations', async function binList (t) { - testEncoding(t, writeList, readList, { - struct: 'List', - id: [100, 33], - type: 'Array', - start: null, - end: null - }) -}) - -const writeMap = Y.Struct.Map.binaryEncode -const readMap = Y.Struct.Map.binaryDecode - -test('encode/decode Map operations', async function binMap (t) { - testEncoding(t, writeMap, readMap, { - struct: 'Map', - id: [100, 33], - type: 'Map', - map: {} - }) -}) diff --git a/test/index.html b/test/index.html new file mode 100644 index 00000000..550604b0 --- /dev/null +++ b/test/index.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/test/y-array.tests.js b/test/y-array.tests.js index 3a49fa32..e5bf0561 100644 --- a/test/y-array.tests.js +++ b/test/y-array.tests.js @@ -1,4 +1,4 @@ -import { wait, initArrays, compareUsers, Y, flushAll, garbageCollectUsers, applyRandomTests } from '../tests-lib/helper.js' +import { wait, initArrays, compareUsers, Y, flushAll, applyRandomTests } from '../tests-lib/helper.js' import { test, proxyConsole } from 'cutest' proxyConsole() @@ -38,7 +38,6 @@ test('concurrent insert (handle three conflicts)', async function array2 (t) { array0.insert(0, [0]) array1.insert(0, [1]) array2.insert(0, [2]) - await compareUsers(t, users) }) @@ -213,7 +212,6 @@ test('garbage collector', async function gc1 (t) { await wait() await users[0].reconnect() await flushAll(t, users) - await garbageCollectUsers(t, users) await compareUsers(t, users) }) diff --git a/tests-lib/helper.js b/tests-lib/helper.js index 1fbc1b1b..de60e07f 100644 --- a/tests-lib/helper.js +++ b/tests-lib/helper.js @@ -1,34 +1,25 @@ import _Y from '../../yjs/src/y.js' - -import yArray from '../../y-array/src/y-array.js' -import yText from '../../y-text/src/y-text.js' -import yMap from '../../y-map/src/y-map.js' -import yXml from '../../y-xml/src/y-xml.js' import yTest from './test-connector.js' import Chance from 'chance' export let Y = _Y -Y.extend(yArray, yText, yMap, yTest, yXml) - export var database = { name: 'memory' } export var connector = { name: 'test', url: 'http://localhost:1234' } -function getStateSet () { - var ss = {} - this.ss.iterate(this, null, null, function (n) { - var user = n.id[0] - var clock = n.clock +function getStateSet (y) { + let ss = {} + for (let [user, clock] of y.ss.state) { ss[user] = clock - }) + } return ss } -function getDeleteSet () { +function getDeleteSet (y) { var ds = {} - this.ds.iterate(this, null, null, function (n) { + y.ds.iterate(this, null, null, function (n) { var user = n.id[0] var counter = n.id[1] var len = n.len @@ -43,11 +34,6 @@ function getDeleteSet () { return ds } -export async function garbageCollectUsers (t, users) { - await flushAll(t, users) - await Promise.all(users.map(u => u.db.emptyGarbageCollector())) -} - export function attrsObject (dom) { let keys = [] let yxml = dom.__yxml @@ -67,7 +53,7 @@ export function domToJson (dom) { if (dom.nodeType === document.TEXT_NODE) { return dom.textContent } else if (dom.nodeType === document.ELEMENT_NODE) { - let attributes = attrsObject(dom, dom.__yxml) + let attributes = attrsObject(dom) let children = Array.from(dom.childNodes.values()) .filter(d => d.__yxml !== false) .map(domToJson) @@ -97,20 +83,9 @@ export async function compareUsers (t, users) { await wait() await flushAll(t, users) - var userArrayValues = users.map(u => u.share.array._content.map(c => c.val || JSON.stringify(c.type))) - function valueToComparable (v) { - if (v != null && v._model != null) { - return v._model - } else { - return v || null - } - } - var userMapOneValues = users.map(u => u.share.map.get('one')).map(valueToComparable) - var userMapTwoValues = users.map(u => u.share.map.get('two')).map(valueToComparable) - var userXmlValues = users.map(u => u.share.xml.getDom()).map(domToJson) - - await users[0].db.garbageCollect() - await users[0].db.garbageCollect() + var userArrayValues = users.map(u => u.get('array', Y.Array).toJSON()) + var userMapValues = users.map(u => u.get('map', Y.Map).toJSON()) + var userXmlValues = users.map(u => u.get('xml', Y.Xml).getDom().toString()) // disconnect all except user 0 await Promise.all(users.slice(1).map(async u => @@ -130,39 +105,30 @@ export async function compareUsers (t, users) { u.connector.whenSynced(resolve) }) )) - let filterDeletedOps = users.every(u => u.db.gc === false) - var data = await Promise.all(users.map(async (u) => { + var data = users.forEach(u => { var data = {} - u.db.requestTransaction(function () { - let ops = [] - this.os.iterate(this, null, null, function (op) { - ops.push(Y.Struct[op.struct].encode(op)) - }) - - data.os = {} - for (let i = 0; i < ops.length; i++) { - let op = ops[i] - op = Y.Struct[op.struct].encode(op) - delete op.origin - /* - If gc = false, it is necessary to filter deleted ops - as they might have been split up differently.. - */ - if (filterDeletedOps) { - let opIsDeleted = this.isDeleted(op.id) - if (!opIsDeleted) { - data.os[JSON.stringify(op.id)] = op - } - } else { - data.os[JSON.stringify(op.id)] = op - } + let ops = [] + y.os.iterate(null, null, function (op) { + if (!op._deleted) { + ops.push({ + left: op._left, + right: op._right, + deleted: op._deleted + }) } - data.ds = getDeleteSet.apply(this) - data.ss = getStateSet.apply(this) }) - await u.db.whenTransactionsFinished() + + data.os = {} + for (let i = 0; i < ops.length; i++) { + let op = ops[i] + op = Y.Struct[op.struct].encode(op) + delete op.origin + data.os[JSON.stringify(op.id)] = op + } + data.ds = getDeleteSet.apply(this) + data.ss = getStateSet.apply(this) return data - })) + }) for (var i = 0; i < data.length - 1; i++) { await t.asyncGroup(async () => { t.compare(userArrayValues[i], userArrayValues[i + 1], 'array types') @@ -174,39 +140,27 @@ export async function compareUsers (t, users) { t.compare(data[i].ss, data[i + 1].ss, 'ss') }, `Compare user${i} with user${i + 1}`) } - await Promise.all(users.map(async (u) => { - await u.close() - })) + users.map(u => u.close()) } export async function initArrays (t, opts) { var result = { users: [] } - var share = Object.assign({ flushHelper: 'Map', array: 'Array', map: 'Map', xml: 'XmlElement("div")' }, opts.share) var chance = opts.chance || new Chance(t.getSeed() * 1000000000) var conn = Object.assign({ room: 'debugging_' + t.name, generateUserId: false, testContext: t, chance }, connector) for (let i = 0; i < opts.users; i++) { - let dbOpts let connOpts if (i === 0) { - // Only one instance can gc! - dbOpts = Object.assign({ gc: false }, database) connOpts = Object.assign({ role: 'master' }, conn) } else { - dbOpts = Object.assign({ gc: false }, database) connOpts = Object.assign({ role: 'slave' }, conn) } - let y = await Y({ - connector: connOpts, - db: dbOpts, - share: share + let y = new Y({ + connector: connOpts }) result.users.push(y) - for (let name in share) { - result[name + i] = y.share[name] - } - y.share.xml.setDomFilter(function (d, attrs) { + y.get('xml', Y.Xml).setDomFilter(function (d, attrs) { if (d.nodeName === 'HIDDEN') { return null } else { @@ -241,7 +195,7 @@ export async function flushAll (t, users) { // flush for any connector await Promise.all(users.map(u => { return u.db.whenTransactionsFinished() })) - var flushCounter = users[0].share.flushHelper.get('0') || 0 + var flushCounter = users[0].get('flushHelper', Y.Map).get('0') || 0 flushCounter++ await Promise.all(users.map(async (u, i) => { // wait for all users to set the flush counter to the same value @@ -249,7 +203,7 @@ export async function flushAll (t, users) { function observer () { var allUsersReceivedUpdate = true for (var i = 0; i < users.length; i++) { - if (u.share.flushHelper.get(i + '') !== flushCounter) { + if (u.get('flushHelper', Y.Map).get(i + '') !== flushCounter) { allUsersReceivedUpdate = false break } @@ -258,8 +212,8 @@ export async function flushAll (t, users) { resolve() } } - u.share.flushHelper.observe(observer) - u.share.flushHelper.set(i + '', flushCounter) + u.get('flushHelper', Y.Map).observe(observer) + u.get('flushHelper').set(i + '', flushCounter) }) })) } diff --git a/tests-lib/test-connector.js b/tests-lib/test-connector.js index 42a87564..07a7f92b 100644 --- a/tests-lib/test-connector.js +++ b/tests-lib/test-connector.js @@ -1,6 +1,6 @@ /* global Y */ -import { wait } from './helper.js' -import { formatYjsMessage } from '../src/MessageHandler.js' +import { wait } from './helper' +import { messageToString, messageToRoomname } from '../src/MessageHandler/messageToString' var rooms = {}