From 0e426f89284dc587df57edcc9affe1d2dd3124f8 Mon Sep 17 00:00:00 2001
From: Kevin Jahns <kevin.jahns@rwth-aachen.de>
Date: Sat, 14 Oct 2017 23:03:24 +0200
Subject: [PATCH] fix compiling issues

---
 .flowconfig                                  |   2 +
 package.json                                 |   3 +-
 rollup.browser.js                            |   2 +-
 rollup.node.js                               |   2 +-
 src/Binary/Decoder.js                        |  61 ++++++---
 src/Binary/Encoder.js                        |   2 +-
 src/Connector.js                             |   9 +-
 src/MessageHandler/integrateRemoteStructs.js |   6 +-
 src/MessageHandler/messageToString.js        |   8 +-
 src/MessageHandler/syncStep1.js              |   2 +-
 src/MessageHandler/syncStep2.js              |   4 +-
 src/MessageHandler/update.js                 |   4 +-
 src/Persistence.js                           |   2 +-
 src/Store/DeleteStore.js                     |   4 +-
 src/Store/OperationStore.js                  |  11 +-
 src/Store/StateStore.js                      |   2 +-
 src/Struct/Delete.js                         |   2 +-
 src/Struct/Item.js                           |   1 -
 src/Struct/ItemJSON.js                       |   2 +-
 src/Struct/ItemString.js                     |   2 +-
 src/Struct/Type.js                           |   2 +-
 src/Type/YArray.js                           |   4 +-
 src/Type/YMap.js                             |   6 +-
 src/Type/YText.js                            |   4 +
 src/Type/YXml.js                             |   4 +
 src/Util/ID.js                               |  18 +--
 src/Util/RootID.js                           |  14 ++
 src/Util/deleteItemRange.js                  |   2 +-
 src/Util/generateUserID.js                   |   2 +-
 src/Util/structReferences.js                 |  12 +-
 src/Y.js                                     |  35 ++---
 test/encode-decode.js                        | 129 +------------------
 test/index.html                              |   8 ++
 test/y-array.tests.js                        |   4 +-
 tests-lib/helper.js                          | 122 ++++++------------
 tests-lib/test-connector.js                  |   4 +-
 36 files changed, 192 insertions(+), 309 deletions(-)
 create mode 100644 src/Util/RootID.js
 create mode 100644 test/index.html

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 @@
+<!DOCTYPE html>
+<html>
+  <head>
+  </head>
+  <body>
+    <script type="module" src="./encode-decode.js"></script>
+  </body>
+</html>
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 = {}